网络 - OkHttp

一、概念

是由Square公司开发的一套高效、轻量的HTTP客户端库,支持HTTP/1.1、HTTP/2、HTTP/3(QUIC)及HTTPS,广泛用于Android和Java项目中,它封装了底层网络通信细节,提供了简洁的API。

连接池复用通过ConnectionPool管理TCP连接的复用(基于HTTP的keep-alive机制),避免频繁创建/销毁连接的开销(原生API需手动管理,复杂且低效)。
拦截器机制提供分层拦截器(应用拦截器、网络拦截器),可轻松实现请求头统一处理、日志打印、缓存控制、签名验证等功能(原生API需手动嵌入代码,耦合度高)。
自动处理常见场景支持自动重定向(3xx状态码)、自动重试(如连接失败)、Gzip压缩解压(减少数据传输量),原生API需手动实现这些逻辑。
异步回调简化内置异步请求机制(enqueue()),回调在子线程执行,避免Handler+Thread的繁琐代码(原生API需手动管理线程通信)。
缓存支持内置HTTP缓存(基于Cache-Control头),可配置磁盘缓存目录和大小,实现离线访问(原生API需手动处理缓存逻辑,易出错)。
HTTP/2与HTTP/3支持原生支持多路复用(HTTP/2)和QUIC协议(HTTP/3),大幅提升并发请求性能(原生HttpURLConnection对高版本HTTP支持有限)。

1.1 RequestBody 类

是OkHttp中用于封装POST、PUT等请求的请求体的抽象类。

FormBody用途:提交表单数据(application/x-www-form-urlencoded格式,键值对)
场景:简单表单提交(如登录、搜索,数据为字符串键值对)。
MultipartBody用途:提交混合类型数据(multipart/form-data格式,支持文本+文件)。
适用场景:文件上传(如头像、文档),同时需携带表单字段。
RequestBody.create()用途:提交自定义格式数据(如JSON、纯文本、二进制)。
适用场景:API接口提交JSON数据、发送纯文本等。
ByteStringRequestBody用途:提交ByteString(OkHttp的高效字节容器)数据。
适用场景:内存中已存在的二进制数据(如加密后的字节流)。

1.1.1 FormBody

// 构建表单数据
FormBody formBody = new FormBody.Builder()
    .add("username", "test")
    .add("password", "123456")
    .build();
// 构建POST请求
Request request = new Request.Builder()
    .url("https://api.example.com/login")
    .post(formBody)
    .build();  

1.1.2 MultipartBody

// 构建文件请求体(上传图片)
RequestBody fileBody = RequestBody.create(
    new File("/sdcard/avatar.jpg"),
    MediaType.parse("image/jpeg")
);
// 构建混合请求体
MultipartBody multipartBody = new MultipartBody.Builder()
    .setType(MultipartBody.FORM) // 必须指定类型为FORM
    .addFormDataPart("username", "test") // 文本字段
    .addFormDataPart("avatar", "avatar.jpg", fileBody) // 文件字段(name、文件名、文件体)
    .build();
// 构建POST请求
Request request = new Request.Builder()
    .url("https://api.example.com/upload")
    .post(multipartBody)
    .build(); 

1.1.3 RequestBody.create() 静态方法创建

// JSON数据
String json = "{\"name\":\"张三\",\"age\":20}";
// 创建JSON请求体(指定媒体类型为application/json)
RequestBody jsonBody = RequestBody.create(
    json,
    MediaType.parse("application/json; charset=utf-8")
);
// 构建POST请求
Request request = new Request.Builder()
    .url("https://api.example.com/user")
    .post(jsonBody)
    .build(); 

1.2 拦截器 Interceptor

拦截器是OkHttp中用于拦截、修改、监控HTTP请求与响应的组件,类似“中间件”,它可以在请求发送到服务器前处理请求(如添加头信息、签名),或在响应返回客户端后处理响应(如解析数据、打印日志),是OkHttp灵活性的核心。

无论响应是来自网络还是缓存,应用拦截器都会被调用。如果响应来自于缓存,网络拦截器将完全不会被调用。(对于缓存拦截器一定要用应用拦截器)

1.2.1 统一添加请求头(如Token),使用应用拦截器

val headerInterceptor = object : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        //获取原始请求
        val originalRequest = chain.request()
        //构建新请求,添加统一头信息(如Token)
        val newRequest = originalRequest.newBuilder()
            .addHeader("Authorization", "Bearer " + getToken()) //添加Token
            .addHeader("App-Version", "1.0.0")  // 添加App版本
            .build()
        //继续执行请求
        return chain.proceed(newRequest)
    }
    fun getToken(): String {
        return "user_token_123456"
    }
}

val client = OkHttpClient.Builder()
    .addInterceptor(headerInterceptor)
    .build()

1.2.2 请求/响应日志打印,使用应用拦截器或网络拦截器

推荐使用官方LoggingInterceptor(需添加依赖)

val logInterceptor = object : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        //打印请求信息
        val request = chain.request()
        Log.d("请求头URL", "${request.url}")
        Log.d("请求头", "${request.headers}")
        //执行请求
        val startTime = System.currentTimeMillis()
        val response = chain.proceed(request)
        val endTime = System.currentTimeMillis()
        //打印响应信息
        Log.d("响应码", "${response.code}")
        Log.d("耗时", "${endTime - startTime} ms")
        Log.d("响应体", response.body.toString())
        return response
    }
}

val client = OkHttpClient.Builder()
    .addNetworkInterceptor(logInterceptor)
    .build()

1.2.3 缓存控制,使用应用拦截器

详见

缓存策略主要通过HTTP请求头/响应头的Cache-Control字段控制。通过请求头设置缓存读取策略,通过响应头设置缓存保存策略。

1.3 连接池 ConnectionPool

基于HTTP的keep-alive机制,管理TCP连接的复用,避免频繁创建/销毁连接的性能损耗(TCP三次握手、四次挥手耗时)。当请求完成后,TCP连接不会立即关闭,而是被放入连接池(标记为“空闲”)。新请求若访问相同的主机和端口(且协议版本兼容),会优先复用连接池中的空闲连接。连接池通过后台线程(CleanupThread)定期清理过期连接(超过最大存活时间)或超出最大空闲数的连接。

//高频访问同一服务器可增大最大空闲数,减少连接创建开销。
val connectionPool = ConnectionPool(
    maxIdleConnections = 10,    //最大空闲连接数
    keepAliveDuration = 10,     //存活时间
    timeUnit = TimeUnit.MINUTES
    )

val client = OkHttpClient.Builder()
    .connectionPool(connectionPool)
    .build()

二、基本使用

2.1 添加依赖

implementation 'com.squareup.okhttp3:okhttp:4.12.0' 

2.2 创建 OkHttpClient

OkHttpClient 是线程安全的,全局单例(避免重复创建连接池等资源)即可满足所有请求需求,如需不同配置(如不同超时),可创建多个OkHttpClient实例。  

object MyClient {
    //创建客户端
    private val okHttpClient by lazy {
        OkHttpClient.Builder()
            .connectTimeout(10, TimeUnit.SECONDS)    //连接超时时间
            .readTimeout(10, TimeUnit.SECONDS)  //读取超时
            .writeTimeout(10, TimeUnit.SECONDS) //写入超时
            //还可添加拦截器、缓存等配置
            .build()
    }
}

2.3 GET 请求

构建Request对象,通过Call执行,分为同步和异步两种。

2.3.1 同步请求 execute()

同步请求会阻塞当前线程,必须在子线程中执行,否则会导致ANR。

 fun getDemo() = Thread {
    //创建请求
    val request = Request.Builder()
        .url("https://www.baidu.com")
        .get()    //默认为GET,可省略
        .addHeader("Accept","application/json")    //可选,添加请求头
        .build()
    //创建任务
    val call = MyClient.okHttpClient.newCall(request)
    try {
        //执行任务(同步执行,阻塞当前线程)
        val response = call.execute()
        //处理响应
        if (response.code == HttpURLConnection.HTTP_OK) {
            Log.d("成功", response.body.toString())
        } else {
            Log.d("失败", response.code.toString())
        }
    } catch (e: Exception) {
        e.printStackTrace()
    }
}.start()

2.3.2 异步请求 enqueue()

异步请求通过回调处理结果,不会阻塞当前线程(推荐使用).

fun getDemo2() {
    //创建请求
    val request = Request.Builder()
        .url("https://api.example.com/user?userId=123")
        .build()
    //创建任务
    val call = MyClient.okHttpClient.newCall(request)
    //执行任务(异步执行,,回调在子线程中)
    call.enqueue(object : Callback {
        //请求完成,无论成功与否
        override fun onResponse(call: Call, response: Response) {
            if (response.code == HttpURLConnection.HTTP_OK) {
                //(反序列化,json转bean)
                val dataBean = Gson().fromJson(response.body.toString(), DataBean::class.java)
            }
        }
        //请求失败
        override fun onFailure(call: Call, e: IOException) {
            Log.d("失败", e.toString())
        }
    })
}

2.2 post请求

fun postCall() {
    //创建客户端
    val client = OkHttpClient.Builder()
        .connectTimeout(5000, TimeUnit.MILLISECONDS)
        .build()
    //创建要提交的内容(序列化,内容转bean再转json)
    val dataBean = DataBean("张三")
    val jsonStr = Gson().toJson(dataBean)
    val mediaType = "application/json".toMediaTypeOrNull()
    val requestBody = jsonStr.toRequestBody(mediaType)
    //创建请求
    val request = Request.Builder()
        .post(requestBody)
        .url("https://www.baidu.com")
        .build()
    //创建任务
    val call = client.newCall(request)
    //执行任务
    call.enqueue(object : Callback {
        override fun onFailure(call: Call, e: IOException) {
            Log.d("ErrorMsg", e.toString())
        }
        override fun onResponse(call: Call, response: Response) {
            Log.d("Result", response.body.toString())
        }
    })
}

三、日志打印

implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0' 
//日志打印级别:BASEIC(请求/响应行)、HEADER(请求/响应行+头)、BODY(请求/响应航+头+体)、NONE(不打印)
private val loggingInterceptor by lazy {
    HttpLoggingInterceptor().apply {
        //仅在debug模式下打印详细(BODY),release模式禁用或简化(BASIC)
        level = if (Constants.IS_DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE
    }
}
//设置给OkHttoClient
private val okHttpClient = OkHttpClient.Builder()
    .addInterceptor(loggingInterceptor) //应用拦截器,打印所有网络请求
//    .addNetworkInterceptor(loggingInterceptor)    //网络拦截器,只打印真实网络请求
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值