OkHttp4网络请求

217 阅读4分钟

前言

网络请求库,目前比较常用的okhttp或retrofit三方请求库。

  • okhttp使用起来比较方便:支持GZIP压缩,连接池复用底层TCP,请求自动重试重定向...

目前Google官网将源码中的HttpURLConnection底层实现改成了okhttp了,同时retrofit底层也是okhttp。

OkHttp版本4.0.1

基本使用流程

// 1、创建client
OkHttpClient client = new OkhttpClient().newBuilder()
    .cookieJar(CookieJar.NO_COOKIES)
    .callTimeOut(10*1000,TimeUnit.MILLISECONDS)
    .build();
// 2创建request
Request request = new Request.Builder()
    .url("http://192.168.x.x:8088/test");
// 3、构建call对象
Call call = client.newCall(request);
// 4.1 同步请求;response对象中保存返回的相应参数
Response response = Call.execute();
// 4.2 异步请求
call.enqueue(new Callback(){
    @Override
    public void onFailure(@NonNull Call call, @NonNull IOException e){
        // 请求失败
        Log.i(TAG,"onFailure");
    }
    @Override
    public void onResponse(@NonNull Call call,@NonNull Respone res){
        // 请求成功
        Log.i(TAG,"onResponse");
    }
})

一次网络请求由OkHttpClient Request Call 三个对象完成。通过建造者模式,根据自己需求简洁配置可选参数。

同步请求和异步请求流程

override fun newCall(request :Request):Call = RealCall(this,request,forWebSocket=false)

RealCall实现了Call接口

interface Call:Cloneable{
    // 同步请求方法
    @Throws(IOException::class)
    fun execute():Response
    // 异步请求方法
    fun enqueue(responseCallback:Callback)
    // OkHttpClient实现的Factory接口,newCall方法
    fun interface Factory{
        fun newCall(request:Request):Call
    }
}

通过newCall得到RealCall对象,通过RealCall中execute和enqueue进行网络请求。

// RealCall.kt
override fun execute():Response{
    // 一个Call对象只能执行一次execute方法
    // 用CAS思想进行比较,提高效率
    check(executed.compareAndSet(false,true)){"Already Executed"}
    timeout.enter()
    // 监听器,开始进行网络请求了
    callStart()
    try{
        // 通过分发器进行任务分发
        // 将当前请求加入到一个同步队列中
        client.dispatcher.executed(this)
        // 获得相应的结果
        return getResponseWithInterceptorChain()
    }finally{
        // 收尾工作
        client.dispatcher.finish(this)
    }
}

同步请求分发器:

// Dispatcher.kt

// 正在执行的同步请求队列 
private val runningSyncCalls = ArrayDeque<RealCall>()
// 将当前请求加入到同步队列
@Synchronized internal fun executed(call:RealCall){
    runningSyncCalls.add(call)
}
// finish方法
internal fun finished(call:RealCall){
    finished(runningSyncCalls,call)
}
private fun <T>finished(calls:Deque<T>,call:T){
    // 分发器空闲时回调
    val idleCallBack:Runnable?
    synchronized(this){
        // 完成请求的call移除
        if(!calls.remove(call)) throw AssertionError("Call wasn't in-flight!)
        idleCallback = this.idleCallback
    }
    // 一般主要体现在异步请求中的用途
    val isRunning = promoteAndExecute()
    // 空闲回调
    if(!isRunning && idleCallback!=null){
        idleCallback.run()
    }
}

分发器仅仅将当前请求加入到一个同步请求队列中,请求完成后进行移除。在同步请求中finished只有一个回调作用,重点在异步请求回调中。

异步请求流程

// RealCall.kt
override fun enqueue(responseCallback:Callback){
    // 一个call对象只能执行一次execute方法
    check(executed.compareAndSet(false,true)){"Already Executed"}
    // 监听
    callStart()
    // 构建AsyncCall对象,交给dispatcher进行分发
    client.dispatcher.enqueue(AsyncCall(responseCallback))
}

AsyncCall 是RealCall的内部类,实现了Runnable接口,在线程池执行run方法

// RealCall.kt
internal inner class AsyncCall(private val responseCallback:Callback):Runnable{
    // callPersHost连接同一个host的连接数
    @Volatile var callsPerHost = AtomicInteger(0)
        priate set
    fun reuseCallsPerHostFrom(other:AsyncCall){
        // 连接数必须小于5
        this.callsPerHost = other.callsPerHost
    }
    val host:String get()=originalRequest.url.host
    val request:Request get()=originalRequest
    val call:RealCall get()=this@RealCall
    // 将asyncCall 添加到线程池中去执行的方法
    fun executerOn(executorService:ExecutorService){
        client.dispatcher.assertThreadDoesntHoldLock()
        var success = false
        try{
            // 线程池去执行当前AsyncCall对象的run方法
            executorService.execute(this)
            success = true
        }catch(e:RejectedExcutionException){
            val ioException = InterruptedIOException("executor rejected")
            ioException.initCause(e)
            noMoreExcahges(ioException)
            responseCallback.onFilure(this@RealCall,ioException)
        }finally{
            if(!success){
                // 收尾;内部调用promoteAndExcute()
                client.dispatcher.finished(this)
            }
        }
    }
    override fun run(){
        threadName("OkHttp ${redactedUrl()}")
        var signalledCallback = false
        timeout.enter()
        try{
            // 获取请求的相应结果
            val response= getResponseWithIntrceptorChain()
            signalledCallback = true
            // 请求成功回调
            responseCallback.onResponse(this@RealCall,response)
        }catch(e:IOException){
            if(signalledCallback){
                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")
                caceledException.addSuppressed(t)
                // 请求失败回调
                responseCallback.onFailure(this@RealCall,canceledException)
            }
            throw t
        }finally{
            // 收尾
            client.dispatcher.finish(this)
        }
    }
}

Dispatcher中

// 准备执行的异步请求队列
private val readyAsyncCalls = ArrayDeque<AsyncCall>()
// 正在执行的异步请求
private val runnningAsynCalls = ArrayDeque<AsyncCall>()
internal fun enqueue(call:AsyncCall){
    synchronized(this){
        // asyncCall加到准备执行的异步请求队列
        readyAsyncCalls.add(call)
        if(!call.call.forWebSocket){
            // 连接同一个host的请求数
            val existingCall = findExistingCallWithHost(call.host)
            if(existingCall!=null)call.reuseCallsPerHostForm(existingCall)
        }
    }
    // dispatcher进行分发call任务的方法
    promoteAndExecute()
}
// dispatch进行任务分发的方法
private fun promoteAndExecute():Boolean{
    this.assertThreadDoesntHoldLock()
    // 需要开始执行的任务集合
    val executableCalls = mutableListOf<AsyncCall>()
    val isRunning:Boolean
    synchronized(this){
        val i = readSyncCalls.iterator()
        // 迭代等待执行异步请求
        while(i.hasNext()){
            val asyncCall = i.next()
            // 正在执行异步请求的总任务不能大于64个,否则直接退出这个循环,不再将请求加到异步请求队列中
            if(runningAsyncCalls.size>=this.maxRequests) break;
            // 同一个host的请求不能大于5,否则直接跳过此call对象的添加,去遍历下一个asyncCall对象
            if(asyncCall.callsPerHost.get()>=this.maxRequestsPerHost)continue
            i.remove()
            // callsPerHost加一
            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
}

通过Dispatcher将AsyncCall对象哦通过挑选,加到线程池中

  1. 当前执行德总请求数要小于64个
  2. 连接同一个host请求,要保证数量小于5

image.png

五大拦截器

okhttp内置五大拦截器,通过责任链模式将请求逐层下发,最后往上返回结果。

  1. RetryAndFollowUpIntercepter:请求失败自动重试,如果DNS设置了多个ip地址会自动重试其余ip地址。
  2. BridgeIntercepter:补全请求中的请求头,Host Cookie Accept-Encoding等
  3. CacheIntercepter:选择性将响应结果进行保存,便下次直接读取,不再向服务器拿数据。
  4. ConnectIntercepter:建立连接并得到对应的socket;管理连接池,从中读取连接,达到连接复用的目的。
  5. CallServerInterceptor:与服务器建立连接,具体进行网络请求,将结果逐层返回的地方。

通过addInterceptor()加入拦截器。将所有拦截器包括用户自定义的蓝机器全部放到一个集合中,构建出RealIntercepterChain对象,调用proceed拦截器逐层分发工作。