ARouter原始碼分析(3)-跨模組載入實現類與引數的自動注入
文章來源自作者的Android進階計劃(ofollow,noindex">https://github.com/SusionSuc/AdvancedAndroid )
其實在第一篇文章的時候已經介紹過ARouter
中的InterceptorService
、GlobalDegradeService
也是框架在執行時期動態載入的。即使這兩個類位於其它的模組。
所以ARouter
也是具有WMRouter
的ServiceLoader
相似的功能的。本文就來看一下這個功能。
攔截器Service的載入
先來看一下ARouter
的攔截器的實現類:
@Route(path = "/arouter/service/interceptor") public class InterceptorServiceImpl implements InterceptorService {}
即也使用了@Route
註解標記,按照我們前面文章的分析,這個類也會在執行的時候被動態的載入到路由表Warehouse.routes
和Warehouse.groupIndex
中。
我們看一下ARouter
是如何在程式碼中獲取這個類的實現類的:
static void afterInit() { //這個程式碼在ARouter初始化完畢後呼叫 interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation(); }
這裡可能就有疑問了,為什麼ARouter.getInstance().build("/arouter/service/interceptor").navigation()
可以得到一個物件?
我們已經知道@Route
標記的類在RouteProcessor
處理時會生成對應的RouteMeta
。其實RouterMeta是有型別的,我們來看一下註解掃描生成RouteMeta
那一段程式碼:
TypeMirror tm = element.asType(); //tm 即註解標註的那個類 if (types.isSubtype(tm, type_Activity)) {// Activity routeMeta = new RouteMeta(route, element, RouteType.ACTIVITY, paramsType); routeMeta.setInjectConfig(injectConfig); } else if (types.isSubtype(tm, iProvider)) {// IProvider routeMeta = new RouteMeta(route, element, RouteType.PROVIDER, null); } else if (types.isSubtype(tm, type_Service)) { // Service routeMeta = new RouteMeta(route, element, RouteType.parse(SERVICE), null); }
即如果被註解標註的類是個Activity
,那麼RouteMeta
的型別就是RouteType.ACTIVITY
。如果是IProvider
,那麼對應的型別就是RouteType.PROVIDER
。
其實InterceptorServiceImpl
就是IProvider
型別:
public interface InterceptorService extends IProvider { interceptions(Postcard postcard, InterceptorCallback callback); }
我們前面只看了ARouter
只在路由過程中對RouteType.ACTIVITY
的處理,這裡我們看一下對RouteType.PROVIDER
的處理:
//_ARouter.java private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) { switch (postcard.getType()) { case ACTIVITY: //對activity進行跳轉 break; case PROVIDER: return postcard.getProvider(); .... default: return null; } return null; }
即對於RouteType.PROVIDER
直接返回了postcard.getProvider()
。 好我們來看一下postcard.getProvider()
是什麼? 我們來看一下LogisticsCenter
根據RouteMeta
組裝Postcard
的那一段程式碼:
switch (routeMeta.getType()) { case PROVIDER:// if the route is provider, should find its instance // Its provider, so it must implement IProvider Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination(); IProvider instance = Warehouse.providers.get(providerMeta); //..如果 instance 為空的話則例項化 `providerMeta`對應的類,然後放入到`Warehouse.providers` postcard.setProvider(instance); postcard.greenChannel();// Provider should skip all of interceptors break; case FRAGMENT: postcard.greenChannel();// Fragment needn't interceptors default: break; }
其實這裡就可以猜測,對於@Route
標記的IProvider
介面,也會建立一個Warehouse.providers
,用來儲存這些例項。
所以postcard.getProvider()
就是一個InterceptorServiceImpl
物件。綜上:
static void afterInit() { //這個程式碼在ARouter初始化完畢後呼叫 interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation(); }
interceptorService
就是InterceptorServiceImpl
物件。
好到這裡我們知道了InterceptorService
是如何載入的了。Interceptor
是怎載入的呢?
這裡就不去仔細追究原始碼了,直接解釋一下大致原理。
-
在ARoute中,攔截器可以使用
@Interceptor
來標註 -
InterceptorProcessor
會動態生成一個註冊檔案,類似於Activity的路由節點註冊的java類。 -
在ARouter初始化的時候,會註冊到
Warehouse.interceptors
-
InterceptorService
在初始化的時候,會例項化Warehouse.interceptors
表中記錄的類
我們只看一下InterceptorServiceImpl
的初始化方法,來證明上面所說的 :
@Override public void init(final Context context) { if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {//這段程式碼是在非同步執行緒執行的 for (Map.Entry<Integer, Class<? extends IInterceptor>> entry : Warehouse.interceptorsIndex.entrySet()) { Class<? extends IInterceptor> interceptorClass = entry.getValue(); IInterceptor iInterceptor = interceptorClass.getConstructor().newInstance(); iInterceptor.init(context); Warehouse.interceptors.add(iInterceptor); } ...... } }
IProvider 的妙用
看了上面這個功能,我們是不是可以利用它來載入一個其他庫的實現類,比如一個UI的實現類? 答案是可以的,比如我們在一個庫中定義了這個類:
@Route(path = "/custom/ui1") public class MyCustomUi implements IProvider {//必須實現 IProvider 介面 public MyCustomUi() { }//要提供預設構造方法 @Override public void init(Context context) {} }
我們在主工程中,這樣就可以獲得它的例項:
IProvider ui1 = (IProvider) ARouter.getInstance().build("/custom/ui1").navigation();
這個功能是不是和WMRouter
的ServiceLoader
有異曲同工之妙?
@Autowired 引數的自動注入
ARouter
是支援在Activity的屬性上標記@Autowired
註解,然後在跳轉的時候自動將引數賦值到這些屬性上。比如:
ARouter.getInstance() .build("/test/activity1") .withInt("age", 23) .navigation(); @Route(path = "/test/activity1") public class Test1Activity extends AppCompatActivity { @Autowired int age = 10; @Override protected void onCreate(Bundle savedInstanceState) { ARouter.getInstance().inject(this);// 在 onCreate中必須呼叫這個方法 } }
其實這個功能的實現也是基於上面@Route
機制的,因此這裡就大致講一下原來和關鍵程式碼:
-
AutowiredProcessor
會在編譯時掃描@Autowired
註解,並生成下面的檔案
public class Test1Activity$$ARouter$$Autowired implements ISyringe { @Override public void inject(Object target) { Test1Activity substitute = (Test1Activity)target; substitute.age = substitute.getIntent().getIntExtra("age", substitute.age); } }
-
ARouter.getInstance().inject(this);
最終會呼叫下面程式碼
static void inject(Object thiz) { AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation()); if (null != autowiredService) { autowiredService.autowire(thiz); } }
-
來看一下
AutowiredService
的實現類
@Route(path = "/arouter/service/autowired") public class AutowiredServiceImpl implements AutowiredService { private LruCache<String, ISyringe> classCache; private List<String> blackList; @Override public void init(Context context) { classCache = new LruCache<>(66); blackList = new ArrayList<>(); } @Override public void autowire(Object instance) {//instance 就是Test1Activity的例項 .... //autowiredHelper其實就是Test1Activity$$ARouter$$Autowired autowiredHelper = (ISyringe) Class.forName(instance.getClass().getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance(); autowiredHelper.inject(instance); // 呼叫 Test1Activity$$ARouter$$Autowired.inject() ... } }