首頁>技術>

前言

相信絕大多數公司專案都做了元件化。為了解耦,元件化勢必要解決元件間的通訊。其中阿里巴巴開源的Arouter很好的解決了元件間的通訊,一直受到開發者的青睞。今天,我們來一步步揭開它的神祕面紗。

首先下載原始碼,專案地址:

https://github.com/alibaba/ARouter

來講一下專案結構

原始碼

app:專案主工程,演示程式碼module-java:java演示程式碼module-kotlin:kotlin演示程式碼arouter-annotation:所有註解以及註解涉及到的類arouter-compiler:註解處理器,APTarouter-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<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、引數、優先順序、額外引數。

涉及到的知識點:

aptjavapoetauto-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

先整體感受一下整個流程

整體流程

根據官方說明,首先在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<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.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<String, Class<? extends IRouteGroup>> routes) { routes.put("app", ARouter$$Group$$app.class); }}

Interceptor

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

Provider

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,這是自定義外掛的入口。

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。

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<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,設定綠色通道。所謂綠色通道就是不被攔截器攔截。

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

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<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() + "]"); } } 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.CountDownLatch3.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> 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.外掛化是怎麼實現路由表的升級的。

  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • Flutter快速實現APP首頁操作按鈕,原始碼直接拿去用,Android,IOS