全網最全面的Arouter源碼解析

前言

相信絕大多數公司項目都做了組件化。為了解耦,組件化勢必要解決

組件間的通信。其中阿里巴巴開源的Arouter很好的解決了組件間的通信,一直受到開發者的青睞。今天,我們來一步步揭開它的神秘面紗。

首先下載源代碼,項目地址:

https://github.com/alibaba/ARouter

來講一下項目結構

全網最全面的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

public class ARouter$$Group$$app implements IRouteGroup {
 @Override
 public void loadInto(Map 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多次執行可以通過下面方法處理

@Override
 public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnvironment) {
 if (annotations != null && annotations.size() > 0) {
 
 }
 }

下面正式講解api

先整體感受一下整個流程

全網最全面的Arouter源碼解析

整體流程

根據官方說明,首先在Application中調用如下api

if(BuildConfig.DEBUG){
 ARouter.openLog();//打開日誌
 ARouter.openDebug();//打開路由調試
 }
 ARouter.init(this);

進入Arouter.init(this)

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

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是一個自定義的線程池(實現了一種拋出錯誤的方式)。

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 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.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);
 }
 }
 }
 } 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下的路由)

public class ARouter$$Root$$app implements IRouteRoot {
 @Override
 public void loadInto(Map> routes) {
 routes.put("app", ARouter$$Group$$app.class);
 }
}

Interceptor

public class ARouter$$Interceptors$$app implements IInterceptorGroup {
 @Override
 public void loadInto(Map> interceptors) {
 interceptors.put(9, TestInterceptor2.class);
 interceptors.put(10, TestInterceptor.class);
 }
}

Provider

public class ARouter$$Providers$$app implements IProviderGroup {
 @Override
 public void loadInto(Map 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,這是自定義插件的入口。

public class PluginLaunch implements Plugin {
 @Override
 public void apply(Project project) {
 def android = project.extensions.getByType(AppExtension)
 def transformImpl = new RegisterTransform(project)

 ArrayList 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。

inputs.each { TransformInput input ->
 input.jarInputs.each {
 if (ScanUtil.shouldProcessPreDexJar(src.absolutePath)) {
 ScanUtil.scanJar(src, dest)
 }
 FileUtils.copyFile(src, dest)
 }
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

input.directoryInputs.each { DirectoryInput directoryInput ->
 directoryInput.file.eachFileRecurse { File file ->
 if(file.isFile() && ScanUtil.shouldProcessClass(path)){
 ScanUtil.scanClass(file)
 }
 }
 }
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

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[] interfaces) {
 super.visit(version, access, name, signature, superName, interfaces)
 RegisterTransform.registerList.each { ext ->
 if (ext.interfaceName && interfaces != null) {
 interfaces.each { itName ->
 if (itName == ext.interfaceName) {
 ext.classList.add(name)
 }
 }
 }
 }
 }
 }

一看就明白,就是把所有實現了IRouteRoot、IInterceptorGroup、IProviderGroup接口的類存到集合中

接著看最後一步做了什麼

if (fileContainsInitClass) {
 registerList.each { ext ->
 if (ext.classList.isEmpty()) {
 Logger.e("No class implements found for interface:" + ext.interfaceName)
 } else {
 RegisterCodeGenerator.insertInitCodeTo(ext)
 }
 }
 }

關鍵代碼都在RegisterCodeGenerator這個類中,我只列關鍵代碼。

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點的設計特別巧妙,增強了代碼的可讀性。

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方法做了什麼。

 private static void register(String className) {
 if (!TextUtils.isEmpty(className)) {
 try {
 Class> clazz = Class.forName(className);
 Object obj = clazz.getConstructor().newInstance();
 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相對應的集合中。至此自動註冊工作完成。

路由跳轉

ARouter.getInstance().build("/home/test").withString("key3", "888")
 .withLong("key1", 666L)
 .navigation(this)

先看build,new一個Postcard對象並給Postcard設置path和group。Postcard構造方法中new了一個bundler對象。PathReplaceService提供了動態改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);
 }
 }

.withString("key3", "888").withLong("key1", 666L)把參數設置給當前Postcard的bundle中。

再看navigation方法

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)

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 resultMap = TextUtils.splitQueryParameters(rawUri);
 Map paramsType = routeMeta.getParamsType();

 if (MapUtils.isNotEmpty(paramsType)) {
 for (Map.Entry 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,設置綠色通道。所謂綠色通道就是不被攔截器攔截。

第二個部分是處理攔截。我們稍後再講
先看第三部分

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().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;
 }

看到這裡是不是很親切,這不就是我們平時常寫的startActivity(intent,class)嗎?如果是fragment的話反射調用Fragment構造方法返回fragment對象。provider也是返回 Provider對象。至此跳轉這一塊基本上都搞清楚了。

分析一下攔截器怎麼實現的

之前講了Aroute.init之後會將所有的攔截器實例化。我們看看_ARouter.afterInit()做了什麼

static void afterInit() {
 interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
 }

使用自己的路由方法初始化interceptorService服務,沒毛病。該服務的實現類是InterceptorServiceImpl,從前面的分析可以知道navigation會調用服務的init方法。看看init裡面做了什麼

@Override
 public void init(final Context context) {
 LogisticsCenter.executor.execute(new Runnable() {
 @Override
 public void run() {
 if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
 for (Map.Entry> 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() + "]");
 }
 }

 interceptorHasInit = true;
 }
 }
 });
 }

反射調用所有攔截器的構造函數實例化對象添加到Warehouse.interceptors並調用init方法,這裡使用了object.wait和object.notifyAll保證子線程中的所有攔截器實例化完成。攔截的時機在前面已經提到過了,我們來看看具體的代碼。

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);
 }
 }
 });
@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);
 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);
 }
 }
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()); // save the exception message for backup.
 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可以隨意 第一種比較簡單我麼不講,講一下第二種方式

@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的優先級高於全局降級處理。

if (null != callback) {
 callback.onLost(postcard);
 } else {
 DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
 if (null != degradeService) {
 degradeService.onLost(context, postcard);
 }
 }

關鍵代碼是下面一段代碼,詮釋了服務的navigation是如何運行的

protected  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的時候。

參數自動獲取

@Autowired
 @JvmField
 var key3: String? = null
 @Autowired
 @JvmField
 var key1: Long = 0L
 
 ARouter.getInstance().inject(this)

從文檔中可以知道,按照上面的方式就可以自動獲取各個參數。關鍵代碼肯定是在inject方法中,調用的還是服務。

static void inject(Object thiz) {
 AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation());
 if (null != autowiredService) {
 autowiredService.autowire(thiz);
 }
 }

看看AutowiredService的autowire方法

@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.forName(instance.getClass().getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance();
 }
 autowiredHelper.inject(instance);
 classCache.put(className, autowiredHelper);
 }
 } catch (Exception ex) {
 blackList.add(className);
 }
 }

最關鍵的方法是XXclass_$$ARouter$$Autowired.inject,其實這個類還是在註解生成類中

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.註解實現類的取名Group和path比較容易混淆。
  • 3.自動註冊路由表的plugin考慮做增量和併發編譯處理,效率有待商榷。
  • 4.插件化是怎麼實現路由表的升級的。

  • 分享到:


    相關文章: