我一直感觉Android源码不能只简单的看,结合设计模式的思想再去看源码会有更深的理解。但是源码中应用某个设计模式,会根据实际情况会有一些差异。所以需要先透彻地理解设计模式。熟悉设计模式的应用场景就可以在Android源码的场景中找到相应的实现,熟悉设计模式就能够透过浩瀚的源码看到本质。
今天突然想到有关View点击事件的分发,感觉它的设计需求就很符合职责链模式(Chain of Responsibility)的应用场景。《Android源码设计模式解析与实战》中也说到了这点。所以本篇先重点介绍职责链模式。
如果已经理解该模式可以直接看下篇:
Android开发-分析ViewGroup、View的事件分发机制、结合职责链模式
使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。
想象这样的一个需求场景:在公司里面申请活动费用,申请人填写表格,交给上级审批,如果活动费用数额在上级的处理范围就处理,然后返回通过请求,否则上级再交给他的上级,处理逻辑同样。最后肯定有一个人处理。
针对上面的场景,就是职责链模式的最典型应用场景。
图中最关键的点就是:那条从Handler出发又指向自己的线,它就是实现链式调用的关键。
先上图是为了能够先看清职责链模式的大概框架。现在用实际代码和运行结果展示该模式的实现思想。
Handler抽象类作为职责接口,是所有职责对象的父类。
/**
* Created by LiCola on 2016/05/31 23:16
*/
public abstract class Handler {
/**
* 内部持有后继的职责对象
*/
protected Handler successor;
/**
* 注入后继职责对象
* @param successor
*/
public void setSuccessor(Handler successor) {
this.successor = successor;
}
/**
* 抽象父类统一请求处理方法
* 可以在方法中传值 作为判断条件 也可以不传值利用其它外部条件
*/
public abstract void handlerRequest(int cost);
}
为了简洁起见,只实现两个具体的职责对象,在方法内部都提供结果打印显示处理流程。
职责对象1
/**
* Created by LiCola on 2016/05/31 23:19
*/
public class ConcreteHandler1 extends Handler {
@Override
public void handlerRequest(int cost) {
/*
根据某个条件判断是否是自己的处理职责范围
一般是传入的值 或者外部数据
*/
if (cost<1000){
//如果是自己的职责范围 就在内部处理
System.out.println(this.getClass().getSimpleName()+" handler event");
}else {
//如果不在自己的职责范围 就判断是否有后继者
if (this.successor!=null){
//存在后继者 讲请求传递
System.out.println(this.getClass().getSimpleName()+" post handler");
this.successor.handlerRequest(cost);
}
}
}
}
职责对象2
/**
* Created by LiCola on 2016/05/31 23:19
*/
public class ConcreteHandler2 extends Handler {
@Override
public void handlerRequest(int cost) {
//简化流程 该对象一定能处理事件 不再下发
System.out.println(this.getClass().getSimpleName()+" handler event");
}
}
在这里客户端不是简单的调用,它还需要组装职责链,并发起请求
/**
* Created by LiCola on 2016/05/31 23:27
*/
public class Client {
public static void main(String[] args){
//客户端需要自己组装职责链
//构造职责对象
Handler handler1=new ConcreteHandler1();
Handler handler2=new ConcreteHandler2();
//组装链
handler1.setSuccessor(handler2);
//发起请求
handler1.handlerRequest(2000);
}
}
说明:
因为发出的请求不在ConcreteHandler1的处理范围,它将事件下发了,而ConcreteHandler2具有最高的权限范围,一定能处理请求。
上面的代码只是最简单的实现,只拼接了两个职责对象,根据实际情况可以有很多的职责对象。
再看一下职责链的调用过程加深印象:
看完上面的代码,相信你就能够理解这句话了:
客户端发出一个请求,链上的对象都有机会来处理这一请求,而客户端不需要知道谁是具体的处理对象。这样就实现了请求者和接受者之间的解耦,并且在客户端可以实现动态的组合职责链。
如果对上面的代码修改成:每个请求都不拦截,而是一步步的往下传,每一步都会处理请求对象,就可以变形成处理请求功能链。
这里提一个概念
隐式接受者:对于请求者而言,它并不知道最终的接受者是谁,但是一般情况下总会有一个对象来处理,因此称为隐式接受者
刚才的代码只是示例,在客户端只简单的拼接了链。实际使用中,职责链的构造是非常重要的点。那如果构造职责链?
按照实现的角度来分:
外部链
,我们上面的代码就是实现了外部链。内部链
按照构造链的数据来源分,也就是决定了按照什么顺序呢来组合链的数据可以分为:
如果是从外部获取数据来构建链,那么在程序运行时,会读取这些数据,然后根据数据的要求来获取相应的对象组合起来。
作为一个Android开发者,看到这里是不是感觉外部数据、配置文件、隐式接受者。会不会是App项目中的
manifest
文件中每个组件的 ,和我们平常的隐式启动Activity
我也只是猜想,没有经过具体的验证证明。附上Intent 和 Intent 过滤器,大家自己去看。
说了这么多,下面总结一下职责链模式的优缺点
职责链模式的最主要功能就是:动态组合,请求者和接受者解耦
说到职责链模式的缺点,突然想到Android开发中隐式Intent的隐患。
如果我们在代码中,想通过隐式启动处理某个用户请求,比如这样的代码
Uri uri = Uri.parse(link);
Intent intent= new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
可能的结果:当没有任何应用处理发送到 startActivity() 的隐式 Intent。如果出现这种情况,则调用将会失败,且应用会崩溃。
所以要验证 Activity 是否会接收 Intent,请对 Intent 对象调用 resolveActivity()。如果结果为非空,则至少有一个应用能够处理该 Intent,且可以安全调用 startActivity()。如果结果为空,则不应使用该 Intent。
对应的代码是这样的
// Verify that the intent will resolve to an activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
startActivity(sendIntent);
}
只所以把第一项说得这么细,就是因为它刚好就是我们Android开发中View的事件分发处理。手在屏幕上点击产生事件,每个View都是职责对象都可以处理事件。具体内容我会在下一篇博客详细分析。
下篇点这里: Android开发-分析ViewGroup、View的事件分发机制、结合职责链模式