前言说到源码,很多朋友都觉得复杂难懂。但是,如果是结构清晰、解耦彻底的优质源码库呢?OkHttp就是这样一个存在。对于这个原生的网络框架,你一定看过很多相关的源码分析。它的源代码易于阅读和清晰。所以今天我打算从设计模式的角度来重新阅读一下OkHttp的源码。主要内容分为两大类:OkHttp基本运行过程中涉及的设计模式(本文源码版本为okhttp:4.9.0,拦截器下期再讲)。开始:valokHttpClient=OkHttpClient()valrequest:Request=Request.Builder().url(url).build()okHttpClient.newCall(request).enqueue(object:Callback{overridefunonFailure(call:Call,e:IOException){Log.d(TAG,"onFailure:")}overridefunonResponse(call:Call,response:Response){Log.d(TAG,"onResponse:"+response.body?.string())}})从这个使用方法看,我提取了四个重要的信息:okHttpClientRequestnewCall(request)enqueue(Callback)大概意思我们先可以猜到:配置一个客户端实例okHttpClient和一个Request请求,然后这个请求被okHttpClient的newCall方法封装,最后,使用enqueue方法将其发送出去并收到Callback响应。接下来一个一个去认证,找里面的设计模式。okHttpClient首先看okhttp的客户端对象,也就是okHttpClient。OkHttpClientclient=newOkHttpClient.Builder().addInterceptor(newHttpLoggingInterceptor()).readTimeout(500,TimeUnit.MILLISECONDS).build();这里,我们实例化了一个HTTP客户端客户端,然后配置了它的一些参数,比如拦截器和超时时间。这样我们就可以通过一个统一的对象来调用接口或者方法来实现我们的需求,而内部各种复杂对象的调用和跳转就不需要我们去关心了。设计模式就是外观模式(facadepattern)。FacadePattern隐藏了系统的复杂性,为客户端提供了一个接口,客户端可以通过该接口访问系统。这类设计模式是一种结构模式,它在现有系统上增加一个接口,以隐藏系统的复杂性。重点是我们不需要了解系统和各个子系统之间的复杂关系,我们只需要调度这个门面,这里就是OkHttpClient。它就像接待员一样存在,我们告诉它我们的需求,要做的事情。然后接待员去内部处理,各种调度,终于完成了。外观模式的主要解决方案是降低访问复杂系统内部子系统的复杂度,简化客户端与它们之间的接口。这种模式也是三方库非常常见的设计模式。给你一个对象,你只需要调用这个对象就可以完成你的需求。当然,这里还有一个很明显的设计模式就是建造者模式,下面会提到。Requestvalrequest:Request=Request.Builder().url(url).build()//Request.ktopenclassBuilder{internalvarurl:HttpUrl?=nullinternalvarmethod:Stringinternalvarheaders:Headers.Builderinternalvarbody:RequestBody?=nullconstructor(){this.method="GET“this.headers=Headers.Builder()}openfunbuild():Request{returnRequest(checkNotNull(url){"url==null"},方法,headers.build(),body,tags.toImmutableMap())}}从Request的生成代码可以看出,使用了它的内部类Builder,然后通过Builder类组装了一个完整的带有各种参数的Request类。这是典型的建造者模式。Builder模式将复杂对象的构造与其表示分离,使得相同的构造过程可以创建不同的表示。我们可以使用Builder构造不同的Request请求。我们只需要传入不同的请求地址url、请求方法method、header信息headers、请求body即可。(这也是网络请求中请求报文的格式)这种通过构造可以形成不同表示的设计模式就是建造者模式,也用的比较多,主要是为了方便我们传入不同的参数给构造对象。再比如上面okHttpClient的构造。newCall(request)接下来就是调用OkHttpClient类的newCall方法,获取可以调用enqueue方法的接口。//使用valokHttpClient=OkHttpClient()okHttpClient.newCall(request)//OkHttpClient.ktopenclassOkHttpClientinternalconstructor(builder:Builder):Cloneable,Call.Factory,WebSocket.Factory{overridefunnewCall(request:Request):Call=RealCall,(this,forWebSocket=false)}//调用接口interfaceCall:Cloneable{funexecute():Responsefunenqueue(responseCallback:Callback)funinterfaceFactory{funnewCall(request:Request):Call}}newCall方法其实就是Call.Factory接口中的一个方法。即创建Call的过程是通过Call.Factory接口的newCall方法创建的,而该方法的实际实现则交给了该接口的子类OkHttpClient。定义统一创建对象的接口,然后子类决定实例化对象的设计模式就是工厂模式。在工厂模式下,我们在创建对象时不向客户端暴露创建逻辑,而是使用一个通用的接口指向新创建的对象。当然okhttp这边的工厂有点小,只有一条生产线,就是Call接口,只有一个产品RealCall。enqueue(Callback)下一个方法enqueue必须是okhttp源码的重中之重。刚才说了newCall方法其实是获取了RealCall对象,所以我就去RealCall的enqueue方法:overridefunenqueue(responseCallback:Callback){client.dispatcher.enqueue(AsyncCall(responseCallback))}然后转到dispatcher。//Dispatcher.ktvalexecutorService:ExecutorServiceget(){if(executorServiceOrNull==null){executorServiceOrNull=ThreadPoolExecutor(0,Int.MAX_VALUE,60,TimeUnit.SECONDS,SynchronousQueue(),threadFactory("$okHttpNameDispatcher",false))}returnexecutorServiceOrNull!!}internalfunenqueue(call:AsyncCall){promoteAndExecute()}privatefunpromoteAndExecute():Boolean{//通过线程池切换线程for(iin0untilexecutableCalls.size){valasyncCall=executableCalls[i]asyncCall.executeOn(executorService)}returnisRunning}//RealCall.ktfunexecuteOn(executorService:ExecutorService){try{executorService.execute(this)success=true}}这里使用了一个新的类Dispatcher,调用的方法是asyncCall.executeOn(executorService)。executorService这个参数大家应该不陌生吧,线程池。最后调用executorService.execute方法执行线程池任务。线程池的概念实际上使用了一种称为享元模式的设计模式。FlyweightPattern主要用于减少创建对象的数量,以减少内存使用,提高性能。这类设计模式是一种结构模式,它提供了一种方法来减少对象的数量,从而改善应用程序所需的对象结构。它的核心在于共享对象,很多池对象,比如线程池,连接池,都采用享元模式的设计模式。当然,okhttp中不仅有线程池,还有连接池,提供连接多路复用,管理所有socket连接。回到Dispatcher,这个类是做什么用的?是用来切换线程的,因为我们调用的enqueue是一个异步方法,所以最后会使用线程池来切换线程和执行任务。继续看execute(this)中的this任务。execute(this)overridefunrun(){threadName("OkHttp${redactedUrl()}"){try{//获取响应消息并回调Callbackvalresponse=getResponseWithInterceptorChain()responseCallback.onResponse(this@RealCall,response)}catch(e:IOException){if(!signalledCallback){responseCallback.onFailure(this@RealCall,e)}}catch(t:Throwable){cancel()if(!signalledCallback){responseCallback.onFailure(this@RealCall,canceledException)}}}没错,这里就是请求接口的地方。通过getResponseWithInterceptorChain方法获取response消息,然后通过Callback的onResponse方法回调,或者出现异常通过onFailure方法回调。同步方式不使用线程池吗?寻找execute方法:overridefunexecute():Response{//...returngetResponseWithInterceptorChain()}果然通过execute方法直接返回了getResponseWithInterceptorChain,也就是responsemessage。至此,okhttp的大致流程就结束了。这部分的流程大致是:设置请求消息->配置客户端参数->根据同步还是异步判断使用子线程->发起请求并获取响应消息->回调结果的剩余内容通过Callback接口都在getResponseWithInterceptorChain方法中,是okhttp的核心。getResponseWithInterpectionspectectereptereptresponseponsewithWithInterRchain():响应{//buildafullstackofienters.valinterpeptors.valinterpeptors=mutablepteptenteptionportof
