Android 源码中的责任链模式
Android 源码中的责任链模式
Android 源码中的责任链模式
1、View 的 onTouchEvent()
从子 View 开始,是否消费,如果消费了,那么事件就交给该子 View 处理;如果没有消费,继续传递给其 Parent,看是否消费;这样向上传递,只要该链上有一个消费了该事件,那么事件就交给它处理。
2、OkHttp 的 Interceptor
OkHttp 中的拦截器是一条链,OkHttp 中的核心功能,都是由 Interceptor 实现,并串联在这条链上,只要其中有一个 Interceptor 处理了 Response,那么就不再往下传递了;否则一直往下传递给下一个 Interceptor。
1
2
3
4
5
6
7
8
9
10
// RealCall#execute()
@Override public Response execute() throws IOException {
// …
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
// …
return result;
}
// …
然后走入到了 getResponseWithInterceptorChain()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
Interceptor 接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Response proceed(Request request) throws IOException;
/**
* Returns the connection the request will be executed on. This is only available in the chains
* of network interceptors; for application interceptors this is always null.
*/
@Nullable Connection connection();
Call call();
int connectTimeoutMillis();
Chain withConnectTimeout(int timeout, TimeUnit unit);
int readTimeoutMillis();
Chain withReadTimeout(int timeout, TimeUnit unit);
int writeTimeoutMillis();
Chain withWriteTimeout(int timeout, TimeUnit unit);
}
}
RealInterceptorChain
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
// interceptor走完
if (index >= interceptors.size()) throw new AssertionError();
// …
// Call the next interceptor in the chain. // 构造下一个chain,并带上interceptors,和index+1索引
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
// 获取到当前的Interceptor
Interceptor interceptor = interceptors.get(index);
// 调用interceptor,并传入下一个chain,如果下一个interceptor不拦截,就调用next.proceed()就可以交给下一个Interceptor处理了
Response response = interceptor.intercept(next);
// …
return response;
}
应用
网络请求错误码的全局处理
ARouter 降级服务错误处理
3、ARouter 中的拦截器 增加了线程池
类、接口的定义
InterceptorService 提供拦截的服务,可有可无
ARouter 中 InterceptorService 拦截器的服务,进行路由的时候,会查找是否定义了 InterceptorService,如果定义了 InterceptorService,会调用 doInterceptions 来进行拦截
1
2
3
4
5
6
public interface InterceptorService extends IProvider {
/**
* Do interceptions
*/
void doInterceptions(Postcard postcard, InterceptorCallback callback);
}
IInterceptor 所有拦截器需要实现的接口 类似 OKHttp 的 Interceptor
1
2
3
4
5
6
7
8
9
10
public interface IInterceptor {
/**
* The operation of this interceptor.
*
* @param postcard meta
* @param callback cb
*/
void process(Postcard postcard, InterceptorCallback callback);
}
InterceptorCallback 拦截器的 callback,类似 OkHttp 的 Interceptor.Chain
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface InterceptorCallback {
/**
* Continue process
*
* @param postcard route meta
*/
void onContinue(Postcard postcard);
/**
* Interrupt process, pipeline will be destroy when this method called.
*
* @param exception Reson of interrupt.
*/
void onInterrupt(Throwable exception);
}
Postcard 明信片 类似 OkHttp 的 Request/Response,携带参数和返回结果
xin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public final class Postcard extends RouteMeta {
// … 其他参数
private Object tag; // A tag prepare for some thing wrong.
private int timeout = 300; // Navigation timeout, TimeUnit.Second
public Object getTag() {
return tag;
}
public Postcard setTag(Object tag) {
this.tag = tag;
return this;
}
public int getTimeout() {
return timeout;
}
/**
* Set timeout of navigation this time.
* @param timeout timeout
* @return this
*/
public Postcard setTimeout(int timeout) {
this.timeout = timeout;
return this;
}
}
具体源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// _ARouter.java
private static InterceptorService interceptorService;
static void afterInit() {
// Trigger interceptor init, use byName.
interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
}
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
// …
interceptorService.doInterceptions(postcard, new InterceptorCallback() {
/**
* Continue process
*
* @param postcard route meta
*/
@Override
public void onContinue(Postcard postcard) {
_navigation(context, postcard, requestCode, callback);
}
/**
* Interrupt process, pipeline will be destory when this method called.
*
* @param exception Reson of interrupt.
*/
@Override
public void onInterrupt(Throwable exception) {
if (null != callback) {
callback.onInterrupt(postcard);
}
logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());
}
});
// …
}
接着看 InterceptorServiceImpl:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
public class InterceptorServiceImpl implements InterceptorService {
private static boolean interceptorHasInit;
private static final Object interceptorInitLock = new Object();
@Override
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
if (null != Warehouse.interceptors && Warehouse.interceptors.size() > 0) {
checkInterceptorsInitStatus();
if (!interceptorHasInit) {
callback.onInterrupt(new HandlerException("Interceptors initialization takes too much time."));
return;
}
LogisticsCenter.executor.execute(new Runnable() {
@Override
public void run() {
CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
try {
_execute(0, interceptorCounter, postcard);
interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
if (interceptorCounter.getCount() > 0) { // Cancel the navigation this time, if it hasn't return anythings.
callback.onInterrupt(new HandlerException("The interceptor processing timed out."));
} else if (null != postcard.getTag()) { // Maybe some exception in the tag.
callback.onInterrupt(new HandlerException(postcard.getTag().toString()));
} else {
callback.onContinue(postcard);
}
} catch (Exception e) {
callback.onInterrupt(e);
}
}
});
} else {
callback.onContinue(postcard);
}
}
/**
* Excute interceptor
*
* @param index current interceptor index
* @param counter interceptor counter
* @param postcard routeMeta
*/
private static void _execute(final int index, final CancelableCountDownLatch counter, final Postcard postcard) {
if (index < Warehouse.interceptors.size()) {
IInterceptor iInterceptor = Warehouse.interceptors.get(index);
iInterceptor.process(postcard, new InterceptorCallback() {
@Override
public void onContinue(Postcard postcard) {
// Last interceptor excute over with no exception.
counter.countDown();
_execute(index + 1, counter, postcard); // When counter is down, it will be execute continue ,but index bigger than interceptors size, then U know.
}
@Override
public void onInterrupt(Throwable exception) {
// Last interceptor excute over with fatal exception.
postcard.setTag(null == exception ? new HandlerException("No message.") : exception.getMessage()); // save the exception message for backup.
counter.cancel();
// Be attention, maybe the thread in callback has been changed,
// then the catch block(L207) will be invalid.
// The worst is the thread changed to main thread, then the app will be crash, if you throw this exception!
// if (!Looper.getMainLooper().equals(Looper.myLooper())) { // You shouldn't throw the exception if the thread is main thread.
// throw new HandlerException(exception.getMessage());
// }
}
});
}
}
@Override
public void init(final Context context) {
LogisticsCenter.executor.execute(new Runnable() {
@Override
public void run() {
if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
for (Map.Entry<Integer, Class<? extends IInterceptor>> entry : Warehouse.interceptorsIndex.entrySet()) {
Class<? extends IInterceptor> interceptorClass = entry.getValue();
try {
IInterceptor iInterceptor = interceptorClass.getConstructor().newInstance();
iInterceptor.init(context);
Warehouse.interceptors.add(iInterceptor);
} catch (Exception ex) {
throw new HandlerException(TAG + "ARouter init interceptor error! name = [" + interceptorClass.getName() + "], reason = [" + ex.getMessage() + "]");
}
}
interceptorHasInit = true;
logger.info(TAG, "ARouter interceptors init over.");
synchronized (interceptorInitLock) {
interceptorInitLock.notifyAll();
}
}
}
});
}
private static void checkInterceptorsInitStatus() {
synchronized (interceptorInitLock) {
while (!interceptorHasInit) {
try {
interceptorInitLock.wait(10 * 1000);
} catch (InterruptedException e) {
throw new HandlerException(TAG + "Interceptor init cost too much time error! reason = [" + e.getMessage() + "]");
}
}
}
}
}
责任链在 Android 中的应用
多媒体文件批量后台上传功能 remix
多媒体文件在后台上传,需要获取七牛的 token,压缩文件,上传自己的后台服务器去,这些拦截器都需要运行在子线程中
可以增加一个阻塞队列,后台单线程一个个上传文件
IM/WebSocket 的消息发送接收功能
网络框架 错误码全局处理
Android ARouter
路由框架全局降级服务处理
公共部分
定义 Processor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
interface IARouterDegradeProcessor {
/**
* 是否要处理该path
* @return true处理;false不处理,调用下一个processor处理
*/
fun interesting(postcard: Postcard): Boolean
/**
* interesting()返回true后调用该方法处理
*/
@Throws(Exception::class)
fun onLost(chain: Chain)
/**
* 输入参数都一个方法,返回值为输入参数
*/
interface Chain {
/**
* 输入参数Postcard
*/
fun postcard(): Postcard
/**
* 输入参数Context,如果是通过h5 scheme url调用的Context为null
*/
fun context(): Context?
/**
* 如要让下一个Processor处理,调用该方法
*/
@Throws(Exception::class)
fun onContinue(postcard: Postcard)
}
}
定义 ProcessorChain
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
internal class ARouterDegradeProcessorChain(
private val processors: List<IARouterDegradeProcessor>?,
private val index: Int,
private val context: Context?,
private val postcard: Postcard) : IARouterDegradeProcessor.Chain {
override fun postcard(): Postcard {
return postcard
}
override fun context(): Context? {
return context
}
override fun onContinue(postcard: Postcard) {
requireNotNull(processors)
if (index >= processors.size) {
throw IllegalArgumentException("越界了:index:" + index + ",size:" + processors.size)
}
val next = ARouterDegradeProcessorChain(processors, index + 1, context, postcard)
val processor = processors[index]
if (processor.interesting(postcard)) {
processor.onLost(next)
} else {
next.onContinue(postcard)
}
}
}
业务部分
调用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Route(path = ARouterConstants.Service.ROUTER_SERVICE_DEGRADE)
class ARouterDegradeService : DegradeService {
override fun init(context: Context?) {
LogUtils.w(ARouterConstants.TAG, "${this.javaClass.simpleName} init()")
}
override fun onLost(context: Context?, postcard: Postcard?) {
ARouterDegradeDelegate.onLost(context, postcard)
}
}
object ARouterDegradeDelegate {
private val processors: List<IARouterDegradeProcessor> by lazy {
listOf(
ARouterDegradeRelationProcessor(),
ARouterDegradeReturnSmallRoomProcessor(),
ARouterDegradeOpenLuckyDrawProcessor(),
ARouterDegradeFindTopHotRoomProcessor(),
ARouterDegradeOpenGiftPanelProcessor(),
ARouterDegradeToastProcessor() // 这个一定要放最后
)
}
fun onLost(context: Context?, postcard: Postcard?) {
if (postcard != null) {
val chain = ARouterDegradeProcessorChain(processors, 0, context, postcard)
chain.onContinue(postcard)
}
}
}
具体的 Processor1:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
* 本地查找缓存的最近房间,打开礼物面板,并选中礼物
*/
class ARouterDegradeOpenGiftPanelProcessor : IARouterDegradeProcessor {
@Autowired
@JvmField
var roomProvider: IRoomProvider? = null
init {
ARouter.getInstance().inject(this)
}
companion object {
private val HANDLE_PATHS = arrayOf(Constants.DeepLink.OPEN_LATEST_ROOM_LUCKY_GIFT)
}
override fun interesting(postcard: Postcard): Boolean {
val path = postcard.getSafePath()
if (path !in HANDLE_PATHS) {
return false
}
LogUtils.w(ARouterConstants.TAG, "${anchor("interesting")} 本地查找缓存的最近房间,打开礼物面板,并选中礼物,$postcard")
return true
}
override fun onLost(chain: IARouterDegradeProcessor.Chain) {
val postcard = chain.postcard()
when (postcard.path ?: "") {
Constants.DeepLink.OPEN_LATEST_ROOM_LUCKY_GIFT -> { // 本地查找缓存的最近房间,打开幸运抽奖
val giftId = postcard.uri.getQueryParameter("gift_id")?.toLongOrNull()
?: Constants.INDEX_DEFAULT
val from = postcard.uri.getQueryParameter(Constants.IntentKey.FROM)
val giftPanelDeepLink = roomProvider?.getGiftPanelDeepLink(giftId, from = from)
showShortDebug("本地查找缓存的最近房间,打开礼物面板,并选中指定的幸运礼物giftId=$giftId, deeplink=$giftPanelDeepLink")
giftPanelDeepLink?.let { ARouter.getInstance().build(it).navigation() }
}
}
}
}
具体的 Processor2:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 这个放在最后面,处理deeplink找不到,toast提示文案
*/
class ARouterDegradeToastProcessor : IARouterDegradeProcessor {
override fun interesting(postcard: Postcard): Boolean {
LogUtils.e(ARouterConstants.TAG, "${anchor("interesting")} 处理deeplink找不到,toast提示文案 $postcard")
return true
}
override fun onLost(chain: IARouterDegradeProcessor.Chain) {
ResUtils.getStr(R.string.common_deeplink_not_found_toast_text).showLongSafe()
}
}
Android 弹窗优先级
Android Deeplink/Applink 预处理器
TODO:
本文由作者按照 CC BY 4.0 进行授权