【Android设计模式】从Okhttp看懂责任链模式
前言
平时写代码的时候可能为了完成某一个任务而只是应付性地编码,然后写完理直气壮地来一句"又不是不能用!",但如果要把编码当作一项艺术来打造,那就需要结合我们的设计模式了。设计模式可以运用在诸多方面,让我们的代码更加优雅。设计模式在Android中也是无处不在,动态代理、建造者、工厂、适配器....等等等等,可见它对我们的重要性。最近结合责任链模式对项目中的启动弹窗处理进行了重构。
什么是责任链模式?
责任链模式就是一个请求发送到接收者,接收者组成链式结构,沿着链式结构传递请求,直到有对象处理请求。
比如在一个场景可能有ABCD四种情况的处理,将它们组成一条链式结构,我把请求发送给A,A接收到这个请求后,判断是否处理,如果不处理就直接传递给B,同样B如果不处理就传递给C,直至传递到D。这种一级一级的传递就是链式结构。传递过程中也可以直接处理结束链式循环,比如处理到B时,处理掉请求,不会继续传递到C和D。
如何实现责任链模式?
最近项目中刚好有这么一个需求,在启动应用首页时,会有多个满足该场景的弹窗需要弹出,比如隐私协议弹窗、强制更新弹窗、用户引导弹窗、通知权限开启弹窗;每个弹窗都是要满足一定的条件才会弹出,如果不控制好它们之间的逻辑,可能会出现同时弹出两个弹窗的情况,从用户体验来看肯定是不合适的。因此需要让它们一一判断是否满足条件,最终只展示一个。
如果不使用设计模式,可能处理方式就是通过嵌套的if判断去逐个判断是否满足条件,也能实现效果,伪代码如下:
public void checkShowDialog(){
if(强制更新弹窗满足条件){
//弹出强制更新
}else{
if(隐私协议弹窗满足条件){
//弹出隐私协议
}else{
if(用户引导弹窗满足条件){
//弹出用户引导
}else{
if(满足通知权限弹窗条件){
//弹出通知权限
}
}
}
}
}
但一方面代码结构看起来不清晰,一方面如果要控制它们弹出的优先级,又要调整很多判断代码。这时候责任链模式就可以派上用场了。
1. 定义一个责任链处理者抽象类,也是本模式最关键的地方:
public abstract class AbstractCheck {
protected AbstractCheck nextCheck;
//关联下一个处理者
public void setNextCheck(AbstractCheck next) {
this.nextCheck = next;
}
//检查该弹窗展示的条件是否满足
abstract void checkShow(ICheckCallback callback);
}
其实就是持有下一个处理者的实例,然后当自己不满足条件时,可以调用 nextCheck
的 checkShow方法,即通知下一个处理者,我不处理,轮到你处理请求了。
接着就可以将每个弹窗的判断条件都单独定义成一个继承于AbstractCheck的子类,例如更新弹窗的写法会变成这样:
/**
* 更新弹窗职责检测类
*/
public class UpgradeDialogCheck extends AbstractCheck{
@Override
void checkShow() {
reqHasNewVersion(new RequestCallback<Boolean>(){
@Override
public void onSuccess(Boolean param) {
if(param){
showUpgradeDialog();
}else{
if(nextCheck != null){
nextCheck.checkShow();
}
}
}
@Override
public void onFailed(int code) {
if(nextCheck != null){
nextCheck.checkShow();
}
}
});
}
public void reqHasNewVersion(RequestCallback<Boolean> callback){
//请求更新接口
}
public void showUpgradeDialog(){
//展示更新弹窗
}
}
可以看到,在重写的父类的checkShow方法里面,先是发起了网络请求,获取得知是否需要更新,如果需要,就展示更新弹窗,如果不需要,就将这个展示权通过 nextCheck.checkShow(callback)
丢给它的下一个处理者去决定。其他的弹窗也是依照这样的写法去定义,就不一一列举了,虽然代码量变多了,但是每个弹窗的逻辑都在它们各自的检测类里面去处理,代码结构也变得清晰很多。
3. 定义好了每个弹窗的职责类之后,就差最后一步,关联它们的关系,将它们串成一条链:
public class DialogManager {
private AbstractCheck upgradeCheck;
public DialogManager() {
upgradeCheck = new UpgradeDialogCheck();
AbstractCheck privacyCheck = new PrivacyDialogCheck();
AbstractCheck guideCheck = new GuideDialogCheck();
AbstractCheck notifyCheck = new NotifyDialogCheck();
upgradeCheck.nextCheck = privacyCheck;
privacyCheck.nextCheck = guideCheck;
guideCheck.nextCheck = notifyCheck;
}
/**
* 调用第一个处理者进行处理,触发整条链
*/
public void startCheck(){
upgradeCheck.checkShow();
}
}
可以看到,两两之间都通过nextCheck关联起来,谁先处理谁后处理,优先级分明,通过调用 startCheck
触发第一个处理者处理,后面就会自动按着职责链的顺序走下去。责任链模式中用于处理请求的每个对象,并不需要关心该请求的上一个或下一个处理者,只需要将当前处理的进度,组织成请求处理者遵循的接口规范即可,并且确保请求处理者的响应,也遵循统一的接口规范,这样就能使请求处理链路,在某一时刻一旦满足客户端需求时,能直接打断,并将响应结果反馈给客户端。 且如果某一天产品要叫你调整它们之间的优先级,就方便很多了~
Okhttp源码中的责任链模式
我们都知道,okhttp可以针对请求配置很多拦截器,而这些拦截器正是通过责任链模式链接起来,并最终返回处理的结果,可以从Okhttp的 execute
方法看到:
@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
可以看到首先调用的就是 getResponseWithInterceptorChain
返回拦截的结果赋值给Response对象,进一步看 getResponseWithInterceptorChain
的源码:
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
首先创建了一个interceptors容器,接着分别将客户端自定义、请求重试、桥接、缓存等不同职责的拦截器,依次添加到了这个容器中,每个拦截器就相当于责任链上对请求的不同处理者。最后,创建了一个RealInterceptorChain拦截链,并将interceptors容器添加到了这个拦截链中,再通过RealInterceptorChain的proceed方法触发拦截链,使每个拦截器被依次执行。
那我们随便找一个拦截器,看看它是设计的思路,查看CacheInterceptor缓存拦截器的源码如下:
继承于一个Interceptor接口,并实现它的
intercept
,且 intercept
有一个参数Chain,且调用了chain的process方法:
责任链模式
为何要这么做,先留个悬念,我们看回
RealInterceptorChain
拦截链这个类,看下它对这些拦截器做了什么:首先,它是实现了Chain接口:
public final class RealInterceptorChain implements Interceptor.Chain {
实现process方法:
责任链模式
可以看到,RealInterceptorChain的process方法中,会生成一个RealInterceptorChain对象,且注意到index+1,即生成下一个Chain对象,并且同时获取拦截器集合里的下一个拦截器,调用它的intercept,将下一个Chain(next)作为参数传给他去处理,回顾到刚才上面说的,拦截器的interpect里面调用了chain的process,也就是说,每一个拦截器都会持有下一个拦截器的chain对象,并通过chain的process方法,触发RealInterceptorChain里的index下标再+1,从而串联起整个拦截链。
总结
责任链模式的本质是解耦请求和处理,将节点处理者组合成了链式结构,并允许节点自身决定是否进行请求处理或转发,相当于让请求流动了起来。因为责任链模式具备链式结构,当处理者较多时,可以很清晰地排列出来,且后续替换处理者或调整优先级时也减少很多维护成本。