Android_Banner.jpg
简介
- 通常我们使用ARouter的拦截器,用于页面跳转时的拦截
- 比如,我们的某些页面,需要用户登录之后才能进入,这时候我们可以使用拦截器。
基本使用
- 声明一个类 MyInterceptor implements IInterceptor 实现他的process()方法
- 在 process()方法里面我们可以做拦截的操作
- 当然我们还需要ARouter提供的 @Interceptor(priority = 5) 注解 作用于当前我们的自定义注解类上,并未它设置优先级
- 这个优先级值越小那么它的执行优先级就越高(和存储它的映射关系的数据结构有关系是一个红黑树结构)。
@Interceptor(priority = 5)
public class TestInterceptor implements IInterceptor {
private static final String TAG = "TestInterceptor";
@Override
public void process(Postcard postcard, InterceptorCallback callback) {
Log.d(TAG, "process: ----> perform");
if (postcard.getPath().equals("/interceptor/test")) {
Log.d(TAG, "TestInterceptor ---> /interceptor/test ");
callback.onContinue(postcard);
} else {
callback.onInterrupt(null);
}
}
@Override
public void init(Context context) {
Log.d(TAG, "init: ----> perform");
}
}
- 这样当我们进行页面跳转的时候,就会执行拦截器中的 process方法了
- 是不是觉得很神奇,那么接下来我们就分析下它是怎么个运行原理的
原理分析
- 其实在之前分析页面跳转的时候,我们就涉及到拦截器的部分,只要是关于页面跳转的 都会去执行项目中的拦截器。
- 在ARouter进行初始化的时候,当我们初始化成功的时候,有一行代码没有进行分析
public static void init(Application application) {
if (!hasInit) {
logger = _ARouter.logger;
_ARouter.logger.info(Consts.TAG, "ARouter init start.");
hasInit = _ARouter.init(application);
//------> 分析1:
if (hasInit) {
_ARouter.afterInit();
}
_ARouter.logger.info(Consts.TAG, "ARouter init over.");
}
}
_ARouter # afterInit()
/**
* 在初始化的时候,执行这个方法
* /arouter/service/interceptor 路由路径是作用于 InterceptorServiceImpl
* InterceptorServiceImpl implements InterceptorService
* InterceptorService extends IProvider
* InterceptorServiceImpl 是provider类型的
*/
static void afterInit() {
// 获取到InterceptorServiceImpl的实例
interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
}
// 当我们获取到InterceptorServiceImpl的实例的同时,接着就执行了它的init方法?
// 在补充Postcard数据的时候 也就是 LogisticsCenter #completion()
switch (routeMeta.getType()) {
case PROVIDER: // if the route is provider, should find its instance
// Its provider, so it must implement IProvider
Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
IProvider instance = Warehouse.providers.get(providerMeta);
if (null == instance) { // There's no instance of this provider
IProvider provider;
try {
// 通过反射拿到实例对象
provider = providerMeta.getConstructor().newInstance();
//执行init方法
provider.init(mContext);
Warehouse.providers.put(providerMeta, provider);
instance = provider;
} catch (Exception e) {
throw new HandlerException("Init provider failed! " + e.getMessage());
}
}
postcard.setProvider(instance);
postcard.greenChannel(); // Provider should skip all of interceptors
break;
case FRAGMENT:
postcard.greenChannel(); // Fragment needn't interceptors
default:
break;
}
InterceptorServiceImpl # init()
@Override
public void init(final Context context) {
//线程池中执行
LogisticsCenter.executor.execute(new Runnable() {
@Override
public void run() {
// 那么我们的数据仓库中的interceptorIndex是什么时候填充数据的呢?
// 我们知道在每次进行页面跳转的时候,都会执行拦截器的操作,所在在ARouter初始化的时候,就会将拦截器加载存储到 Warehouse.interceptorsIndex
// 可以在LogisticsCenter.init()可以找到
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();
//执行了init方法
iInterceptor.init(context);
// 将我们获取到的自定义拦截器实例装填到数据仓库的的interceptors的集合中
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();
}
}
}
});
}
那么我们获取到的InterceptorServiceImpl实例什么时候使用呢?
// 我们进行页面跳转的时候,在我们进行补充Posrcard数据完之后,有使用过的哟
// 正常 我们进行页面跳转的时候,不是绿色通道需要去调用InterceptorServiceImpl中的doInterceptions()
//我们看下代码 _ARouter # navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback)
//代码如下
if (!postcard.isGreenChannel()) { // It must be run in async thread, maybe interceptor cost too mush time made ANR.
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());
}
});
} else {
return _navigation(context, postcard, requestCode, callback);
}
InterceptorServiceImpl # doInterceptions()
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
// 首先判断我们是否有自定义的拦截器
if (null != Warehouse.interceptors && Warehouse.interceptors.size() > 0) {
// ------> 分析1:
checkInterceptorsInitStatus();
// 在init方法中 获取到拦截器并存储到集合中后,将初始化拦截器设置为true了
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 {
// ------> 分析2:
_excute(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);
}
}
分析1: checkInterceptorsInitStatus();
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() + "]");
}
}
}
}
//总结来说就是 如果拦截器没有初始化,就会等待10秒,如果超出10秒就会抛出异常
分析2: _excute(0, interceptorCounter, postcard);
private static void _excute(final int index, final CancelableCountDownLatch counter, final Postcard postcard) {
if (index < Warehouse.interceptors.size()) {
//拿到我们自定义的拦截器
IInterceptor iInterceptor = Warehouse.interceptors.get(index);
//执行自定义拦截器的 process()方法
iInterceptor.process(postcard, new InterceptorCallback() {
@Override
public void onContinue(Postcard postcard) {
// Last interceptor excute over with no exception.
counter.countDown();
_excute(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());
// }
}
});
}
}
// 依次取出拦截器实例,然后调用拦截器的process方法,传入回调接口 InterceptorCallBack
// 正常的话就会毁掉 onCointinue()方法 然后重新执行 _execute()方法只不过需要将index+1了
- 到这里我们拦截器的初始化到执行的流程就完事了,也就完成了拦截器的使命了
总结
- 为什么指定在注解中的 priority属性值 越小会被先执行呢?
// 其实这个跟我们数据仓库中 Warehouse # interceptorIndex有关系 它是一个红黑树结构的集合是按照顺序存储的,key是以priority,value是我们自定义拦截器的Class对象
// 当我们从interceptorIndex取出Class对象的时候,是按照 存储的优先级拿到的
网友评论