一、OkHttp简介
OkHttp一个处理网络请求的开源项目,是安卓端最火热的轻量级框架,由移动支付Square公司贡献(该公司还贡献了Picasso)
二、 OKHttp优点
- 支持get请求和post请求
- 支持基于Http的文件上传和下载
- 支持加载图片
- 支持GZIP压缩
- 支持响应缓存避免重复的网络请求
- 支持使用连接池来降低响应延迟问题。
三、 OkHttp使用简介
- 创建一个OkHttpClient对象
- 创建一个request对象,通过内部类Builder调用生成Request对象
- 创建一个Call对象,调用execute(同步请求)/enqueue(异步请求)
示例代码 1. execute(同步get请求)
public void getSyncRequest(View view) {
// 1. 拿到OkHttpClient对象
OkHttpClient client = new OkHttpClient();
// 2. 构造Request对象
final Request request = new Request.Builder()
.get()
.url("https:www.baidu.com")
.build();
// 3. 将Request封装为Call
final Call call = client.newCall(request);
// 4. 根据需要调用同步请求方法
new Thread(new Runnable() {
@Override
public void run() {
try {
// 同步调用,返回Response,会抛出IO异常
Response response = call.execute();
Log.d(TAG, "response.code() = " + response.code() +
", response.message() = " + response.message() +
", response.body() = " + response.body().string() +
"");
} catch (IOException e) {
Log.d(TAG, "" + e +
"");
e.printStackTrace();
}
}
}).start();
}
2. enqueue(异步get请求)
public void getAsyncRequest(View view) {
// 1. 拿到OkHttpClient对象
OkHttpClient client = new OkHttpClient();
// 2. 构造Request对象
final Request request = new Request.Builder()
.get()
.url("https:www.baidu.com")
.build();
// 3. 将Request封装为Call
final Call call = client.newCall(request);
// 4. 根据需要调用异步请求方法
call.enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
Log.d(TAG, "get failed" +
"");
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
final String res = response.body().string();
runOnUiThread(new Runnable() {
@Override
public void run() {
textView.setText(res);
}
});
}
});
}
post请求
public void postFormRequest(View view) {
// 1. 拿到OkHttpClient对象
OkHttpClient client = new OkHttpClient();
// 2. 构建FormBody,传入参数
FormBody formBody = new FormBody.Builder()
.add("username", "admin")
.add("username", "admin")
.build();
// 3. 构建Request,将FormBody作为Post方法的参数传入
final Request request = new Request.Builder()
.url("http://www.jianshu.com")
.post(formBody)
.build();
// 4. 将Request封装为Call
Call call = client.newCall(request);
// 5. 调用请求,重写回调方法
call.enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
Log.d(TAG, "get failed" +
", e.getMessage() = " + e.getMessage() +
", e = " + e +
"");
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
final String res = response.body().string();
runOnUiThread(new Runnable() {
@Override
public void run() {
textView.setText(res);
}
});
}
});
}
四、 OkHttp源码分析(kotlin)
OkHttpClient算是执行调用请求Call的工厂,这个工厂将会被用来发送Http请求和读取他们的返回,这里强调OkHttp的使用最好创建一个单例OkHttpClient实例,并且重复使用。这是因为每一个Client都有自己的一个连接池connection pool和线程池thread pools。重用这些连接池和线程池可以减少延迟和节约内存。
// 1. 拿到OkHttpClient对象
OkHttpClient client = new OkHttpClient();
// 2. 构造Request对象
final Request request = new Request.Builder()
.get()
.url("https:www.baidu.com")
.build();
// 3. 将Request封装为Call
final Call call = client.newCall(request);
查看newCall方法
/** Prepares the [request] to be executed at some point in the future. */
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
RealCall是Call的实现类,RealCall中实现了Execute和enqueue等方法。查看Real方法的execute方法
override fun execute(): Response {
check(executed.compareAndSet(false, true)) { "Already Executed" }
timeout.enter()
callStart()
try {
client.dispatcher.executed(this)
return getResponseWithInterceptorChain()
} finally {
client.dispatcher.finished(this)
}
}
同步方法中做了4件事
- 检查这个 call 是否已经被执行了,每个 call 只能被执行一次,如果想要一个完全一样的 call,可以利用 call#clone 方法进行克隆。
- 利用 client.dispatcher().executed(this) 来进行实际执行,dispatcher 是刚才看到的OkHttpClient.Builder 的成员之一,它的文档说自己是异步 HTTP 请求的执行策略,现在看来,同步请求它也有掺和。
- 调用 getResponseWithInterceptorChain() 函数获取 HTTP 返回结果,从函数名可以看出,这一步还会进行一系列“拦截”操作。
- 最后还要通知 dispatcher 自己已经执行完毕。
这里同步请求,只是把当前请求添加到队列而已
/** Used by [Call.execute] to signal it is in-flight. */
@Synchronized internal fun executed(call: RealCall) {
runningSyncCalls.add(call)
}
真正发出网络请求,解析返回结果的,还是 getResponseWithInterceptorChain,这个下面说,最后再调用了dispatch.finish方法
接下来看下异步请求RealCall.enqueue
override fun enqueue(responseCallback: Callback) {
check(executed.compareAndSet(false, true)) { "Already Executed" }
callStart()
client.dispatcher.enqueue(AsyncCall(responseCallback))
}
先判断当前Call是否在执行,再调用dispatch.enqueue方法
internal fun enqueue(call: AsyncCall) {
synchronized(this) {
readyAsyncCalls.add(call)
// Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
// the same host.
if (!call.call.forWebSocket) {
val existingCall = findExistingCallWithHost(call.host)
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
}
}
promoteAndExecute()
}
调用在promoteAndExecute方法中
private fun promoteAndExecute(): Boolean {
this.assertThreadDoesntHoldLock()
val executableCalls = mutableListOf<AsyncCall>()
val isRunning: Boolean
synchronized(this) {
val i = readyAsyncCalls.iterator()
while (i.hasNext()) {
val asyncCall = i.next()
if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.
if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue // Host max capacity.
i.remove()
asyncCall.callsPerHost.incrementAndGet()
executableCalls.add(asyncCall)
runningAsyncCalls.add(asyncCall)
}
isRunning = runningCallsCount() > 0
}
for (i in 0 until executableCalls.size) {
val asyncCall = executableCalls[i]
asyncCall.executeOn(executorService)
}
return isRunning
}
,在promoteAndExecute方法中,方法中,这里先有个判断,如果当前运行的异步请求队列长度小于最大请求数maxRequests,也就是64,并且主机的请求数小于每个主机的请求数maxRequestsPerHost也就是5,则把当前请求添加到 运行队列,接着交给线程池ExecutorService处理,否则则放置到readAsyncCall进行缓存,等待执行。 可以看到同步与异步一点区别就是,异步的执行交给了线程池去操作。
我们看下OkHttp里面的线程池ExecutorService
@get:Synchronized
@get:JvmName("executorService") val executorService: ExecutorService
get() {
if (executorServiceOrNull == null) {
executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,
SynchronousQueue(), threadFactory("$okHttpName Dispatcher", false))
}
return executorServiceOrNull!!
}
这里则是通过ThreadPoolExecutor来创建线程池
dispatch.enqueue方法d参数AsyncCall,AsyncCall继承于NamedRunnable,而NamaedRunnable则实现了Runnable方法
/**
* Attempt to enqueue this async call on [executorService]. This will attempt to clean up
* if the executor has been shut down by reporting the call as failed.
*/
fun executeOn(executorService: ExecutorService) {
client.dispatcher.assertThreadDoesntHoldLock()
var success = false
try {
executorService.execute(this)
success = true
} catch (e: RejectedExecutionException) {
val ioException = InterruptedIOException("executor rejected")
ioException.initCause(e)
noMoreExchanges(ioException)
responseCallback.onFailure(this@RealCall, ioException)
} finally {
if (!success) {
client.dispatcher.finished(this) // This call is no longer running!
}
}
}
override fun run() {
threadName("OkHttp ${redactedUrl()}") {
var signalledCallback = false
timeout.enter()
try {
val response = getResponseWithInterceptorChain()
signalledCallback = true
responseCallback.onResponse(this@RealCall, response)
} catch (e: IOException) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log("Callback failure for ${toLoggableString()}", Platform.INFO, e)
} else {
responseCallback.onFailure(this@RealCall, e)
}
} catch (t: Throwable) {
cancel()
if (!signalledCallback) {
val canceledException = IOException("canceled due to $t")
canceledException.addSuppressed(t)
responseCallback.onFailure(this@RealCall, canceledException)
}
throw t
} finally {
client.dispatcher.finished(this)
}
}
}
}
可以看到在它的构造器中,Callback就是我们设置的创建的,带有onFail和onResponse方法。 而线程池中最终调用到的则是我们的Runnable。
这里通过
val response = getResponseWithInterceptorChain()
方法来进行连接访问,这里跟同步请求一样。最后根据返回值调用callback.onFailure/onResponse
我们关键还是看OkHttp如何连接返回的,我们看下这个方法
@Throws(IOException::class)
internal fun getResponseWithInterceptorChain(): Response {
// Build a full stack of interceptors.
val interceptors = mutableListOf<Interceptor>()
interceptors += client.interceptors
interceptors += RetryAndFollowUpInterceptor(client)
interceptors += BridgeInterceptor(client.cookieJar)
interceptors += CacheInterceptor(client.cache)
interceptors += ConnectInterceptor
if (!forWebSocket) {
interceptors += client.networkInterceptors
}
interceptors += CallServerInterceptor(forWebSocket)
val chain = RealInterceptorChain(
call = this,
interceptors = interceptors,
index = 0,
exchange = null,
request = originalRequest,
connectTimeoutMillis = client.connectTimeoutMillis,
readTimeoutMillis = client.readTimeoutMillis,
writeTimeoutMillis = client.writeTimeoutMillis
)
var calledNoMoreExchanges = false
try {
val response = chain.proceed(originalRequest)
if (isCanceled()) {
response.closeQuietly()
throw IOException("Canceled")
}
return response
} catch (e: IOException) {
calledNoMoreExchanges = true
throw noMoreExchanges(e) as Throwable
} finally {
if (!calledNoMoreExchanges) {
noMoreExchanges(null)
}
}
}
可以看到这里全都是关于Interceptor拦截器的使用
-
- RetryAndFollowUpInterceptor : 负责失败重试以及重定向,创建StreamAllocation对象,处理http的重定向,出错重试。对后续Interceptor的执行的影响:修改request及StreamAllocation。
-
- BridgeInterceptor:负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应。补全缺失的一些http header,Cookie设置。对后续Interceptor的执行的影响:修改request。
-
- CacheInterceptor:负责读取缓存直接返回、更新缓存。处理http缓存。对后续Interceptor的执行的影响:若缓存中有所需请求的响应,则后续Interceptor不再执行。
-
- ConnectInterceptor:负责和服务器建立连接。借助于前面分配的StreamAllocation对象建立与服务器之间的连接(具体建立是在newStream方法中),并选定交互所用的协议是HTTP 1.1还是HTTP 2。对后续Interceptor的执行的影响:创建了httpStream和connection。
-
- NetworkInterceptors:配置
OkHttpClient
- NetworkInterceptors:配置
-
- CallServerInterceptor:负责向服务器发送请求数据、从服务器读取响应数据。处理IO,与服务器进行数据交换。对后续Interceptor的执行的影响:为Interceptor链中的最后一个Interceptor,没有后续Interceptor。
[责任链模式]在这个 Interceptor 链条中得到了很好的实践