文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Android设计模式之从OKHttp的拦截器中学习责任链模式

2024-12-02 23:42

关注

在这个链条上,每个责任单元都负责自己应该负责的责任,

而责任单元之间时互不干扰的,当有事件需要处理时,从链条的

首个责任单元开始处理,首个责任单元处理事件中自己负责的部分,

当处理完之后,若事件还未处理完

毕,还需进一步处理而同时当前责任单元无法处理或是不是自己负责的部分时,

当前责任单元将事件传

递给下一个责任单元,而后面该哪个责任单元处理,当前责任单元不关心,

当前责任单元只需处理自己

负责的部分并确定事件是否该继续传递到下一责任单元。

Android开发中用到的责任链模式

拦截器的使用

如需对cookie进行处理,我们一般会定义一个拦截器类继承自Interceptor,并重写intercept方法,

在该方法中处理cookie(添加或获取cookie保存),

以下代码实现的是向请求头加入cookie的拦截器,获取请求头中cookie方法与此类似,这里不做展示。

  1.  
  2. public class InterceptorOfAddCookie implements Interceptor { 
  3.     private static final String TAG = "InterceptorOfAddCookie"
  4.     @Override 
  5.     public Response intercept(Chain chain) throws IOException { 
  6.         Log.d(TAG, "InterceptorOfAddCookie"); 
  7.         return chain.proceed(chain.request()); 
  8.     } 

接着向okhttpClient对象中添加拦截器,使用的方法如下面的addInterceptor方法,参数就是创建的拦截器类对象,我这里是添加了两个拦截器,包括cookie的添加和获取。

  1. okHttpClient = new OkHttpClient 
  2.         .Builder() 
  3.         .addInterceptor(new InterceptorOfAddCookie()) 
  4.         .addInterceptor(new InterceptorOfReceivedCookie()) 
  5.         .build(); 

正式进入正题,切入点就在这里的addInterceptor方法,查看一下该方法的源码,看一下内部实现了怎样的逻辑处理。

  1.  
  2. public Builder addInterceptor(Interceptor interceptor) { 
  3.   if (interceptor == null) throw new IllegalArgumentException("interceptor == null"); 
  4.   interceptors.add(interceptor); 
  5.   return this; 

然后是在再看到ohhttpClient中使用拦截器并发送请求的过程

  1. okHttpClient = new OkHttpClient 
  2.         .Builder() 
  3.         .addInterceptor(new InterceptorOfAddCookie()) 
  4.         .addInterceptor(new InterceptorOfReceivedCookie()) 
  5.         .build(); 
  6. Request request = new Request.Builder().url("").get().build(); 
  7. Call call = okHttpClient.newCall(request); 
  8. call.enqueue(new Callback() { 
  9.     @Override 
  10.     public void onFailure(Call call, IOException e) { 
  11.     } 
  12.     @Override 
  13.     public void onResponse(Call call, Response response) throws IOException { 
  14.     } 
  15. }); 

其中拦截器是被添加到了okhttpClient的拦截器集合interceptors中,而通过okHttpClient.newCall(request)方法将okhttpClient引用到了RealCall中的client,

因为在okHttpClient.newCall()方法源码如下:

  1. @Override public Call newCall(Request request) { 
  2.   return RealCall.newRealCall(this, request, false ); 

可以看到newCall方法实际上创建的是RealCall对象,RealCall类实现了Call方法。接着再到call对象调用enqueue(CallBack callBack)方法发起请求,进入到enqueue内部查看,即进入到RealCall中的enqueue()方法中:

  1. @Override public void enqueue(Callback responseCallback) { 
  2.   synchronized (this) { 
  3.     if (executed) throw new IllegalStateException("Already Executed"); 
  4.     executed = true
  5.   } 
  6.   transmitter.callStart(); 
  7.   client.dispatcher().enqueue(new AsyncCall(responseCallback)); 

可以看到这边创建了一个AsyncCall对象,并传入CallBack对象,在RealCall类中可以找到合格内部类AsyncCall是继承自NamedRunnable,而进一步查看NamedRunnable是继承自

Runnable,所以AsyncCall可以被看作为一个Runnable

沿着client.dispatcher().enqueue(new AsyncCall(responseCallback));方法进入到Dispatcher类中的enqueue方法中,

  1. void enqueue(AsyncCall call) { 
  2.   synchronized (this) { 
  3.     readyAsyncCalls.add(call); 
  4.     // Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to 
  5.     // the same host. 
  6.     if (!call.get().forWebSocket) { 
  7.       AsyncCall existingCall = findExistingCallWithHost(call.host()); 
  8.       if (existingCall != null) call.reuseCallsPerHostFrom(existingCall); 
  9.     } 
  10.   } 
  11.   promoteAndExecute(); 

可以发现这里最终调用了promoterAndExecute()方法,再看一下这个方法中具体实现

  1. private boolean promoteAndExecute() { 
  2.   assert (!Thread.holdsLock(this)); 
  3.   List executableCalls = new ArrayList<>(); 
  4.   boolean isRunning; 
  5.   synchronized (this) { 
  6.     for (Iterator i = readyAsyncCalls.iterator(); i.hasNext(); ) { 
  7.       AsyncCall asyncCall = i.next(); 
  8.       if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity. 
  9.       if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity. 
  10.       i.remove(); 
  11.       asyncCall.callsPerHost().incrementAndGet(); 
  12.       executableCalls.add(asyncCall); 
  13.       runningAsyncCalls.add(asyncCall); 
  14.     } 
  15.     isRunning = runningCallsCount() > 0; 
  16.   } 
  17.   for (int i = 0, size = executableCalls.size(); i < size; i++) { 
  18.     AsyncCall asyncCall = executableCalls.get(i); 
  19.     asyncCall.executeOn(executorService()); 
  20.   } 
  21.   return isRunning; 

可以发现在这个方法最终会调用

  1. asyncCall.executeOn(executorService());这里的executeOn传入的参为线程池对象 
  2. ExecutorService实例,在回到AsyncCall类中查看executeOn方法的具体实现, 
  3. void executeOn(ExecutorService executorService) { 
  4.   assert (!Thread.holdsLock(client.dispatcher())); 
  5.   boolean success = false
  6.   try { 
  7.     executorService.execute(this); 
  8.     success = true
  9.   } catch (RejectedExecutionException e) { 
  10.     InterruptedIOException ioException = new InterruptedIOException("executor rejected"); 
  11.     ioException.initCause(e); 
  12.     transmitter.noMoreExchanges(ioException); 
  13.     responseCallback.onFailure(RealCall.this, ioException); 
  14.   } finally { 
  15.     if (!success) { 
  16.       client.dispatcher().finished(this); // This call is no longer running! 
  17.     } 
  18.   } 

可以看到executorService.execute(this);就是将this(即AsyCall对象,而AsyncCall之前提到它的父类NameRunnable是实现了Runnable的)传入到线程池中,当线程池执行该Runnable任务时回执行run()方法,而可以看到AsyncCall父类NameRunnable类中的run()方法是调用了自身的execute()方法,而在AsyncCall中重写了该execute()方法,即执行NameRunnable的execute()相当于执行了AsyncCall类中的execute()方法。

再看到execute()方法中,在这个方法中主要看到Response response = getResponseWithInterceptorChain();这一行,查看一下getResponseWithInterceptorChain()方法的实现:

  1. Response getResponseWithInterceptorChain() throws IOException { 
  2.   // Build a full stack of interceptors. 
  3.   List interceptors = new ArrayList<>(); 
  4.   interceptors.addAll(client.interceptors()); 
  5.   interceptors.add(new RetryAndFollowUpInterceptor(client)); 
  6.   interceptors.add(new BridgeInterceptor(client.cookieJar())); 
  7.   interceptors.add(new CacheInterceptor(client.internalCache())); 
  8.   interceptors.add(new ConnectInterceptor(client)); 
  9.   if (!forWebSocket) { 
  10.     interceptors.addAll(client.networkInterceptors()); 
  11.   } 
  12.   interceptors.add(new CallServerInterceptor(forWebSocket)); 
  13.   Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0, 
  14.       originalRequest, this, client.connectTimeoutMillis(), 
  15.       client.readTimeoutMillis(), client.writeTimeoutMillis()); 
  16.   boolean calledNoMoreExchanges = false
  17.   try { 
  18.     Response response = chain.proceed(originalRequest); 
  19.     if (transmitter.isCanceled()) { 
  20.       closeQuietly(response); 
  21.       throw new IOException("Canceled"); 
  22.     } 
  23.     return response; 
  24.   } catch (IOException e) { 
  25.     calledNoMoreExchanges = true
  26.     throw transmitter.noMoreExchanges(e); 
  27.   } finally { 
  28.     if (!calledNoMoreExchanges) { 
  29.       transmitter.noMoreExchanges(null); 
  30.     } 
  31.   } 

这里发现了创建了一个拦截器集合,并通过client.interceptors()方法获取到了client的拦截器集合interceptors,随后也往新创建的拦截器集合添加了其他的拦截器,而client中的拦截器集合包含的只是我们自定义的拦截器集合,还记得起初提到的创建okhttpClient实例时通过addInterceptor方法添加自定义拦截器吗?所以在这里也可以发现,如果处理拦截器的时候会先执行我们自定义的拦截器再执行内部的拦截器。

再往下看会发现Interceptor.Chain chain = new RealInterceptorChain()传入iterceptors创建了Interceptor.Chain,这个就是责任链,将拦截器集合都放到这个链条上,组成了一个拦截器责任链。

注意:RealInterceptorChain实现了Interceptor接口中的内部接口Chain接口。

接着往下看Response response = chain.proceed(originalRequest);这里执行了chain的proceed方法并传入了Request对象originalRequest(即是我们最初创建

Call call = okHttpClient.newCall(request),RealCall对象)

接着再看chain.proceed方法的具体实现(进入到RealInterceptorChain类中,因为该类实现了Chain接口,所以具体逻辑实现会在该类的proceed中):

  1. @Override public Response proceed(Request request) throws IOException { 
  2.   return proceed(request, transmitter, exchange); 

其内部依然调用proceed方法,再看自身的proceed方法:

  1. public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange) 
  2.     throws IOException { 
  3.   if (index >= interceptors.size()) throw new AssertionError(); 
  4.   calls++; 
  5.   // If we already have a stream, confirm that the incoming request will use it. 
  6.   if (this.exchange != null && !this.exchange.connection().supportsUrl(request.url())) { 
  7.     throw new IllegalStateException("network interceptor " + interceptors.get(index - 1) 
  8.         + " must retain the same host and port"); 
  9.   } 
  10.   // If we already have a stream, confirm that this is the only call to chain.proceed(). 
  11.   if (this.exchange != null && calls > 1) { 
  12.     throw new IllegalStateException("network interceptor " + interceptors.get(index - 1) 
  13.         + " must call proceed() exactly once"); 
  14.   } 
  15.   // Call the next interceptor in the chain. 
  16.   RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange, 
  17.       index + 1, request, call, connectTimeout, readTimeout, writeTimeout); 
  18.   Interceptor interceptor = interceptors.get(index); 
  19.   Response response = interceptor.intercept(next); 
  20.   // Confirm that the next interceptor made its required call to chain.proceed(). 
  21.   if (exchange != null && index + 1 < interceptors.size() && next.calls != 1) { 
  22.     throw new IllegalStateException("network interceptor " + interceptor 
  23.         + " must call proceed() exactly once"); 
  24.   } 
  25.   // Confirm that the intercepted response isn't null
  26.   if (response == null) { 
  27.     throw new NullPointerException("interceptor " + interceptor + " returned null"); 
  28.   } 
  29.   if (response.body() == null) { 
  30.     throw new IllegalStateException( 
  31.         "interceptor " + interceptor + " returned a response with no body"); 
  32.   } 
  33.   return response; 
  34. //其中最关键的代码在于这三行代码 
  35. RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,index + 1, request, call, connectTimeout, readTimeout, writeTimeout); 
  36. Interceptor interceptor = interceptors.get(index); 
  37. Response response = interceptor.intercept(next); 

这里又通过调用RealInterceptorChain类构造方法,而这里不同的是,参数index的值为index+1,并且在该类中index为全局变量,所以index的值增量为1,通过index将拦截器集合interceptors中的第index个拦截器interceptor取出,并执行interceptor的interceprt(Chain)方法,接着我们回顾一下最初我们自定义的拦截其中实现了什么逻辑:

  1. public class InterceptorOfAddCookie implements Interceptor { 
  2.     private static final String TAG = "InterceptorOfAddCookie"
  3.     @Override 
  4.     public Response intercept(Chain chain) throws IOException { 
  5.         Log.d(TAG, "InterceptorOfAddCookie"); 
  6.         return chain.proceed(chain.request()); 
  7.     } 

可以看到intercept(Chain)(这里的Chain为RealInterceptorChain)方法中调用了

chain.proceed(Request)方法,即又调用了proceed方法,而前面分析到RealInterceptorChain重写父接口的proceed方法的具体实现中又调用了RealInterceptorChain自身的proceed方法,而自身的proceed方法又调用了interceptor.intercept()方法,所以这里是形成了一个递归,而这里的递归

思想就是责任链模式的核心思想。即不断执行拦截器interceptor中的intercept(Chain)方法,而我们只需要在intercept方法中实现我们的逻辑即可,可以通过Chain获取到Request或者Response,实现对请求体或请求头的处理,如处理请求头的cookie。

总结

okhttp中的拦截器实现可以总结为如下:

这样的设计方法明显易于后续扩展,

而不会涉及到期待责任单元的逻辑更改,

只需创建一个类要实现责任单元接口,创建这个类的实例,

并将其添加到责任链中即可。该设计模式的关键思想在于递归,

在责任链Chain中通过递归调用责任单元方法,

即可将要处理的事件沿着责任链传递处理,

也可以在责任单元中通过逻辑判断是否要将事件继续传递到下一责任单元。

本文转载自微信公众号「Android开发编程 」

 

来源:Android开发编程 内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯