前言
相信绝大多数公司的项目都做了组件化。为了解耦,组件化势必要解决组件间的通信 。其中阿里巴巴开源的Arouter很好的解决了组件间的通信,一直受到开发者的青睐。今天,我们来一步步揭开它的神秘面纱。
首先下载源代码,项目地址:
https://github.com/alibaba/ARouter
来讲一下项目结构:
app:项目主工程,演示代码
module-java:java演示代码
module-kotlin:kotlin演示代码
arouter-annotation:所有注解以及注解涉及到的类
arouter-compiler:注解处理器,APT
arouter-gradle-plugin:路由表自动注册插件
arouter-idea-plugin:路由导航插件,搜索ARouter Helper插件安装即可
arouter-api:所有的api,关键代码基本上在这边
第一步就是要生成注解类
@Route @Autowired Interceptor Provider都会生成如下面所示的对应注解类,java生成的注解类的位置在build-generated-sourse-apt中,kotlin生成的注解类的位置在build-generated-sourse-kapt
1 2 3 4 5 6 7 8 public class ARouter?Group?app implements IRouteGroup { @Override public void loadInto(Map<String, RouteMeta> atlas ) { atlas.put("/app/degrade1" , RouteMeta . build(RouteType.PROVIDER, DegradeServiceImpl .class , "/app/degrade1" , "app" , null, -1 , -2147483648 )); atlas.put("/app/main" , RouteMeta . build(RouteType.ACTIVITY, MainActivity .class , "/app/main" , "app" , null, -1 , -2147483648 )); atlas.put("/app/path" , RouteMeta . build(RouteType.PROVIDER, PathReplaceServiceImpl .class , "/app/path" , "app" , null, -1 , -2147483648 )); } }
这里需要重点关注一下RouteMeta这个类,这个类存储了目标对象的所有信息。包括路由类型、目标对象类、path、group、参数、优先级、额外参数。
涉及到的知识点:
1.注解,注解生成器apt
2.javapoet
3.auto-service
这里是我写的一个AptDemo,仅供参考:
https://github.com/liulingfeng/APT
关于AbstractProcessor的process多次执行 可以通过下面方法处理
1 2 3 4 5 6 7 @Override public boolean process (Set <? extends TypeElement> annotations, RoundEnvironment roundEnvironment ) { if (annotations != null && annotations.size() > 0 ) { } }
下面正式讲解api
先整体感受一下整个流程
根据官方说明,首先在Application中调用如下api
1 2 3 4 5 if (BuildConfig.DEBUG){ ARouter .open Log() ; ARouter .open Debug() ; }ARouter . init(this);
进入Arouter.init(this)
1 2 3 4 5 6 7 8 9 10 public static void init(Application application) { if (!hasInit) { logger = _ARouter . logger; hasInit = _ARouter . init(application); if (hasInit) { _ARouter . afterInit() ; } } }
hasInit保证只初始化一次,内部调用了_ARouter.init(application),Arouter是门面, _Arouter是具体实现,典型的门面模式。初始化之后调用 _ARouter.afterInit初始化拦截器(这个后面细讲)。继续跟进 _ARouter.init
1 2 3 4 5 6 7 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中,executor是一个自定义的线程池(实现了一种抛出错误的方式)。
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 public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException { try { if (registerByPlugin) { logger.info(TAG, "Load router map by arouter-auto-register plugin." ); } else { Set<String> routerMap; if (ARouter . debuggable() || PackageUtils . isNewVersion(context ) ) { routerMap = ClassUtils . getFileNameByPackageName(mContext , ROUTE_ROOT_PAKCAGE) ; if (!routerMap.isEmpty() ) { context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE) .edit() .putStringSet(AROUTER_SP_KEY_MAP, routerMap ) .apply() ; } PackageUtils . updateVersion(context ) ; } else { for (String className : routerMap) { if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT) ) { ((IRouteRoot) (Class .for Name(className ) .getConstructor() .new Instance() )).loadInto(Warehouse.groupsIndex ) ; } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS) ) { ((IInterceptorGroup) (Class .for Name(className ) .getConstructor() .new Instance() )).loadInto(Warehouse.interceptorsIndex ) ; } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS) ) { ((IProviderGroup) (Class .for Name(className ) .getConstructor() .new Instance() )).loadInto(Warehouse.providersIndex ) ; } } } } catch (Exception e) { throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e .getMessage () + "]" ); } }
代码比较长,我把它分解一下
1.判断是不是用插件自动注册路由表,插件注册的方式另说
2.从dex中加载指定路径(com.alibaba.android.arouter.routes)下的所有类的名字,其实就是注解生成类,然后根据版本号升级版本。非debuggable环境下从SharedPreferences缓存中读取(做的一个优化点)
3.反射调用loadInto把Group、Interceptor、Provider的映射关系添加到集合中
看一下各种类型的注解生成类 Root(这里做了优化先加载各个group ,用到的时候再加载各个group下的路由)
1 2 3 4 5 6 public class ARouter ?Root ?app implements IRouteRoot { @Override public void loadInto (Map <String , Class<? extends IRouteGroup>> routes ) { routes.put("app" , ARouter?Group?app.class); } }
Interceptor
1 2 3 4 5 6 7 public class ARouter ?Interceptors ?app implements IInterceptorGroup { @Override public void loadInto(Map<Integer, Class<? extends IInterceptor>> int erceptors) { int erceptors.put(9 , TestInterceptor2.class); int erceptors.put(10 , TestInterceptor.class); } }
Provider
1 2 3 4 5 6 public class ARouter ?Providers ?app implements IProviderGroup { @Override public void loadInto (Map <String , RouteMeta> providers ) { providers.put("com.xls.HelloService" , RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/yourservicegroupname/hello" , "yourservicegroupname" , null , -1 , -2147483648 )); } }
init工作总结及知识点
1.把Group、Interceptor、Provider注解类的映射添加到Warehouse.groupsIndex、Warehouse.interceptorsIndex、Warehouse.providersIndex集合中
2.实例化所有的Interceptor添加到Warehouse.interceptors中
3.dex分析-多dex怎么查找-热修复的根本原理是什么
4.线程池-线程池各个参数-线程池抛出错误的方法-如何保证线程池线程名字唯一性-原子类
顺便补充一下插件自动注册路由表
首先目光移到PluginLaunch,这是自定义插件的入口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class PluginLaunch implements Plugin<Project > { @Override public void apply(Project project ) { def android = project .extensions.getByType(AppExtension) def transformImpl = new RegisterTransform(project ) ArrayList<ScanSetting> list = new ArrayList<>(3 ) list.add(new ScanSetting('IRouteRoot' )) list.add(new ScanSetting('IInterceptorGroup' )) list.add(new ScanSetting('IProviderGroup' )) RegisterTransform.registerList = list android.registerTransform(transformImpl) } } }
这里完成了自定义Transform的注册以及添加需要过滤的接口到ScanSetting,最主要的代码自然是在RegisterTransform中。直奔RegisterTransform的transform方法,首先遍历jar。
1 2 3 4 5 6 7 8 inputs.each { TransformInput input -> input.jarInputs.each { if (ScanUtil . shouldProcessPreDexJar(src .absolutePath ) ) { ScanUtil . scanJar(src , dest ) } FileUtils . copyFile(src , dest ) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 static void scanJar(File jarFile , File destFile ) { if (jarFile) { def file = new JarFile(jarFile ) Enumeration enumeration = file.entries() while (enumeration.hasMoreElements() ) { JarEntry jarEntry = (JarEntry) enumeration.nextElement() String entryName = jarEntry.getName() if (entryName.startsWith("com/alibaba/android/arouter/routes/" ) ) { InputStream inputStream = file.getInputStream(jarEntry ) scanClass(inputStream ) inputStream.close() } else if ("com/alibaba/android/arouter/core/LogisticsCenter.class" == entryName) { RegisterTransform . fileContainsInitClass = destFile } } file.close() } }
做到两步工作:1.把com/alibaba/android/arouter/routes包名下的交给scanClass处理(这个稍后会分析到) 2.找到LogisticsCenter.class类,对于这个类想必很熟悉吧。
接下来遍历directory
1 2 3 4 5 6 7 input.directoryInputs.each { DirectoryInput directoryInput -> directoryInput.file.eachFileRecurse { File file -> if (file.isFile() && ScanUtil . shouldProcessClass(path ) ){ ScanUtil . scanClass(file ) } } }
1 2 3 4 5 6 7 static void scanClass(InputStream inputStream ) { ClassReader cr = new ClassReader(inputStream ) ClassWriter cw = new ClassWriter(cr , 0) ScanClassVisitor cv = new ScanClassVisitor(Opcodes.ASM5, cw ) cr.accept(cv, ClassReader.EXPAND_FRAMES) inputStream.close() }
把文件流丢给ScanClassVisitor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 static class ScanClassVisitor extends ClassVisitor { ScanClassVisitor(int api, ClassVisitor cv) { super (api, cv) } void visit(int version, int access, String name, String signature, String superName, String[] int erfaces) { super .visit(version, access, name, signature, superName, int erfaces) RegisterTransform.registerList.each { ext -> if (ext.int erfaceName && int erfaces != null ) { int erfaces.each { itName -> if (itName == ext.int erfaceName) { ext.classList.add(name) } } } } } }
一看就明白,就是把所有实现了IRouteRoot、IInterceptorGroup、IProviderGroup接口的类存到集合中 。
接着看最后一步做了什么
1 2 3 4 5 6 7 8 9 if (fileContainsInitClass) { registerList.each { ext -> if (ext.classList.isEmpty() ) { Logger . e("No class implements found for interface:" + ext.interfaceName) } else { RegisterCodeGenerator . insertInitCodeTo(ext ) } } }
关键代码都在RegisterCodeGenerator这个类中,我只列关键代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 private byte[] referHackWhenInit(InputStream inputStream) { ClassReader cr = new ClassReader (inputStream) ClassWriter cw = new ClassWriter (cr, 0 ) ClassVisitor cv = new MyClassVisitor (Opcodes.ASM5, cw) cr.accept(cv, ClassReader.EXPAND_FRAMES) return cw.toByteArray() } MethodVisitor visitMethod(int access, String name, String desc, String signature, String [] exceptions) { MethodVisitor mv = super .visitMethod(access, name, desc, signature, exceptions) if (name == "loadRouterMap" ) { mv = new RouteMethodVisitor (Opcodes.ASM5, mv) } return mv }
找到hook点loadRouterMap。hook点的设计特别巧妙,增强了代码的可读性。
1 2 3 4 5 6 7 8 9 10 11 12 void visitInsn(int opcode) { if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN )) { extension .classList.each { name -> mv.visitMethodInsn(Opcodes.INVOKESTATIC , "com/alibaba/android/arouter/core/LogisticsCenter" , "register" , "(Ljava/lang/String;)V" , false ) } } super.visitInsn(opcode) }
调用LogisticsCenter的register方法,我们来看一下register方法做了什么。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 private static void register(String className) { if (!TextUtils . isEmpty(className ) ) { try { Class<?> clazz = Class .for Name(className ) ; Object obj = clazz.getConstructor() .new Instance() ; if (obj instanceof IRouteRoot) { registerRouteRoot((IRouteRoot) obj); } else if (obj instanceof IProviderGroup) { registerProvider((IProviderGroup) obj); } else if (obj instanceof IInterceptorGroup) { registerInterceptor((IInterceptorGroup) obj); } else { logger.info(TAG, "register failed, class name: " + className + " should implements one of IRouteRoot/IProviderGroup/IInterceptorGroup." ); } } catch (Exception e) { logger.error(TAG,"register class error:" + className); } } }
所有实现了IRouteRoot、IInterceptorGroup、IProviderGroup接口的类都加入了Warehouse相对应的集合中。至此自动注册工作完成。
插件注册涉及知识点
路由跳转
1 2 3 ARouter . getInstance() .build("/home/test" ).with String("key3" , "888" ) .with Long("key1" , 666L) .navigation(this)
先看build,new一个Postcard对象并给Postcard设置path和group。Postcard构造方法中new了一个bundler对象。PathReplaceService提供了动态改path的方式,后面细讲。
1 2 3 4 5 6 7 8 9 10 11 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.for String(path ) ; } return new Postcard(path , group ) ; } }
.withString(“key3”, “888”).withLong(“key1”, 666L)把参数设置给当前Postcard的bundle中。
再看navigation方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) { try { LogisticsCenter.completion(postcard); } catch (NoRouteFoundException ex) { if (debuggable()) { Toast.makeText(mContext, "There's no route matched!\n" + " Path = [" + postcard.getPath() + "]\n" + " Group = [" + postcard.getGroup() + "]" , Toast.LENGTH_LONG).show (); } if (null != callback) { callback.onLost(postcard); } else { DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class ); if (null != degradeService) { degradeService.onLost(context, postcard); } } return null ; } return null ; }
先看第一部分,重点落在LogisticsCenter.completion(postcard)。内部主要做的是实例化当前group下的具体Route添加到Warehouse.routes,如果没找到就降级处理,两种方式(1.设置NavigationCallback 2.实现DegradeService)
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 public synchronized static void completion(Postcard postcard) { 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 { 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) { Map <String , String > resultMap = TextUtils.splitQueryParameters(rawUri); Map <String , Integer> paramsType = routeMeta.getParamsType(); if (MapUtils.isNotEmpty(paramsType)) { for (Map .Entry<String , Integer> params : paramsType.entrySet()) { setValue(postcard, params.getValue(), params.getKey(), resultMap.get (params.getKey())); } postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String []{})); } postcard.withString(ARouter.RAW_URI, rawUri.toString()); } switch (routeMeta.getType()) { case PROVIDER: 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(); break ; case FRAGMENT: postcard.greenChannel(); default : break ; } } }
分析一下这段代码
1.判断Warehouse的routes中对应path的RouteMeta是否为空,看过注解生成类其实我们知道RouteMeta保存了类的具体信息
2.在集合中找到对应的group分组,然后实例化对应分组下的具体Route添加到集合中
3.把RouteMeta的各种信息设置给当前postcard对象
4.uri跳转的处理,uri跳转和普通跳转唯一的区别就是参数的剥离,普通跳转是直接设置的而uri是通过在链接中剥离的,其中参数的数据类型是在Routemeta的paramsType中设置的
5.根据跳转的类型不同做不同处理。如果是服务,直接实例化当前服务调用init方法并设置给postcard。设置绿色通道;如果是fragment,设置绿色通道。所谓绿色通道就是不被拦截器拦截。
第二个部分是处理拦截。我们稍后再讲 先看第三部分
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 private Object _navigation(final Context context , final Postcard postcard , final int requestCode , final NavigationCallback callback ) { final Context currentContext = null == context ? mContext : context; switch (postcard.getType() ) { case ACTIVITY: final Intent intent = new Intent(currentContext , postcard .getDestination () ); intent.putExtras(postcard .getExtras () ); int flags = postcard.getFlags() ; if (-1 != flags) { intent.setFlags(flags ) ; } else if (!(currentContext instanceof Activity)) { intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) ; } String action = postcard.getAction() ; if (!TextUtils . isEmpty(action ) ) { intent.setAction(action ) ; } runInMainThread(new Runnable() { @Override public void run() { startActivity(requestCode , currentContext , intent , postcard , callback ) ; } }); break; case PROVIDER: return postcard.getProvider() ; case BOARDCAST: case CONTENT_PROVIDER: case FRAGMENT: Class fragmentMeta = postcard.getDestination() ; try { Object instance = fragmentMeta.getConstructor() .new Instance() ; 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; }
看到这里是不是很亲切,这不就是我们平时常写的startActivity(intent,class)吗?如果是fragment的话反射调用Fragment构造方法返回fragment对象。provider也是返回 Provider对象。至此跳转这一块基本上都搞清楚了。
分析一下拦截器是怎么实现的
之前讲了Aroute.init之后会将所有的拦截器实例化。我们看看_ARouter.afterInit()做了什么
1 2 3 static void afterInit ( ) { interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor" ).navigation(); }
使用自己的路由方法初始化interceptorService服务,没毛病。该服务的实现类是InterceptorServiceImpl,从前面的分析可以知道navigation会调用服务的init方法。看看init里面做了什么
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @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() .new Instance() ; 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 ; } } }); }
反射调用所有拦截器的构造函数实例化对象添加到Warehouse.interceptors并调用init方法,这里使用了object.wait和object.notifyAll保证子线程中的所有拦截器实例化完成。拦截的时机在前面已经提到过了,我们来看看具体的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 if (!postcard.isGreenChannel() ) { interceptorService.do Interceptions(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 ) ; } } });
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 @Override public void do Interceptions(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 ) ; 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 () .to String() )); } else { callback.onContinue(postcard ) ; } } catch (Exception e) { callback.onInterrupt(e ) ; } } }); } else { callback.onContinue(postcard ) ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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 ) { counter.countDown() ; _excute(index + 1, counter , postcard ) ; } @Override public void onInterrupt(Throwable exception ) { postcard.setTag(null == exception ? new HandlerException("No message." ) : exception .getMessage() ); counter.cancel() ; } }); } }
使用CountDownLatch.await使得代码阻塞直到所有拦截器执行完成或者超时。拦截器process方法中需要调用callback.onContinue才能调用到counter.countDown()移交到下一个拦截器,这就解释了自定义的拦截器为什么一定要调用counter.countDown()
涉及知识点
1.线程间通信
2.CountDownLatch
3.Object.wait/Object.notify
降级处理
两种方式:1.navigation的时候添加NavigationCallback回调 2.写一个类实现DegradeService别忘了添加@Route path可以随意 第一种比较简单我么不讲,讲一下第二种方式
1 2 3 4 5 6 7 8 9 @Route(path = "/app/degrade1" ) class DegradeServiceImpl : DegradeService { override fun onLost (context: Context ?, postcard: Postcard ?) { Log.e("降级处理" ,"自定义降级处理" ) } override fun init (context: Context ?) { } }
生成的注解类在ARouter?Providers?app中,也是init的时候就把映射关系添加到集合中。调用的地方是在navigation中,这段代码也间接的说明了NavigationCallback的优先级高于全局降级处理。
1 2 3 4 5 6 7 8 if (null != callback) { callback.onLost(postcard ) ; } else { DegradeService degradeService = ARouter . getInstance() .navigation(DegradeService .class ); if (null != degradeService) { degradeService.onLost(context , postcard ) ; } }
关键代码是下面一段代码,诠释了服务的navigation是如何运行的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 protected <T> T navigation(Class<? extends T> service) { try { Postcard postcard = LogisticsCenter . buildProvider(service .getName () ); if (null == postcard) { postcard = LogisticsCenter . buildProvider(service .getSimpleName () ); } LogisticsCenter . completion(postcard); return (T) postcard.getProvider() ; } catch (NoRouteFoundException ex) { logger.warning(Consts.TAG, ex.getMessage() ); return null; } }
buildProvider是根据service的名字从集合中找到对应的RouteMeta并把path和group设置给postcard,接下来也是给postcard设置其他各种参数,和上面分析的大同小异。
path动态改变
调用的方式和降级处理一模一样,时机是在build的时候。
参数自动获取(uri方式跳转参数必须加Autowired标记)
1 2 3 4 5 6 7 8 @Autowired @JvmField var key3 : String? = null@Autowired @JvmField var key1 : Long = 0 L ARouter.getInstance().inject(this)
从文档中可以知道,按照上面的方式就可以自动获取各个参数。关键代码肯定是在inject方法中,调用的还是服务。
1 2 3 4 5 6 static void inject (Object thiz ) { AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired" ).navigation()); if (null != autowiredService) { autowiredService.autowire(thiz); } }
看看AutowiredService的autowire方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Override public void autowire(Object instance) { String className = instance.getClass() .getName() ; try { if (!blackList.contains(className)) { ISyringe autowiredHelper = classCache.get(className); if (null == autowiredHelper) { autowiredHelper = (ISyringe) Class .for Name(instance .getClass () .getName() + SUFFIX_AUTOWIRED).getConstructor() .new Instance() ; } autowiredHelper.inject(instance); classCache.put(className, autowiredHelper); } } catch (Exception ex) { blackList.add(className); } }
最关键的方法是XXclass_?ARouter?Autowired.inject,其实这个类还是在注解生成类中
1 2 3 4 5 6 7 8 9 10 11 public class TestOneActivity?ARouter?Autowired implements ISyringe { private SerializationService serializationService; @Override public void inject(Object target) { serializationService = ARouter . getInstance() .navigation(SerializationService .class ); TestOneActivity substitute = (TestOneActivity)target; substitute.key3 = substitute.getIntent() .getStringExtra("girl" ) ; substitute.key1 = substitute.getIntent() .getLongExtra("key1" , substitute .key1 ) ; } }
还是通过getIntent().getExtra方法获取的参数,然后把获取的参数设置给当前类。
分析完源码之后扪心自问一下下面问题是否能回答上来
1.openLog和openDebug为什么要在init之前?
2.非Debug环境如何升级路由表——即添加路由?
3.为什么要自定义线程池?线程池抛出错误的方式有哪几种?
4.activity的跳转是怎么实现的?
5.fragment实例是怎么拿到的?为什么不允许拦截?
6.服务是如何调用的?
7.path能动态修改吗?在哪个时机修改的?
8.uri方式是如何跳转的?
9.路由跳转能否在子线程中?
10.拦截器怎么实现的?初始化的时机?为什么要在process调用callback.onContinue()。各个拦截器之间的优先级是如何保证的(是在跳转的时候根据priority判断的吗)
11.全局降级处理怎么实现的,和NavigationCallback谁优先级更高?
12.如何对path进行预处理,让所有路由失效?
13.实现多个类继承PathReplaceService、PretreatmentService实际会用哪个。
个人的一些思考,大家可以讨论一下
1.Fragment未做onActivityResult回调支持,对Fragment的场景还是偏简单了。
2.插件化是怎么实现路由表的升级的。
3.自动注册路由表的plugin考虑做增量和并发编译处理,效率有待商榷。
4.注解实现类的取名Group和path比较容易混淆。
5.组件跳转结果无法拿到(造成url跳转发起方——比如前端,没有callback,无状态)