概述 Arouter: 阿里开源的一个用于帮助 Android App 进行组件化改造的框架,支持模块间的路由、通信、解耦。具体使用参考官方文档 Arouter 。典型应用:
从外部URL映射到内部页面,以及参数传递与解析
跨模块页面跳转,模块间解耦
拦截跳转过程,处理登陆、埋点等逻辑
跨模块API调用,通过控制反转来做组件解耦
初始化 ARouter 在使用前需要通过 ARouter.init(Application) 初始化:
1 2 3 4 5 6 7 8 9 public static void init (Application application) { if (!hasInit) { hasInit = _ARouter.init(application); if (hasInit) { _ARouter.afterInit(); } } }
这里都是调用的 _ARouter 类中的方法,说明 _ARouter 才是核心类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 protected static synchronized boolean init (Application application) { mContext = application; LogisticsCenter.init(mContext, executor); hasInit = true ; mHandler = new Handler(Looper.getMainLooper()); return true ; } public synchronized static void init (Context context, ThreadPoolExecutor tpe) throws HandlerException { mContext = context; executor = tpe; }
加载 routerMap 有两种方式:通过 arouter-auto-register gradle 插件注册和代码注册。
至于 afterInit 方法则是在初始化成功后初始化拦截器:
1 2 3 4 static void afterInit () { interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor" ).navigation(); }
arouter-auto-register插件注册 1 2 3 4 5 6 7 8 private static void loadRouterMap () { registerByPlugin = false ; }
举例看一下 registerXXX 方法的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 private static void registerRouteRoot (IRouteRoot routeRoot) { markRegisteredByPlugin(); if (routeRoot != null ) { routeRoot.loadInto(Warehouse.groupsIndex); } } private static void registerInterceptor (IInterceptorGroup interceptorGroup) { markRegisteredByPlugin(); if (interceptorGroup != null ) { interceptorGroup.loadInto(Warehouse.interceptorsIndex); } }
跟代码注册类似,都是将生成的类存入 Warehouse 中,只不过将这一步骤通过 gradle 插件自动化了而已。
代码注册 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 Set<String> routerMap; if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) { routerMap = ClassUtils.getFileNameByPackageName(mContext, "com.alibaba.android.arouter.routes" ); if (!routerMap.isEmpty()) { 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("com.alibaba.android.arouter.routes.ARouter$$Root" )) { ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex); } else if (className.startsWith("com.alibaba.android.arouter.routes.ARouter$$Interceptors" )) { ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex); } else if (className.startsWith("com.alibaba.android.arouter.routes.ARouter$$Providers" )) { ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex); } }
Warehouse 是仓库的意思,它存放了 ARouter 自动生成的类(RouteRoot, InterceptorGroup, ProviderGroup)的信息。初始化过程主要完成了对自动生成的路由相关类 RouteRoot、Interceptor、ProviderGroup 的加载,对它们通过反射构造后将信息加载进了 Warehouse 类中。
路由跳转 Postcard创建 先从 ARouter.build 方法开始:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public Postcard build (String path) { return _ARouter.getInstance().build(path); } protected Postcard build (String path) { PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class); if (null != pService) { path = pService.forString(path); } return build(path, extractGroup(path), true ); }
如果 pService 为空,即用户没有实现 PathReplaceService 接口,则通过 build(path, group, afterReplace) 方法获取 Postcard 对象,而 extractGroup 获取 group 的逻辑其实就是取 path 中第一个 /
中的内容作为默认 group:
1 2 3 4 5 6 7 8 9 10 11 protected Postcard build (String path, String group, Boolean afterReplace) { if (!afterReplace) { PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class); if (null != pService) { path = pService.forString(path); } } return new Postcard(path, group); }
在创建了 Postcard 对象后通过 withXXX 方法添加参数,其实就是将参数放入了 Postcard 中的 Bundle 属性中。
执行跳转 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 public Object navigation () { return navigation(null ); } public Object navigation (Context context) { return navigation(context, null ); } public Object navigation (Context context, NavigationCallback callback) { return ARouter.getInstance().navigation(context, this , -1 , callback); } public Object navigation (Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) { return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback); } protected Object navigation (final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) { PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class); if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) { return null ; } postcard.setContext(null == context ? mContext : context); LogisticsCenter.completion(postcard); if (null != callback) { callback.onFound(postcard); } if (!postcard.isGreenChannel()) { interceptorService.doInterceptions(postcard, new InterceptorCallback() { @Override public void onContinue (Postcard postcard) { _navigation(postcard, requestCode, callback); } @Override public void onInterrupt (Throwable exception) { if (null != callback) { callback.onInterrupt(postcard); } } }); } else { return _navigation(postcard, requestCode, callback); } return null ; }
在补全了 Postcard 以及处理了拦截器的逻辑后,即执行真正的跳转逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 private Object _navigation (final Postcard postcard, final int requestCode, final NavigationCallback callback) { final Context currentContext = postcard.getContext(); switch (postcard.getType()) { case ACTIVITY: break ; case PROVIDER: return postcard.getProvider(); case BOARDCAST: case CONTENT_PROVIDER: case FRAGMENT: Class<?> fragmentMeta = postcard.getDestination(); case METHOD: case SERVICE: default : return null ; } return null ; }
获取Service ARouter 除了可以通过 ARouter.getInstance().build().navigation()
这样的方式实现页面跳转之外,还可以通过 ARouter.getInstance().navigation(XXService.class)
这样的方式实现跨越组件的服务获取:
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 public <T> T navigation (Class<? extends T> service) { return _ARouter.getInstance().navigation(service); } protected <T> T navigation (Class<? extends T> service) { try { Postcard postcard = LogisticsCenter.buildProvider(service.getName()); if (null == postcard) { return null ; } postcard.setContext(mContext); LogisticsCenter.completion(postcard); return (T) postcard.getProvider(); } catch (NoRouteFoundException ex) { return null ; } } public static Postcard buildProvider (String serviceName) { RouteMeta meta = Warehouse.providersIndex.get(serviceName); if (null == meta) { return null ; } else { return new Postcard(meta.getPath(), meta.getGroup()); } }
上面通过 buildProvider 方法创建对应 service 的 Postcard 对象,然后补全 Postcard 信息。其中 buildProvider 方法就是通过 Warehouse 中已经初始化的 providersIndex 根据 serviceName 获取对应的 RouteMeta,之后根据 RouteMeta 的 path 和 group 创建对应的 Postcard 对象。
拦截器机制 拦截器执行的入口在 InterceptorService.doInterceptions 方法中:
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 public void doInterceptions (final Postcard postcard, final InterceptorCallback callback) { if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) { 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 ) { callback.onInterrupt(new HandlerException("The interceptor processing timed out." )); } else if (null != postcard.getTag()) { callback.onInterrupt((Throwable) postcard.getTag()); } else { callback.onContinue(postcard); } } catch (Exception e) { callback.onInterrupt(e); } } } } else { callback.onContinue(postcard); } }
上面通过创建一个拦截器大小的 CountDownLatch 对象,用其来保证所有拦截器执行完毕后才接着执行其它逻辑。看看 _execute 方法的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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) { counter.countDown(); _execute(index + 1 , counter, postcard); } @Override public void onInterrupt (Throwable exception) { postcard.setTag(null == exception ? new HandlerException("No message." ) : exception); counter.cancel(); } }); } }
注解处理 ARouter 通过注解处理器在编译期间,使用 JavaPoet 生成了一系列 Java 文件,如 RouteRoot、ProviderGroup、Provider、Interceptor 的子类等。看一下 Warehouse 类中的相关数据结构。
ARouter 会根据 @Route 注解中的 group 生成对应名称的类。所以如果在不同的子模块下声明了相同的 group 路由,那么会生成同一个名字的两个类,添加到 groupsIndex 中的只会是其中一个,那么另一个类中 @Route 注册的组件会找不到。
1 2 3 4 static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();static Map<String, RouteMeta> routes = new HashMap<>();
生成的类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class ARouter $$Group $$service implements IRouteGroup { @Override public void loadInto (Map<String, RouteMeta> atlas) { atlas.put("/service/json" , RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, "/service/json", "service", null , -1 , -2147483648 )); } } public class ARouter $$Group $$test implements IRouteGroup { @Override public void loadInto (Map<String, RouteMeta> atlas) { atlas.put("/app/myprovider" , RouteMeta.build(RouteType.PROVIDER, MyProvider.class, "/app/myprovider", "test", null , -1 , -2147483648 )); } } public class ARouter $$Root $$app implements IRouteRoot { @Override public void loadInto (Map<String, Class<? extends IRouteGroup>> routes) { routes.put("service" , ARouter$$Group$$service.class); routes.put("test" , ARouter$$Group$$test.class); } }
provider 1 2 3 4 5 static Map<Class, IProvider> providers = new HashMap<>();static Map<String, RouteMeta> providersIndex = new HashMap<>();
生成的类:
1 2 3 4 5 6 7 public class ARouter $$Providers $$app implements IProviderGroup { @Override public void loadInto (Map<String, RouteMeta> providers) { providers.put("com.hearing.arouter.MyProvider" , RouteMeta.build(RouteType.PROVIDER, MyProvider.class, "/app/myprovider", "test", null , -1 , -2147483648 )); providers.put("com.alibaba.android.arouter.facade.service.SerializationService" , RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, "/service/json", "service", null , -1 , -2147483648 )); } }
interceptor 1 2 3 4 static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]" );static List<IInterceptor> interceptors = new ArrayList<>();
生成的类:
1 2 3 4 5 6 public class ARouter $$Interceptors $$app implements IInterceptorGroup { @Override public void loadInto (Map<Integer, Class<? extends IInterceptor>> interceptors) { interceptors.put(1 , LoginInterceptor.class); } }
Autowired 1 2 3 4 5 6 7 8 9 10 11 12 public class LoginActivity $$ARouter $$Autowired implements ISyringe { private SerializationService serializationService; @Override public void inject (Object target) { serializationService = ARouter.getInstance().navigation(SerializationService.class); LoginActivity substitute = (LoginActivity)target; substitute.userName = substitute.getIntent().getExtras() == null ? substitute.userName : substitute.getIntent().getExtras().getString("userName" , substitute.userName); substitute.userPassword = substitute.getIntent().getExtras() == null ? substitute.userPassword : substitute.getIntent().getExtras().getString("userPassword" , substitute.userPassword); substitute.person = substitute.getIntent().getExtras() == null ? substitute.person : substitute.getIntent().getExtras().getString("person" , substitute.person); } }
总结 ARouter 的核心流程可以分成三个步骤:
编译期注解处理:通过注解处理器配合 JavaPoet 实现了在编译期间生成 IRouteRoot, IProviderGroup, IProvider, IInterceptor 等的子类,在这些类中会装载注解相关的信息;
初始化:通过 ARouter.init 方法,查找 com.alibaba.android.arouter.routes
包下的所有类,这些都是第一步中注解处理器生成的类。然后将这些类信息存入 Warehouse 中的对应容器里。
路由查找:通过 navigation 方法,从 Warehouse 中查找对应信息并执行相应逻辑。