一个用于帮助 Android App 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦
技术准备
源码分析
我们直接看官方 demo,核心模块包括api,compiler,annotation, 首先先简单讲一下各模块的作用:
- Annoation: 定义注解和 Route 相关的基本信息
- Compiler: 主要是用来在编译期间处理注解Router/Interceptor/Autowire三个注解,在编译期间自动注册注解标注的类,成员变量等。
- Api: 用户调用的核心 api
annoation
Autowired 用于注解 field, 可实现路由件传递参数的自动注入,无需使用者在代码中去获取传递的参数,注意,注解对象的修饰符不能是 private。
Interceptor 用于注解拦截器,拦截器的概念请参考官方文档
Route 路由注解,可以是 Activity,也可以是 Service
RouteMeta 路由相关的数据,包括路由类型,路径,组,权重,参数等信息
compiler
RouteProcessor
路由注解器
关键方法 #parseRoutes#
,我们先看这个方法最后生成的文件到底是啥,弄清楚了才能更容易的理清代码。
public class ARouter$$Root$$app implements IRouteRoot { public ARouter$$Root$$app() { } public void loadInto(Map> routes) { routes.put("service", service.class); routes.put("test", test.class); }}复制代码
注意map 存入的key 分别是 service 和 test,valude 则对应为 ARouter$$Group$$service.class 和 ARouter$$Group$$test.class。一个组件有且只有一个 Root 类,不同的组件以后缀作为区分。
public class ARouter$$Group$$test implements IRouteGroup { public ARouter$$Group$$test() { } public void loadInto(Mapatlas) { atlas.put("/test/*", RouteMeta) }}复制代码
public class ARouter$$Group$$service implements IRouteGroup { public ARouter$$Group$$service() { } public void loadInto(Mapatlas) { atlas.put("/service/*", RouteMeta) }}复制代码
注意 Arouter$$Group$$test 中 map 的 key 必然是以 "/test" 为前缀,刚好与其后缀相同
public class ARouter$$Providers$$app implements IProviderGroup { public ARouter$$Providers$$app() { } public void loadInto(Mapproviders) { providers.put(className, RouteMeta) }}复制代码
public class ARouter$$Interceptors$$app implements IInterceptorGroup { public ARouter$$Interceptors$$app() { } public void loadInto(Map> interceptors) { interceptors.put(privority, Class); }}复制代码
小结一下:
- 在一个module中,自动生成的 IRouteRoot 类有且只有一个,后缀为 module 名
- 在一个module中,自动生成的 IRouteGroup 类可以有多个,且都会在 IRouteRoot 类中注册
- IProviderGroup 只有一个,后缀为 module 名
准确来说后缀并不是 module 名,实际上你可以在 gradle 中指定,后续在讲解 parseRoutes 方法时会讲到具体原因
接下来将讲解 RouteProcessor#parseRoutes
方法:
- 拿到 Route,根据 RouteType 构建 RouteMeta,并通过 #categoryies 方法构建映射关系存储到 groupMap(groupMap 的 key 为 routeName ,value 则是 RouteMeta 集合)
- 遍历 groupMap 创建 ARouter$$Group$$groupName 类, 同时将 groupName 与生成的 File 的全类名的映射关系存储到 rootName
- 创建 ARouter$$Providers$$moduleName 类
- 创建 ARouter$$Rout$$moudleName
其中 groupName 就是你在注解中设置的 group,但我们平时在使用 ARouter 的时候很少回去设置 group, 如果你没有指定 group,Arouter 会默认使用 path 的主路径,比如你指定了 path = "/test/main", 那 groupName 就是 test; moudleName 则由你在 gradle 中指定。
AutoWiredProccesor
首先看一下生成的文件:
public class Test1Activity$$ARouter$$Autowired implements ISyringe { private SerializationService serializationService; public Test1Activity$$ARouter$$Autowired() { } public void inject(Object target) { this.serializationService = (SerializationService)ARouter.getInstance().navigation(SerializationService.class); Test1Activity substitute = (Test1Activity)target; substitute.name = substitute.getIntent().getStringExtra("name"); ... substitute.helloService = (HelloService)ARouter.getInstance().navigation(HelloService.class); }}复制代码
生成的 class 继承之 ISyring
只有一个方法 inject(Object),该方法的实现主要做了两件事:
AutoWired
注解的 filed 如果是service
(Arouter 中继承自 IProvider), 则初始化对象:
substitute.helloService = (HelloService)ARouter.getInstance().navigation(HelloService.class);复制代码
AutoWried
注解的 filed 非service
,则从 bundle 中获取相应的 value 赋值给该 field:
substitute.helloService = (HelloService)ARouter.getInstance().navigation(HelloService.class); // activity substitute.name = substitute.getArguments().getString("name"); // fragment复制代码
- 最后生成类名$$Arouter$$AutoWired.class 文件
InterceptorProccessor
拦截注解生成器
public class ARouter$$Interceptors$$app implements IInterceptorGroup { public ARouter$$Interceptors$$app() { } public void loadInto(Map> interceptors) { interceptors.put(7, Test1Interceptor.class); }}复制代码
api
ARouter & _ARouter
launcher 下只有两个类 ARouter
, _ARouter
, ARouter
采用单例模式,对外提供 ARouter 相关 API,我们在调用ARouter.getInstance().build()
时 真正调用的是_ARouter
的相关方法,这里采用了代理模式,现在我们就开始以 ARouter 向外提供的方法来逐步分析:
首先看init()
: 在 Arouter 库中,有一个 Warehose
类,它存储了路由相关的信息,我们在使用 ARouter 之前必须要先调用 init 方法,而 init
方法就是需要将信息存储到 Warehose
中 以下为时序图:
通过调用#loadInto(Warehose)
方法,成功地将信息存储到 Warehose
。
现在Warehose
的 groupsIndex, providersIndex, interceptorsIndex 已经被我们存入了路由信息
最后再调用_ARouter#afterInit()
实例化 interceptorService
接下来是 navigation()
方法:
我们需要先了解Postcard
类,Postcard
继承 RouteMeta
,扩展了一些属性,例如:flag(activity 的 flag),uri,provider,anim(动画)。我们平时在是用 Arouter 时 build 方法返回的就是 Postcasrd
, postcard#navigation
方法,最终调用的是就是 _Arouter#navaigation
LogisticsCenter.completion(postcard);if(!postcard.isGreenChannel()) { // 拦截器相关,暂时跳过 interceptorSevice.doInterceptions(post, new InterceptorCallback() { @Override public void onContinue(Postcard postcard) { _navigation(context, postcard, requestCode, callback); } })} else { return _navigation(context, postcard, requestCode, callback);}复制代码
首先我们来看LogisticsCenter#completion(Postcard)
方法
...RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());if (null == routeMeta) { Class groupMeta = Warehouse.groupsIndex.get(postcard.getGroup()); // Load route meta. IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance(); // ARouter$$Group%%test 对象实例化 iGroupInstance.loadInto(Warehouse.routes); Warehouse.groupsIndex.remove(postcard.getGroup()); completion(postcard); // Reload 进入 else 分支} else { ... // postcadrd 设置属性如:destination,type,priority等 switch(routeMeta.getType) { case PROVIDER: Class providerMeta = (Class ) routeMeta.getDestination(); IProvider instance = Warehouse.providers.get(providerMeta); if(instance == null) { IProvider povider = providerMeta.getConstructor().newInstance(); provider.init(mContext); // init 方法只在创建 provider 对象后调用一次, Warehose.providers.put(providerMeta, provider); // 存入 map,除非调用 ARouter#destory, 该 provider 对象会一直存在 map 中 postcard.setProvider(instance); postcard.greenChannel(); } break; case FRAGMENT: ... break; }}复制代码
到这里面 Warehose
中的 routes
和providers
也被我们放入了相应信息
_navigation()
方法:
switch(postcard.getType()) { case ACTIVITY: ... // build intent startActivity(requestCode, currentCotext, intent, postcard, callback); break; case POSTCARD: return postcard.getProvider(); case Fragment: Fragment fragment = fragmentMeta.getConstructor().newInstance(); fragment.setArguments(postcard.getExtras()); return fragment; default: return null;}return null;复制代码
接下来分析 ARroute#inject(Object)方法:
AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation()); autowiredService.autowire(objec);复制代码
代码很简单,关键是 AutowiredService 的实现
@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(); // $$ARouter$$Autowired = $$ARouter$$Autowired } autowiredHelper.inject(instance); // Text1Activity$$ARouter$$Autowired.inject(this) 取出 bundle 中传递的参数 classCache.put(className, autowiredHelper); } } catch (Exception ex) { blackList.add(className); // This instance need not autowired. } }复制代码
到这里,所有的注解生成的 class 的作用及其实现方法的调用时机都已经介绍到了,相信读者应该对 ARouter 有了新的了解了吧。接下来讲一下我们平时比较少用到的 interceptor
ARouter
官方为我们提供了一个拦截器的使用场景:
比较经典的应用就是在跳转过程中处理登陆事件,这样就不需要在目标页重复做登陆检查复制代码
还记得在讲解 ARroute#navigation
时我们跳过了拦截器相关的内容吗
if(!postcard.isGreenChannel()) { // 拦截器相关,暂时跳过 interceptorSevice.doInterceptions(post, new InterceptorCallback() { @Override public void onContinue(Postcard postcard) { _navigation(context, postcard, requestCode, callback); } })}复制代码
只要是拦截器,一般都会使用责任链设计模式,在 ARouter
中也不例外,我们先看 interceptorService
的创建过程,它的 type 是 Provider,在创建后会首先调用 init
方法
@Overridepublic void init(final Context context) { LogisticsCenter.executor.execute(new Runnable() { @Override public void run() { for(entry : Warehose.interceptorsIndex) { Class interceptorClass = entry.getValue(); IInterceptor iInterceptor = interceptorClass.getConstructor().newInstance(); iInterceptor.init(context); Warehose.interceptors.add(iInterceptor); } } }}复制代码
interceptorService#doInterceptions 调用 _excute 方法
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) { // 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(); } }); }复制代码
常见错误分析
-
service 内存泄漏, 考虑以下代码:
public class HelloServiceImpl implements HelloService { private Context context; @Override public void init(Context context) { this.context = context } @Override public void say() { Toast.makeText(context, R.string.hello, Toast.LENGTH_LONG).show(); }}复制代码
之前我们分析源码的时候就已经讲过了,service 的实现类在初始化后会被放入
Warehose.providers
,后续通过调用ARouter.inject
都是从Warehose.providers
中取出。在上面的代码中如果 context 是一个 activity 对象,将该 activity 赋值给了成员变量 context,则该 activity 对象不会被回收,造成了内存泄漏 -
混淆导致 service 为 null
public class HelloHolder { @Autowired(name = "/service/hello") HelloService helloService; public HelloHolder() { ARouter.getInstance().inject(this); } public void sayHello() { helloSerice.say; }}复制代码
该代码本身并没有问题,问题出在在 android 开发中我们需要混淆代码,大家还记得之前讲的 AutowiredService 的实现吧,关键代码在这里,
if (null == autowiredHelper) { // No cache. autowiredHelper = (ISyringe) Class.forName(instance.getClass().getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance(); // $$ARouter$$Autowired = $$ARouter$$Autowired }复制代码
本来 ARouter 构建的类名应该是
HelloHolder$$ARouter$$Autowired
, 然后实例化该对象,调用该对象的#inject
方法,可是你现在将HelloHolder
混淆成了H
,导致创建对象失败,这就是你为什么在代码中调用了ARouter.getInstance().inject(this)
,但是 service 还是为 null 的原因在 android 开发中,Activity,Fragment 我们在混淆规则中是配置了 keep 等规则的,所以不会有问题。但是像上文中 HelloHolder 这种情况常常被我们所忽略,在开发中一定要注意,建议采用注解方式来保持类名不被混淆。