OkHttp的基本用法

Quibbler 2021-3-10 1324

OkHttp的基本用法


        看完Google官方的网络通信库Volley,再继续学习Android开发中大名鼎鼎的网络请求框架:OkHttp。深入学习一个优秀的框架之前,先了解它的基本用法,由浅入深,逐步探索。本篇简要介绍一下OkHttp的使用,关于拦截器详见OkHttp拦截器Interceptor



1、OkHttp

        OkHttp是Square公司开源的网络请求框架,Android开发中另一个网络请求框架Retrofit就是基于OkHttp二次封装。


1.1、介绍

        OkHttp是一个支持HTTP 和 HTTP/2 的客户端,可以在Android和Java应用程序中使用,其具有以下特点:

        1. API设计轻巧,基本上通过几行代码的链式调用即可获取结果。

        2. 既支持同步请求,也支持异步请求。同步请求会阻塞当前线程,异步请求不会阻塞当前线程,异步执行完成后执行相应的回调方法。

        3. 其支持HTTP/2协议,通过HTTP/2,可以让客户端中到同一服务器的所有请求共用同一个Socket连接。

        4. 如果请求不支持HTTP/2协议,那么Okhttp会在内部维护一个连接池, 通过该连接池,可以对HTTP/1.x的连接进行重用,减少了延迟。

        5. 透明的GZIP处理降低了下载数据的大小。

        6. 请求的数据会进行相应的缓存处理,下次再进行请求时,如果服务器告知304(表明数据没有发生变化),那么就直接从缓存中读取数据,降低了重复请求的数量。

        其它废话就不多粘贴了,详见官方文档:

        官方指南:OkHttp Overview

        GitHub:square/okhttp


1.2、准备

        在模块的build.gradle中添加OkHttp依赖,用最新的稳定版本,开源库的查找参考添加三方开源库的正确方式一文。

    //OkHttp
    implementation 'com.squareup.okhttp3:okhttp:4.9.1'

        使用网络别忘了在AndroidManifest.xml中添加应用网络访问权限,这是新手容易忽略的小坑。

    <uses-permission android:name="android.permission.INTERNET" />

        接下来开始了解OkHttp最常用的两种请求:GET请求和POST请求。



2、GET请求

        网络请求通过OkHttp提供的OkHttpClient发起,先创建一个OkHttpClient实例。这里展示用法,实际项目中不要创建多个OkHttpClient实例(应使用单例)

    //直接创建默认OkHttpClient
    val okHttpClient: OkHttpClient = OkHttpClient()
    
    //使用OkHttpClient.Builder构造
    val okHttpClient: OkHttpClient = OkHttpClient.Builder()
            .connectTimeout(10, TimeUnit.SECONDS)
            .readTimeout(10, TimeUnit.SECONDS)
            .writeTimeout(10, TimeUnit.SECONDS)
            .build()

        

2.1、构造Request

        使用Request的内部类Builder构造网络请求,Request默认请求方法是GET,可以调用get()或者method("GET" , requestBody)方法设置请求为GET

    //网络请求Request通过内部类Request.Builder建造者模式创建
    val requestBuilder: Request.Builder = Request.Builder()
    requestBuilder.url(url)
    
    //请求方法设置为GET,RequestBody请求体空。也可用get()进行设置,内部实现是一样的
    requestBuilder.method("GET", null)
    
    //从Request.Builder创建Request实例
    val request: Request = requestBuilder.build()

        OkHttp中很多类的实例(比如:FormBodyMultipartBodyHeaders等)都是通过内部Builder类构造。本篇只简单了解一下,后面会专门深入源码研究。


2.2、准备请求Call

        借助本节开头创建的OkHttpClient实例,调用newCall(Request )方法获取网络请求Call接口类型的实例,每个Call实例代表单个响应流,只能执行一次请求,实现类是RealCall

    //通过第一步创建的OkHttppClient创建网络Call对象
    val call: Call = okHttpClient.newCall(request)

        通过Call发起最终网络请求,有两种方式:异步enqueue(Callback)和同步的execute()


2.3、异步GET:enqueue()

        拿到已准备好执行的请求Call,调用enqueue(Callback)方法提交异步网络请求。请求会被放到OkHttpClientDispatcher中调度执行,网络请求完之后后执行Callback回调。

    //调用Call的enqueue(Callback )方法异步请求,在回调中处理网络请求结果
    call.enqueue(object : Callback {
        override fun onFailure(call: Call, e: IOException) {
            //网络请求失败回调
        }
        override fun onResponse(call: Call, response: Response) {
            //处理网络请求响应
        }
    })


2.4、同步GET:execute()

        同步的网络请求必须在子线程中进行,因为是耗时的网络操作。执行请求Call的execute()方法获取网络请求返回的结果Response

    //调用Call的execute()方法同步请求,获取返回的Response
    Thread {
        try {
            call.execute().use {
                //处理网络结果Response
                val data = it.body
            }
        } catch (e: Exception) {
            Log.e(TAG, "${e.message}")
        }
    }.start()

        使用完要关闭Response避免内存泄漏,这里使用Closeable的扩展函数use{},方法执行完后自动关闭资源。



3、POST请求

        POST提交请求和GET差不多,POST请求需要了解RequestBody的构造,POST请求携带不同的RequestBody以上传不同的数据内容给服务端。在这节简单了解一下RequestBody请求体,关于OkHttp的RequestBody详见OkHttp之RequestBody请求体


3.1、RequestBody

        POST向服务端提交数据,离不开网络请求的数据载体:RequestBody。在该抽象类中使用Kotlin实现了很多扩展方法方便开发者从String、File、ByteArray中创建RequestBody实例。

        抽象类RequestBody有两个实现类:FormBodyMultipartBody,方便开发者提交表单数据和分块数据。


3.2、POST文本

        从String文本直接通过扩展函数toRequestBody(MediaType )构造RequestBody,传入MediaType指明数据内容的MIME类型。关于MIME详见了解媒体类型MIME

    //创建数据的MIME类型,关于MIME参考另一博客
    val stringMediaType: MediaType = "text/plain".toMediaType()
    //直接通过String扩展函数构造对应的RequestBody
    val stringRequestBody: RequestBody = "string from client".toRequestBody(stringMediaType)
    
    //简单快速的构造一个文本POST请求
    val request: Request = Request.Builder()
        .url(url)
        .post(stringRequestBody)
        .build()
    val call: Call = client.newCall(request)
    
    //处理POST
    try {
        call.execute().use {
        }
    } catch (e: Exception) {
    }


3.3、POST表单

        通过FormBody内部Builder类构造表单,携带key-value表单数据。

    val requestBodyBuilder: FormBody.Builder = FormBody.Builder()
    requestBodyBuilder.add("keywords", "search")
    
    val requestBody: RequestBody = requestBodyBuilder.build()

        Request.Builder构造POST请求,调用post(Request )method("POST", Request)将请求方法设置为POST

    val requestBuilder: Request.Builder = Request.Builder()
    requestBuilder.url(url)
                  .method("POST", requestBody)  //和下面方法等价
                  .post(requestBody)            //和上面方法等价

        发起POST请求和GET方式一样,至于异步或同步看具体业务场景。

    try {
        call.execute().use {
            //表单提交完成
        }
    } catch (e: Exception) {
        //表单提交异常
    }

        

3.4、POST上传文件

        上传文件也很简单,借助RequestBody内部实现的File扩展函数asRequestBody(),从文件对象直接创建对应的RequestBody。发起上传文件的请求和上一节的POST流程一样。

    //指明文件MIME为图片image/png
    val fileContentType: MediaType? = "image/png".toMediaTypeOrNull()
    
    //从File创建RequestBody
    val fileBody = File(path).asRequestBody(fileContentType)
    
    //构造文件上传请求
    val request: Request = Request.Builder()
            .url(url)
            .post(fileBody)
            .build()
    
    //同步执行文件上传请求
    val call: Call = OkHttpClient().newCall(request)
    try {
        call.execute().use {
            //表单提交异常
        }
    } catch (e: Exception) {
        //表单提交异常
    }


3.5、Multipart分块POST

        借助OkHttp内部的MultipartBody类可以构造复杂的请求体。分块请求体中的每块都是独立的一个RequestBody,可以设置自己的Header。

    //通过MultipartBody.Builder构造
    val multipartBodyBuilder: MultipartBody.Builder = MultipartBody.Builder()
    multipartBodyBuilder.setType("multipart/form-data".toMediaType())
    
    //添加分块
    multipartBodyBuilder.addPart(requestOne)
    multipartBodyBuilder.addPart(MultipartBody.Part.create(requestTwo))
    multipartBodyBuilder.addPart(MultipartBody.Part.create(null, requestTwo))
    multipartBodyBuilder.addPart(Headers.headersOf("key", "value"), requestThree)
    
    //build()分块请求体对象
    val multipartBody: MultipartBody = multipartBodyBuilder.build()



不忘初心的阿甘
最新回复 (0)
    • 安卓笔记本
      2
        登录 注册 QQ
返回
仅供学习交流,切勿用于商业用途。如有错误欢迎指出:fluent0418@gmail.com