Android网络请求库【OkHttp4.9.3】基本用法与原理分析
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android网络请求库【OkHttp4.9.3】基本用法与原理分析相关的知识,希望对你有一定的参考价值。
参考技术AOkHttp是一套处理 HTTP 网络请求的依赖库,由 Square 公司设计研发并开源,目前可以在 Java 和 Kotlin 中使用。对于 android App 来说,OkHttp 现在几乎已经占据了所有的网络请求操作,Retrofit + OkHttp实现网络请求似乎成了一种标配。因此它也是每一个 Android 开发工程师的必备技能,了解其内部实现原理可以更好地进行功能扩展、封装以及优化。
OkHttp的高效性体现在:
第一步:创建OkHttpClient,创建OkHttpClient有两种方式:
OkHttpClient提供了丰富的配置方法,例如添加拦截器、指定连接池、设置请求超时等等。
第二步:创建请求
使用Request.Builder() 构建Request实例
第三步:发起网络请求
OkHttp支持同步和异步两种请求方式
OkHttp的使用方法非常简单,三步操作就可以发起一个简单的同步或异步请求。我们也可以很轻松地对网络请求进行配置,例如添加请求头、设置请求方式、设置请求超时等等,这些配置参数会在源码分析过程中详细介绍。
现在我们已经学会了三步操作发起网络请求,接下来以这三个步骤为切入点,深入到源码中学习OkHttp的实现原理,废话少说马上开车。
OkHttpClient创建方式有两种,我们看看两种方式有什么区别。
第一种直接使用默认构造函数,内部依然是使用建造者模式
第二种使用建造者模式
两种方式最终都是调用构造函数OkHttpClient(builder:Builder),由参数builder负责所有的参数配置工作。
当您创建单个OkHttpClient实例并将其用于所有 HTTP 调用时,OkHttp 性能最佳。 这是因为每个OkHttpClient都拥有自己的连接池和线程池,重用连接和线程可减少延迟并节省内存。 相反,为每个请求创建一个客户端会浪费空闲池上的资源。
Request同样使用建造者模式来创建,这里贴上部分重要源码,很简单就不细说了。
OkHttp发起网络请求分为同步请求和异步请求两种方式,我们只分析异步请求流程,因为只要理解了异步请求过程,基本上也就明白同步请求是怎么一回事了。
RealCall是连接应用层与网络层的桥梁,负责处理连接、请求、响应和数据流。
Dispatcher维护着一套异步任务执行策略,分析策略之前先介绍几个重要概念:
client.dispatcher.enqueue(AsyncCall(responseCallback)) 执行步骤为:
AsyncCall实现了Runnable接口,因此一旦被线程池中的线程处理就会调用它的run()方法:
话休絮烦,我们开始分析拦截器责任链:
责任链执行流程:首先获取当前拦截器interceptor,并且调用interceptor.intercept(next)执行拦截器操作。这里的next表示的是index+1后的责任链对象,拦截器的intercept()方法内部会调用next.proceed(request)方法再次进入到责任链,由于此时index已经加1,所以处理的是下一个拦截器。
如此循环往复,直到处理完责任链上最后一个拦截器为止。
注意除最后一个拦截器CallServerInterceptor不会调用chain.proceed(request)方法之外,其他拦截器都应该至少调用一次chain.proceed(request)方法。
为了验证上面的结论,我们进入到RetryAndFollowUpInterceptor的intercept()方法一探究竟:
可以看到注释1处重新进入责任链处理下一个拦截器。
有兴趣可以自行查看最后一个拦截器CallServerInterceptor源码,此处只给出本人阅读源码后得出的结论:
以上就是拦截器责任链的工作流程,我们再通过流程图仔细感受一下。
分析完拦截器责任链,我们继续分析AsyncCall#run()方法:
我们看到,如果getResponseWithInterceptorChain()方法成功获得服务端返回的数据,则调用responseCallback.onResponse(this@RealCall, response)方法完成异步回调;如果服务端数据获取失败(请求异常),则调用responseCallback.onFailure(this@RealCall, canceledException)方法完成异步回调
需要注意的是,responseCallback回调是在子线程中完成的,所以如果想把数据显示到UI上,需要切换回主线程进行UI操作。
OkHttp发起网络请求全过程:
【知识点】OkHttp 原理 8 连问
Android OkHttp, 一行代码 OkHttp提升请求稳定性
OkHttp是可以说是Android开发中,每个项目都必需依赖的网络库,我们可以很便捷高效的处理网络请求,极大的提升了编码效率。但是有时候,我们使用OkHttp也会遇到这样的问题
一.崩溃的stacktrace
E AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher
E AndroidRuntime: Process: com.example.okhttpexceptionsample, PID: 13564
E AndroidRuntime: java.lang.NullPointerException: blablabla
E AndroidRuntime: at com.example.okhttpexceptionsample.MainActivity$createNPEInterceptor$1.intercept(MainActivity.kt:61)
E AndroidRuntime: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:112)
E AndroidRuntime: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:87)
E AndroidRuntime: at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.kt:184)
E AndroidRuntime: at okhttp3.RealCall$AsyncCall.run(RealCall.kt:136)
E AndroidRuntime: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
E AndroidRuntime: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
E AndroidRuntime: at java.lang.Thread.run(Thread.java:784)
二.为什么会崩溃
从上面的stacktrace,我们可以分析到,发生了NullPointerException。发生了崩溃。
等等,我记得OkHttp有处理异常的情况呢。
嗯,确实,OkHttp有处理异常的情况,比如发生异常会调用onFailure
。比如下面的Callback的内容介绍。
interface Callback {
/**
* Called when the request could not be executed due to cancellation, a connectivity problem or
* timeout. Because networks can fail during an exchange, it is possible that the remote server
* accepted the request before the failure.
*/
fun onFailure(call: Call, e: IOException)
/**
* Called when the HTTP response was successfully returned by the remote server. The callback may
* proceed to read the response body with [Response.body]. The response is still live until its
* response body is [closed][ResponseBody]. The recipient of the callback may consume the response
* body on another thread.
*
* Note that transport-layer success (receiving a HTTP response code, headers and body) does not
* necessarily indicate application-layer success: `response` may still indicate an unhappy HTTP
* response code like 404 or 500.
*/
@Throws(IOException::class)
fun onResponse(call: Call, response: Response)
}
是的.
- OkHttp只处理了IOException的情况,
- NullPointerException不是IOException的子类
所以没有被处理,发生了崩溃。
那么有没有办法解决,让这种崩溃不发生,对用户不进行干扰呢?其实是可以的。
三.使用Interceptor
package com.example.okhttpexceptionsample
import okhttp3.Interceptor
import okhttp3.Response
import java.io.IOException
/**
* 对于Interceptor的intercept中可能出现的Throwable包裹成IOExceptionWrapper,转成网络请求失败,而不是应用崩溃
*/
class SafeGuardInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
try {
return chain.proceed(chain.request())
} catch (t: Throwable) {
throw IOExceptionWrapper("SafeGuarded when requesting ${chain.request().url}", t)
}
}
}
/**
* 将chain.proceed处理中发生的Throwable包装成IOExceptionWrapper
*/
class IOExceptionWrapper(message: String?, cause: Throwable?) : IOException(message, cause)
上面的代码,我们将任何Throwable
的转成IOExceptionWrapper
(伪装成IOException),然后添加到OkHttpClient中
fun createOKHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(SafeGuardInterceptor())
.build()
}
当我们再次执行有NPE的代码,日志就发生了改变(不再是崩溃的日志,而是异常的日志)
W System.err: com.example.okhttpexceptionsample.IOExceptionWrapper: SafeGuarded=blablabla
W System.err: at com.example.okhttpexceptionsample.SafeGuardInterceptor.intercept(SafeGuardInterceptor.kt:12)
W System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:112)
W System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:87)
W System.err: at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.kt:184)
W System.err: at okhttp3.RealCall$AsyncCall.run(RealCall.kt:136)
W System.err: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
W System.err: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
W System.err: at java.lang.Thread.run(Thread.java:784)
W System.err: Caused by: java.lang.NullPointerException: blablabla
W System.err: at com.example.okhttpexceptionsample.MainActivity$createNPEInterceptor$1.intercept(MainActivity.kt:61)
W System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:112)
W System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:87)
W System.err: at com.example.okhttpexceptionsample.SafeGuardInterceptor.intercept(SafeGuardInterceptor.kt:10)
W System.err: ... 7 more
上述需要注意两点
- 添加的是Interceptor,而不是NetworkInterceptor
- 顺序很重要,一定要放在第一个位置
四.这么做有什么问题
这么做,当然可以明显增强请求的稳定性和应用的崩溃率。但是是不是也有一些问题呢?比如
- 将问题情况吞掉,不利于发现问题呢
确实可能存在上述的问题,但是我们可以利用下面的方式减轻或者解决问题 - 只针对release情况应用SafeGuardInterceptor,这样便于debug情况下更容易发现
- 针对不同的build variants进行配置,便于尽可能的小范围发现问题
- 实行更加智能的动态开启策略。
在软件工程中,很多决定都是trade-off的体现,具体的实施方案大家可以自行平衡选择。
关于我
更多Android高级面试合集放在github上面了
,需要的小伙伴可以点击关于我?联系我获取
非常希望和大家一起交流 , 共同进步
目前是一名程序员,不仅分享 Android开发相关知识,同时还分享技术人成长历程,包括个人总结,职场经验,面试经验等,希望能让你少走一点弯路。
以上是关于Android网络请求库【OkHttp4.9.3】基本用法与原理分析的主要内容,如果未能解决你的问题,请参考以下文章