组件化被越来越多的Android项目采用,而作为组件化的基础——路由也是重中之重。本篇文章将详细的分析阿里巴巴开源的路由框架ARouter。从源码的角度解释为什么这样使用,以及避免做什么,让你使用地更加顺滑。
项目地址
项目结构
我们把项目clone到本地,打开ARouter项目,可以看到分为如下几个Module:
其中app、module-java、module-kotlin是演示demo相关的,也就是使用的示例。
arouter-register是1.3.0版本新添加的gradle插件的代码,用于路由表的自动注册,可以缩短初始化时间,解决应用加固导致无法直接访问dex文件初始化失败的问题。(本篇文章不涉及这个模块)
arouter-annotation包含了注解类以及携带数据的bean;
arouter-compiler包含了注解处理类,通过java的Annotation Processor Tool按照定义的Processor生成所需要的类。
以demo为例子,当运行项目后会在build文件下生成arouter相关的代码:
我们先不用去关心生成代码的细节,只需要知道按照用法的提示添加诸如Route等注解之后,arouter-compiler中的注解处理类会自动帮我们生成需要的代码(如上图所示)。对源码的分析也只需要知道生成的类的做了什么就够了。
本篇文章也不讨论生成代码细节,如果想了解apt的生成过程,可以参考:Java注解处理器 以及更方便生成的Java代码的库javapoet。
arouter-api提供了给我们使用的api,以实现路由功能。
那我们就从api开始分析。
源码分析
init
按照官方说明,我们找到Arouter的入口,也就是初始化的地方:
if (isDebug()) { // 这两行必须写在init之前,否则这些配置在init过程中将无效
ARouter.openLog(); // 打印日志
ARouter.openDebug(); // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
}
ARouter.init(mApplication); // 尽可能早,推荐在Application中初始化
我们直接进入ARouter.init方法:
/**
* Init, it must be call before used router.
*/
public static void init(Application application) {
if (!hasInit) { //确保只初始化一次
logger = _ARouter.logger;//日志类
_ARouter.logger.info(Consts.TAG, "ARouter init start.");
hasInit = _ARouter.init(application);
if (hasInit) {
_ARouter.afterInit();
}
_ARouter.logger.info(Consts.TAG, "ARouter init over.");
}
}
变量hasInit
用于保证初始化代码只执行一次;
logger是一个日志工具类;
注意_ARouter
类的下划线,Arouter
是对外暴露api的类,而_ARouter
是真正的实现类。为什么这样设计那?当然是为了解耦啦。具体点说有哪些好处呢?看看这个init方法,除了对实现类的调用,还有日志的打印,有没有装饰模式的感觉?可以增加一些额外的功能。其次,对比_ARouter
类与Arouter
类的方法,可以看到明显Arouter类的方法少。要知道Arouter
是对外暴露的,我们可以有选择暴露用户需要的方法,而把一些方法隐藏在内部。相比于用private修饰,这种方式灵活性更强。
接着我们进入实现类看下:
protected static synchronized boolean init(Application application) {
mContext = application;
LogisticsCenter.init(mContext, executor);
logger.info(Consts.TAG, "ARouter init success!");
hasInit = true;
return true;
}
明显有价值的是LogisticsCenter.init(mContext, executor);
,executor是一个线程池。我们接着来看下去除日志等无关代码后的LogisticsCenter.init方法:
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context;
executor = tpe;
...
Set<String> routerMap;//生成类的类名集合
// 如果是debug模式或者是新版本,从apt生成的包中加载类
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
if (!routerMap.isEmpty()) {//加入sp缓存
context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
}
PackageUtils.updateVersion(context); //更新版本
} else {//否则从缓存读取类名
routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
}
//判断类型,使用反射实例化对象,并调用方法
for (String className : routerMap) {
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
...
}
这里体现了debuggable模式的作用,如果没有开启debuggable,并且调试的时候肯定不会更改版本号,因此只会从缓存中读取类信息,所以新添加的路由不会加载到内存中。
ROUTE_ROOT_PAKCAGE
是一个常量:
public static final String ROUTE_ROOT_PAKCAGE = "com.alibaba.android.arouter.routes";
可以看到与上面demo生成类的截图的包名相同。而ClassUtils.getFileNameByPackageName
方法做的就是找到app的dex,然后遍历出其中的属于com.alibaba.android.arouter.routes
包下的所有类名,打包成集合返回。可以想象遍历整个dex查找指定类名的工作量有多大,因此才会有开头提到的1.3.0版本新增gradle插件来代替这个过程。
拿到所有生成类名的集合后,通过反射实例化对象并调用方法,将注解的一些元素添加到static集合中:
class Warehouse {
// Cache route and metas
static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
static Map<String, RouteMeta> routes = new HashMap<>();
// Cache provider
static Map<Class, IProvider> providers = new HashMap<>();
static Map<String, RouteMeta> providersIndex = new HashMap<>();
// Cache interceptor
static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");
static List<IInterceptor> interceptors = new ArrayList<>();
}
interceptor的map有些特别,是UniqueKeyTreeMap
,其实做的很简单,仅仅是在key(优先级)相同时,抛出指定的异常。因此,记住不要出现优先级相同的interceptor。
生成的类有统一的命名规则,方便区分,分别实现对应的接口:
public interface IRouteRoot {
void loadInto(Map<String, Class<? extends IRouteGroup>> routes);
}
public interface IInterceptorGroup {
void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptor);
}
public interface IProviderGroup {
void loadInto(Map<String, RouteMeta> providers);
}
public interface IRouteGroup {
void loadInto(Map<String, RouteMeta> atlas);
}
我们来看下demo生成的实现类都做了什么:
public class ARouter$Root$app implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
//key为分组名,即路径的第一段,value为分组中所有的映射关系
routes.put("service", ARouter$Group$service.class);
routes.put("test", ARouter$Group$test.class);
}
}
//将module中使用@Route注解的activity或Fragment添加到集合中,这里的方法会在之后调用
public class ARouter$Group$test implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/test/activity1", RouteMeta.build(RouteType.ACTIVITY, Test1Activity.class, "/test/activity1", "test", new java.util.HashMap<String, Integer>(){{put("pac", 9); put("ch", 5); put("fl", 6); put("obj", 10); put("name", 8); put("dou", 7); put("boy", 0); put("objList", 10); put("map", 10); put("age", 3); put("url", 8); put("height", 3); }}, -1, -2147483648));
atlas.put("/test/activity2", RouteMeta.build(RouteType.ACTIVITY, Test2Activity.class, "/test/activity2", "test", new java.util.HashMap<String, Integer>(){{put("key1", 8); }}, -1, -2147483648));
atlas.put("/test/activity3", RouteMeta.build(RouteType.ACTIVITY, Test3Activity.class, "/test/activity3", "test", new java.util.HashMap<String, Integer>(){{put("name", 8); put("boy", 0); put("age", 3); }}, -1, -2147483648));
atlas.put("/test/activity4", RouteMeta.build(RouteType.ACTIVITY, Test4Activity.class, "/test/activity4", "test", null, -1, -2147483648));
atlas.put("/test/fragment", RouteMeta.build(RouteType.FRAGMENT, BlankFragment.class, "/test/fragment", "test", null, -1, -2147483648));
atlas.put("/test/webview", RouteMeta.build(RouteType.ACTIVITY, TestWebview.class, "/test/webview", "test", null, -1, -2147483648));
}
}
IRouteRoot的实现将有@Route注解的module名添加到参数集合中,也就是groupsIndex。
这里会存在一个小陷阱,如果不同的module中存在相同的分组(即路径的第一段,如上面的“test”),则会在对应的module中生成不同的IRouteGroup的实现,然后在此处会执行分别执行
routes.put("test", ARouter$Group$moduleA.class);
,以及routes.put("test", ARouter$Group$moduleB.class);
,但是因为key相同,因此前一个会被覆盖,导致前一个定义的路由无法找到。具体可以看我提的issue,官方的建议是路径分组与模块名相同,并且不同模块不要使用相同的分组。
public class ARouter$Interceptors$app implements IInterceptorGroup {
@Override
public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) {
//key是优先级
interceptors.put(7, Test1Interceptor.class);
}
}
同样的IInterceptorGroup的实现将@Interceptor注解的类添加到参数集合中,也就是interceptorsIndex中。
public class ARouter$Providers$app implements IProviderGroup {
@Override
public void loadInto(Map<String, RouteMeta> providers) {
providers.put("com.alibaba.android.arouter.demo.testservice.HelloService", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/service/hello", "service", null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.facade.service.SerializationService", RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, "/service/json", "service", null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.demo.testservice.SingleService", RouteMeta.build(RouteType.PROVIDER, SingleService.class, "/service/single", "service", null, -1, -2147483648));
}
}
IProviderGroup的实现将继承自IProvider的类添加到参数集合中,也就是providersIndex中。
RouteMeta是一个数据bean,封装了被注解类的一些信息
public class RouteMeta {
private RouteType type; // Type of route
private Element rawType; // Raw type of route
private Class<?> destination; // Destination
private String path; // Path of route
private String group; // Group of route
private int priority = -1; // The smaller the number, the higher the priority
private int extra; // Extra data
private Map<String, Integer> paramsType; // Param type
public static RouteMeta build(RouteType type, Class<?> destination, String path, String group, Map<String, Integer> paramsType, int priority, int extra) {
return new RouteMeta(type, null, destination, path, group, paramsType, priority, extra);
}
...
}
其中paramsType是包含了所有注解了Autowired的属性的信息,key为属性名,value为属性类型,ARouter将可被intent传递的数据类型定义了对应的int类型: BOOLEAN,BYTE,SHORT,INT,LONG,CHAR,FLOAT,DOUBLE,STRING,PARCELABLE,OBJECT分别对应0,1,2,3...
RouteType是一个枚举,表示被注解类的路由类型:
public enum RouteType {
ACTIVITY(0, "android.app.Activity"),
SERVICE(1, "android.app.Service"),
PROVIDER(2, "com.alibaba.android.arouter.facade.template.IProvider"),
CONTENT_PROVIDER(-1, "android.app.ContentProvider"),
BOARDCAST(-1, ""),
METHOD(-1, ""),
FRAGMENT(-1, "android.app.Fragment"),
UNKNOWN(-1, "Unknown route type");
int id;
String className;
RouteType(int id, String className) {
this.id = id;
this.className = className;
}
...
}
可以看到ARouter虽然目前只支持Activity和Fragment,但是也预留了Service和Boardcast的类型,可能以后也会实现。
最后别忘了init中还有一个_ARouter.afterInit();
方法:
static void afterInit() {
// Trigger interceptor init, use byName.
interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
}
它是一个管理拦截器的服务,实现了IProvider接口,并且用自己实现的的路由方式获取实例。嗯,没毛病!
根据官方的说明,IProvider接口是用来暴露服务,并且在初始化的时候会被调用init(Context context)
方法。具体的服务有其实现提供,那我们就来看下它的实现做了些什么:
@Route(path = "/arouter/service/interceptor")
public class InterceptorServiceImpl implements InterceptorService {
private static boolean interceptorHasInit;
private static final Object interceptorInitLock = new Object();
@Override
public void init(final Context context) {
... //省略子线程以及同步处理,下面的操作实际是在子线程处理的
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() + "]");
}
}
...
}
}
还记的在上一步初始化的时候将所有注解了Interceptor的类的信息存入了Warehouse.interceptorsIndex,这里就将这些类实例化,并调用iInterceptor.init(context);
完成自定义的初始化内容,最后放入Warehouse.interceptors集合中。
总结来说,init过程就是把所有注解的信息加载内存中,并且完成所有拦截器的初始化。
navigation
完成初始化之后,就可以实现路由跳转了:
ARouter.getInstance().build("/test/activity").navigation();
那我们就从这里开始分析:
getInstance()
是获取ARouter类的单例方法,没什么好说的。
public Postcard build(String path) {
return _ARouter.getInstance().build(path);
}
build方法调动了实现类_ARouter的build方法,继续看:
protected Postcard build(String path) {
...//省略判空
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
return build(path, extractGroup(path));
}
protected Postcard build(String path, String group) {
if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
return new Postcard(path, group);
}
}
这里出现一个PathReplaceService,它是继承IProvider的接口,它是预留给用户实现路径动态变化功能,官方有详细说明:
public interface PathReplaceService extends IProvider {
String forString(String path);
Uri forUri(Uri uri);
}
需要注意的是,build(String path)
方法及build(String path, String group)
方法中都尝试使用PathReplaceService完成路径动态变化,因此,在变化前需要做好判断,否则出现变换两次的情况(例如拼接字符)。
extractGroup方法截取路径中的第一段作为分组名。
build方法最终返回一个Postcard对象。它也是一个数据bean,继承自RouteMeta,附加一些跳转信息,如参数之类:
public final class Postcard extends RouteMeta {
// Base
private Uri uri;
private Object tag; // A tag prepare for some thing wrong.
private Bundle mBundle; // Data to transform
private int flags = -1; // Flags of route
private int timeout = 300; // Navigation timeout, TimeUnit.Second
private IProvider provider; // It will be set value, if this postcard was provider.
private boolean greenChannel;
private SerializationService serializationService;
// Animation
private Bundle optionsCompat; // The transition animation of activity
private int enterAnim = -1;
private int exitAnim = -1;
...
}
最后调用该Postcard对象的navigation方法,层层调用,最终还是调用的_Arouter
的navigation方法,我们先来看下方法的前半部分:
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
try {
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
if (null != callback) {
callback.onLost(postcard);
} else { // 交给全局降级策略
DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
if (null != degradeService) {
degradeService.onLost(context, postcard);
}
}
return null;
}
if (null != callback) {
callback.onFound(postcard);
}
... //方法未结束,分到之后
}
大致可看出LogisticsCenter.completion(postcard);
肯定是试图找到跳转的目标,如果找不到则让callback回调onLost,或者交给全局降级策略处理。找到则回调callback的onFound方法。
那我们看看LogisticsCenter.completion(postcard);
是如何寻找的:
public synchronized static void completion(Postcard postcard) {
...
//从集合中找路径对应的RouteMeta
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) { // 内存中没有,可能是还没有加载过
Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup()); // 找到分组
if (null == groupMeta) {//分组也没有,那就是没有注册
throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
} else { // 加载具体分组的映射,并从groupsIndex集合中删除类信息
try {
IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
iGroupInstance.loadInto(Warehouse.routes);
Warehouse.groupsIndex.remove(postcard.getGroup());
} catch (Exception e) {
throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
}
completion(postcard); // 递归再次尝试加载
}
} else {
//拷贝数据
postcard.setDestination(routeMeta.getDestination());
postcard.setType(routeMeta.getType());
postcard.setPriority(routeMeta.getPriority());
postcard.setExtra(routeMeta.getExtra());
Uri rawUri = postcard.getUri();
if (null != rawUri) { // 如果是Uri跳转
Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);//分割路径中的参数
Map<String, Integer> paramsType = routeMeta.getParamsType();//获取Autowired注解的属性
if (MapUtils.isNotEmpty(paramsType)) { //将属性对应的参数值放入Bundle
for (Map.Entry<String, Integer> params : paramsType.entrySet()) {
setValue(postcard,
params.getValue(),
params.getKey(),
resultMap.get(params.getKey()));
}
// Bundle存入需要自动注入的属性信息
postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));
}
// 保存原始路径
postcard.withString(ARouter.RAW_URI, rawUri.toString());
}
switch (routeMeta.getType()) {
case PROVIDER: //如果目标类型是provider,实例化对象并放入postcard
Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
IProvider instance = Warehouse.providers.get(providerMeta);
if (null == instance) {
IProvider provider;
try {
provider = providerMeta.getConstructor().newInstance();
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不经过拦截器处理
break;
case FRAGMENT:
postcard.greenChannel(); // Fragment不仅过拦截器处理
default:
break;
}
}
}
可以看到主要功能是找到匹配的目标类,并将目标类的一些信息拷贝到postcard对象中。
我们继续看navigation方法的后半部分:
protected Object navigation(final Context context, final Postcard postcard, final int request
Code, final NavigationCallback callback) {
...
if (!postcard.isGreenChannel()) { //不是绿色通道,即经过拦截器处理
interceptorService.doInterceptions(postcard, new InterceptorCallback() {
@Override
public void onContinue(Postcard postcard) {
_navigation(context, postcard, requestCode, callback);
}
@Override
public void onInterrupt(Throwable exception) {
if (null != callback) {
callback.onInterrupt(postcard);
}
}
});
} else { //绿色通道
return _navigation(context, postcard, requestCode, callback);
}
return null;
}
_navigation
方法是最终处理,并且有返回值,如果路由目标是Fragment或者IProvider的话。拦截器的处理可能会耗时,因此会放到子线程处理,通过回调完成继续操作,但此时就无法返回目标类(Fragment或IProvider),也就解释了为什么上面的completion方法中设置了绿色通道。
我们先不考虑拦截过程,直接看_navigation
方法:
private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
//mContext是init传入的Application
final Context currentContext = null == context ? mContext : context;
switch (postcard.getType()) {
case ACTIVITY:
// Build intent
final Intent intent = new Intent(currentContext, postcard.getDestination());
intent.putExtras(postcard.getExtras());
// Set flags.
int flags = postcard.getFlags();
if (-1 != flags) {
intent.setFlags(flags);
} else if (!(currentContext instanceof Activity)) { // Non activity, need less one flag.
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
//拦截过程在子线程,因此该方法可能仍在子线程,需要切换到主线程
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
if (requestCode > 0) { // Need start for result
ActivityCompat.startActivityForResult((Activity) currentContext, intent, requestCode, postcard.getOptionsBundle());
} else {
ActivityCompat.startActivity(currentContext, intent, postcard.getOptionsBundle());
}
if ((-1 != postcard.getEnterAnim() && -1 != postcard.getExitAnim()) && currentContext instanceof Activity) { // Old version.
((Activity) currentContext).overridePendingTransition(postcard.getEnterAnim(), postcard.getExitAnim());
}
if (null != callback) { // Navigation over.
callback.onArrival(postcard);
}
}
});
break;
case PROVIDER:
//LogisticsCenter.completion方法中存入的
return postcard.getProvider();
case BOARDCAST:
case CONTENT_PROVIDER:
case FRAGMENT:
Class fragmentMeta = postcard.getDestination();
try {
Object instance = fragmentMeta.getConstructor().newInstance();
if (instance instanceof Fragment) {
((Fragment) instance).setArguments(postcard.getExtras());
} else if (instance instanceof android.support.v4.app.Fragment) {
((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras());
}
return instance;
} catch (Exception ex) {
logger.error(Consts.TAG, "Fetch fragment instance error, " + TextUtils.formatStackTrace(ex.getStackTrace()));
}
case METHOD:
case SERVICE:
default:
return null;
}
return null;
}
navigation可以传入Context或者不传,不传的话默认就用Application的context,每开启一个Activity就会新开一个task。建议普通界面跳转都传入Activity,避免栈的混乱。
至此整个路由跳转的过程大致完成,整个过程可以分为:封装Postcard -> 查找信息集合,实例化目标类 -> 返回实例或者跳转。
Interceptor
前面我们跳过了拦截过程,现在我们来分析下这个过程,拦截功能是通过ARouter提供的interceptorService实现的,并且之前我们在init章节分析它的初始化,接下来看看具体是如何拦截的:
interceptorService.doInterceptions(postcard, new InterceptorCallback() {
@Override
public void onContinue(Postcard postcard) {
_navigation(context, postcard, requestCode, callback);
}
@Override
public void onInterrupt(Throwable exception) {
if (null != callback) {
callback.onInterrupt(postcard);
}
}
});
可以看到调用了doInterceptions方法,在看这个方法之前,我们先要了解一个类:CountDownLatch。相关资料:CountDownLatch理解一:与join的区别
简单来说CountDownLatch可以阻塞一个线程,知道内部计数器为0时,才继续执行阻塞的线程,计数器的初始值通过构造传入,通过调用countDown()方法减少一个计数。
拦截器的方法中,其中就使用CancelableCountDownLatch类,它继承自CountDownLatch,并扩展了cancel方法,用于直接将计数归0,放开阻塞:
public void cancel() {
while (getCount() > 0) {
countDown();
}
}
我们回到拦截器的doInterceptions方法:
@Override
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
if (null != Warehouse.interceptors && Warehouse.interceptors.size() > 0) {
... //省略同步等待初始化
LogisticsCenter.executor.execute(new Runnable() {
@Override
public void run() {
CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
try {
_excute(0, interceptorCounter, postcard);
//阻塞线程直到超时,或者计数归0
interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
if (interceptorCounter.getCount() > 0) { //拦截超时
callback.onInterrupt(new HandlerException("The interceptor processing timed out."));
} else if (null != postcard.getTag()) { // 被拦截
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 _excute(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) {
// 如果放行,则计数减1,执行后一个拦截器
counter.countDown();
_excute(index + 1, counter, postcard);
}
@Override
public void onInterrupt(Throwable exception) {
// 拦截,将exception存入postcard的tag字段,计数归零
postcard.setTag(null == exception ? new HandlerException("No message.") : exception.getMessage());
counter.cancel();
});
}
}
首先会创建一个与拦截器数量相同的CancelableCountDownLatch初始计数值,每放行一个拦截器就countDown,并交给后一个拦截器,如果拦截则清0计数,并将拦截的Throwable存入postcard的tag字段,interceptorCounter.await();
阻塞直到计数归0或者阻塞超时(默认是300秒),最后通过interceptorCounter.getCount()
判断是否是超时,还是拦截或者放行。
可以看到拦截的过程都是在子线程中处理,包括Interceptor的process也是在子线程调用的,因此,如果想要在拦截过程中展示dialog等都需要切换到主线程。
inject
通过@Autowired注解的属性,通过调用ARouter.getInstance().inject(this);
可以实现自动注入。我们知道原生的Activity传递数据是通过Bundle携带的。因此,ARouter的数据传递肯定也是基于Bundle,并实现了自动赋值的功能。
同样的,我们从方法入口层层深入探究:
static void inject(Object thiz) {
AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation());
if (null != autowiredService) {
autowiredService.autowire(thiz);
}
}
AutowiredService与InterceptorService类似,都是Arouter自己实现的继承IProvider接口的服务。我们找到AutowiredService的实现类:
@Route(path = "/arouter/service/autowired")
public class AutowiredServiceImpl implements AutowiredService {
private LruCache<String, ISyringe> classCache;
private List<String> blackList;
@Override
public void init(Context context) {
classCache = new LruCache<>(66);
blackList = new ArrayList<>();
}
@Override
public void autowire(Object instance) {
String className = instance.getClass().getName();
try {
if (!blackList.contains(className)) {
ISyringe autowiredHelper = classCache.get(className);
if (null == autowiredHelper) { // No cache.
autowiredHelper = (ISyringe) Class.forName(instance.getClass().getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance();
}
autowiredHelper.inject(instance);
classCache.put(className, autowiredHelper);
}
} catch (Exception ex) {
blackList.add(className); // This instance need not autowired.
}
}
}
去掉缓存集合之后,其实很简单,核心内容就两句:
ISyringe autowiredHelper = (ISyringe) Class.forName(instance.getClass().getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance();
autowiredHelper.inject(instance);
ISyringe是一个接口:
public interface ISyringe {
void inject(Object target);
}
它的实现是apt生成的,每一个带有@Autowired注解的类都会生成一个对应的ISyringe的实现,如demo中的:
//生成类的类名 = 目标类名+ "$ARouter$Autowired"(固定后缀)
public class BlankFragment$ARouter$Autowired implements ISyringe {
private SerializationService serializationService;
@Override
public void inject(Object target) {
serializationService = ARouter.getInstance().navigation(SerializationService.class);
//强转成目标类
BlankFragment substitute = (BlankFragment)target;
//给每个注解了@Autowired的属性赋值
//这里的key为属性名或者注解中给定的name
substitute.name = substitute.getArguments().getString("name");
// 如果存在Bundle无法携带的类型,则会通过SerializationService序列化成json的String传递,SerializationService没有提供默认实现,需要用户自己实现
if (null != serializationService) {
substitute.obj = serializationService.parseObject(substitute.getArguments().getString("obj"), new com.alibaba.android.arouter.facade.model.TypeWrapper<TestObj>(){}.getType());
} else {
Log.e("ARouter::", "You want automatic inject the field 'obj' in class 'BlankFragment' , then you should implement 'SerializationService' to support object auto inject!");
}
if (null == substitute.obj) {
Log.e("ARouter::", "The field 'obj' is null, in class '" + BlankFragment.class.getName() + "!");
}
}
}
注意这里赋值的操作是直接调用“目标类对象.属性”的方式赋值,因此,private修饰的属性无法通过这种方式赋值,并且在赋值时会抛出异常,被AutowiredServiceImpl的autowire方法中的try-catch捕获,存入不需要注入的集合中,最终导致同一个类中的其他非private属性也无法注入。
其注入原理基本与ButterKnife类似。前面分析navigation的时候我们看到了将Uri参数存入Bundle的过程(或者由用户手动调用withXX存入bundle),此处则是将Bundle中的数据取出,并赋值给@Autowired注解的属性。