Arouter源码系列之拦截器原理详解
做过组件化开发的小伙伴应该都比较了解Arouter使用,那么Arouter的拦截器就更不用说了,一般用拦截器作用很多,比如在跳转之前做一些额外的操作(经典用法检查是否登陆,没登陆跳到登陆界面,实现一个拦截器也很简单,加一条注解就ok:@Interceptor(priority = 7),priority代表的是优先级。既然加一条注解以后每次跳转都会回调到process方法,如下:
public class Interceptor1 implements IInterceptor {/*** The operation of this interceptor.** @param postcard meta* @param callback cb*/@Overridepublic void process(final Postcard postcard, final InterceptorCallback callback){}
那么肯定有用编译期注解的方式,如下:
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc7'compileOnly 'com.google.auto.service:auto-service-annotations:1.0-rc7'
在android中如果想在编译期根据注解产生新的java类的话,那么:auto-service是必不可少,然后你继承AbstractProcessor即可实现效果,这里处理拦截器的注解类的是InterceptorProcessor,实现处理的方法如下:
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {if (CollectionUtils.isNotEmpty(annotations)) {Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Interceptor.class);try {parseInterceptors(elements);} catch (Exception e) {logger.error(e);}return true;}return false;}
真正实现方法如下:
private void parseInterceptors(Set<? extends Element> elements) throws IOException {if (CollectionUtils.isNotEmpty(elements)) {logger.info(">>> Found interceptors, size is " + elements.size() + " <<<");// Verify and cache, sort incidentally.for (Element element : elements) {if (verify(element)) { // Check the interceptor metalogger.info("A interceptor verify over, its " + element.asType());Interceptor interceptor = element.getAnnotation(Interceptor.class);Element lastInterceptor = interceptors.get(interceptor.priority());if (null != lastInterceptor) { // Added, throw exceptionsthrow new IllegalArgumentException(String.format(Locale.getDefault(), "More than one interceptors use same priority [%d], They are [%s] and [%s].",interceptor.priority(),lastInterceptor.getSimpleName(),element.getSimpleName()));}interceptors.put(interceptor.priority(), element);} else {logger.error("A interceptor verify failed, its " + element.asType());}}// Interface of ARouter.TypeElement type_IInterceptor = elementUtils.getTypeElement(IINTERCEPTOR);TypeElement type_IInterceptorGroup = elementUtils.getTypeElement(IINTERCEPTOR_GROUP);/*** Build input type, format as :** ```Map<Integer, Class<? extends IInterceptor>>```*/ParameterizedTypeName inputMapTypeOfTollgate = ParameterizedTypeName.get(ClassName.get(Map.class),ClassName.get(Integer.class),ParameterizedTypeName.get(ClassName.get(Class.class),WildcardTypeName.subtypeOf(ClassName.get(type_IInterceptor))));// Build input param name.ParameterSpec tollgateParamSpec = ParameterSpec.builder(inputMapTypeOfTollgate, "interceptors").build();// Build method : 'loadInto'MethodSpec.Builder loadIntoMethodOfTollgateBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO).addAnnotation(Override.class).addModifiers(PUBLIC).addParameter(tollgateParamSpec);// Generateif (null != interceptors && interceptors.size() > 0) {// Build method bodyfor (Map.Entry<Integer, Element> entry : interceptors.entrySet()) {loadIntoMethodOfTollgateBuilder.addStatement("interceptors.put(" + entry.getKey() + ", $T.class)", ClassName.get((TypeElement) entry.getValue()));}}// Write to disk(Write file even interceptors is empty.)JavaFile.builder(PACKAGE_OF_GENERATE_FILE,TypeSpec.classBuilder(NAME_OF_INTERCEPTOR + SEPARATOR + moduleName).addModifiers(PUBLIC).addJavadoc(WARNING_TIPS).addMethod(loadIntoMethodOfTollgateBuilder.build()).addSuperinterface(ClassName.get(type_IInterceptorGroup)).build()).build().writeTo(mFiler);logger.info(">>> Interceptor group write over. <<<");}}
这个方法的意思就是取得Interceptor所有注解,然后将优先级做为key,存到TreeMap中,因为key是Integer类型实现了Comparable接口,所以TreeMap是根据优先级排序的,排好序之后,用javaPoet去生成ARouter$$Interceptors$$(MoudleName)类,这个类主要用来保存拦截器的真正实现类的class,如这句代码:loadIntoMethodOfTollgateBuilder.addStatement("interceptors.put(" + entry.getKey() + ",$T.class)", ClassName.get((TypeElement) entry.getValue()));
,JavaPoet链接。
这个拦截器什么时候被使用的呢?当然是在跳转前面,如下:
*/protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {//去掉无用代码......if (!postcard.isGreenChannel()) { // It must be run in async thread, maybe interceptor cost too mush time made ANR.interceptorService.doInterceptions(postcard, new InterceptorCallback() {/*** Continue process** @param postcard route meta*/@Overridepublic void onContinue(Postcard postcard) {_navigation(postcard, requestCode, callback);}/*** Interrupt process, pipeline will be destory when this method called.** @param exception Reson of interrupt.*/@Overridepublic void onInterrupt(Throwable exception) {if (null != callback) {callback.onInterrupt(postcard);}logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());}});} else {return _navigation(postcard, requestCode, callback);}return null;}
可以看到最终拦截器是通过interceptorService类调用,它的实现类是InterceptorServiceImpl,实现方法如下:
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {checkInterceptorsInitStatus();if (!interceptorHasInit) {callback.onInterrupt(new HandlerException("Interceptors initialization takes too much time."));return;}LogisticsCenter.executor.execute(new Runnable() {@Overridepublic void run() {CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());try {_execute(0, interceptorCounter, postcard);interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);if (interceptorCounter.getCount() > 0) { // Cancel the navigation this time, if it hasn't return anythings.callback.onInterrupt(new HandlerException("The interceptor processing timed out."));} else if (null != postcard.getTag()) { // Maybe some exception in the tag.callback.onInterrupt((Throwable) postcard.getTag());} else {callback.onContinue(postcard);}} catch (Exception e) {callback.onInterrupt(e);}}});} else {callback.onContinue(postcard);}}
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() {@Overridepublic void onContinue(Postcard postcard) {// Last interceptor excute over with no exception.counter.countDown();_execute(index + 1, counter, postcard); // When counter is down, it will be execute continue ,but index bigger than interceptors size, then U know.}@Overridepublic void onInterrupt(Throwable exception) {// Last interceptor execute over with fatal exception.postcard.setTag(null == exception ? new HandlerException("No message.") : exception); // save the exception message for backup.counter.cancel();// Be attention, maybe the thread in callback has been changed,// then the catch block(L207) will be invalid.// The worst is the thread changed to main thread, then the app will be crash, if you throw this exception!
// if (!Looper.getMainLooper().equals(Looper.myLooper())) { // You shouldn't throw the exception if the thread is main thread.
// throw new HandlerException(exception.getMessage());
// }}});}}
_execute在内部一直调用的递归,遍历map中所有而编译器用的TreeMap保证了优先级,好了到这拦截器的流程基本走完了,那么还有一个问题就是拦截器的集合 Warehouse.interceptors是什么时候填充的呢?当然是ARouter初始化的时候,如下:
protected static synchronized boolean init(Application application) {mContext = application;LogisticsCenter.init(mContext, executor);logger.info(Consts.TAG, "ARouter init success!");hasInit = true;mHandler = new Handler(Looper.getMainLooper());return true;}
最终调用LogisticsCenter.init,如下:
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {mContext = context;executor = tpe;try {long startInit = System.currentTimeMillis();//load by plugin firstloadRouterMap();if (registerByPlugin) {logger.info(TAG, "Load router map by arouter-auto-register plugin.");} else {Set<String> routerMap;// It will rebuild router map every times when debuggable.if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {logger.info(TAG, "Run with debug mode or new install, rebuild router map.");// These class was generated by arouter-compiler.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); // Save new version name when router map update finishes.} else {logger.info(TAG, "Load router map from cache.");routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));}logger.info(TAG, "Find router map finished, map size = " + routerMap.size() + ", cost " + (System.currentTimeMillis() - startInit) + " ms.");startInit = System.currentTimeMillis();for (String className : routerMap) {if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {// This one of root elements, load root.((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {// Load interceptorMeta((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {// Load providerIndex((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);}}}logger.info(TAG, "Load root element finished, cost " + (System.currentTimeMillis() - startInit) + " ms.");if (Warehouse.groupsIndex.size() == 0) {logger.error(TAG, "No mapping files were found, check your configuration please!");}if (ARouter.debuggable()) {logger.debug(TAG, String.format(Locale.getDefault(), "LogisticsCenter has already been loaded, GroupIndex[%d], InterceptorIndex[%d], ProviderIndex[%d]", Warehouse.groupsIndex.size(), Warehouse.interceptorsIndex.size(), Warehouse.providersIndex.size()));}} catch (Exception e) {throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");}}
其中getFileNameByPackageName真正实现了class的储存加载,如下代码:
public static Set<String> getFileNameByPackageName(Context context, final String packageName) throws PackageManager.NameNotFoundException, IOException, InterruptedException {final Set<String> classNames = new HashSet<>();List<String> paths = getSourcePaths(context);final CountDownLatch parserCtl = new CountDownLatch(paths.size());for (final String path : paths) {DefaultPoolExecutor.getInstance().execute(new Runnable() {@Overridepublic void run() {DexFile dexfile = null;try {if (path.endsWith(EXTRACTED_SUFFIX)) {//NOT use new DexFile(path), because it will throw "permission error in /data/dalvik-cache"dexfile = DexFile.loadDex(path, path + ".tmp", 0);} else {dexfile = new DexFile(path);}Enumeration<String> dexEntries = dexfile.entries();while (dexEntries.hasMoreElements()) {String className = dexEntries.nextElement();if (className.startsWith(packageName)) {classNames.add(className);}}} catch (Throwable ignore) {Log.e("ARouter", "Scan map file in dex files made error.", ignore);} finally {if (null != dexfile) {try {dexfile.close();} catch (Throwable ignore) {}}parserCtl.countDown();}}});}parserCtl.await();Log.d(Consts.TAG, "Filter " + classNames.size() + " classes by packageName <" + packageName + ">");return classNames;}
用的DexFile加载所有编译期产生的新java类的class,然后将class反射实例化,实现如下:
for (String className : routerMap) {if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {// This one of root elements, load root.((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {// Load interceptorMeta((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {// Load providerIndex((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);}}}
最终InterceptorServiceImpl的init方法实现,如下:
public void init(final Context context) {LogisticsCenter.executor.execute(new Runnable() {@Overridepublic 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;logger.info(TAG, "ARouter interceptors init over.");synchronized (interceptorInitLock) {interceptorInitLock.notifyAll();}}}});}
Arouter源码系列之拦截器原理详解
做过组件化开发的小伙伴应该都比较了解Arouter使用,那么Arouter的拦截器就更不用说了,一般用拦截器作用很多,比如在跳转之前做一些额外的操作(经典用法检查是否登陆,没登陆跳到登陆界面,实现一个拦截器也很简单,加一条注解就ok:@Interceptor(priority = 7),priority代表的是优先级。既然加一条注解以后每次跳转都会回调到process方法,如下:
public class Interceptor1 implements IInterceptor {/*** The operation of this interceptor.** @param postcard meta* @param callback cb*/@Overridepublic void process(final Postcard postcard, final InterceptorCallback callback){}
那么肯定有用编译期注解的方式,如下:
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc7'compileOnly 'com.google.auto.service:auto-service-annotations:1.0-rc7'
在android中如果想在编译期根据注解产生新的java类的话,那么:auto-service是必不可少,然后你继承AbstractProcessor即可实现效果,这里处理拦截器的注解类的是InterceptorProcessor,实现处理的方法如下:
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {if (CollectionUtils.isNotEmpty(annotations)) {Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Interceptor.class);try {parseInterceptors(elements);} catch (Exception e) {logger.error(e);}return true;}return false;}
真正实现方法如下:
private void parseInterceptors(Set<? extends Element> elements) throws IOException {if (CollectionUtils.isNotEmpty(elements)) {logger.info(">>> Found interceptors, size is " + elements.size() + " <<<");// Verify and cache, sort incidentally.for (Element element : elements) {if (verify(element)) { // Check the interceptor metalogger.info("A interceptor verify over, its " + element.asType());Interceptor interceptor = element.getAnnotation(Interceptor.class);Element lastInterceptor = interceptors.get(interceptor.priority());if (null != lastInterceptor) { // Added, throw exceptionsthrow new IllegalArgumentException(String.format(Locale.getDefault(), "More than one interceptors use same priority [%d], They are [%s] and [%s].",interceptor.priority(),lastInterceptor.getSimpleName(),element.getSimpleName()));}interceptors.put(interceptor.priority(), element);} else {logger.error("A interceptor verify failed, its " + element.asType());}}// Interface of ARouter.TypeElement type_IInterceptor = elementUtils.getTypeElement(IINTERCEPTOR);TypeElement type_IInterceptorGroup = elementUtils.getTypeElement(IINTERCEPTOR_GROUP);/*** Build input type, format as :** ```Map<Integer, Class<? extends IInterceptor>>```*/ParameterizedTypeName inputMapTypeOfTollgate = ParameterizedTypeName.get(ClassName.get(Map.class),ClassName.get(Integer.class),ParameterizedTypeName.get(ClassName.get(Class.class),WildcardTypeName.subtypeOf(ClassName.get(type_IInterceptor))));// Build input param name.ParameterSpec tollgateParamSpec = ParameterSpec.builder(inputMapTypeOfTollgate, "interceptors").build();// Build method : 'loadInto'MethodSpec.Builder loadIntoMethodOfTollgateBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO).addAnnotation(Override.class).addModifiers(PUBLIC).addParameter(tollgateParamSpec);// Generateif (null != interceptors && interceptors.size() > 0) {// Build method bodyfor (Map.Entry<Integer, Element> entry : interceptors.entrySet()) {loadIntoMethodOfTollgateBuilder.addStatement("interceptors.put(" + entry.getKey() + ", $T.class)", ClassName.get((TypeElement) entry.getValue()));}}// Write to disk(Write file even interceptors is empty.)JavaFile.builder(PACKAGE_OF_GENERATE_FILE,TypeSpec.classBuilder(NAME_OF_INTERCEPTOR + SEPARATOR + moduleName).addModifiers(PUBLIC).addJavadoc(WARNING_TIPS).addMethod(loadIntoMethodOfTollgateBuilder.build()).addSuperinterface(ClassName.get(type_IInterceptorGroup)).build()).build().writeTo(mFiler);logger.info(">>> Interceptor group write over. <<<");}}
这个方法的意思就是取得Interceptor所有注解,然后将优先级做为key,存到TreeMap中,因为key是Integer类型实现了Comparable接口,所以TreeMap是根据优先级排序的,排好序之后,用javaPoet去生成ARouter$$Interceptors$$(MoudleName)类,这个类主要用来保存拦截器的真正实现类的class,如这句代码:loadIntoMethodOfTollgateBuilder.addStatement("interceptors.put(" + entry.getKey() + ",$T.class)", ClassName.get((TypeElement) entry.getValue()));
,JavaPoet链接。
这个拦截器什么时候被使用的呢?当然是在跳转前面,如下:
*/protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {//去掉无用代码......if (!postcard.isGreenChannel()) { // It must be run in async thread, maybe interceptor cost too mush time made ANR.interceptorService.doInterceptions(postcard, new InterceptorCallback() {/*** Continue process** @param postcard route meta*/@Overridepublic void onContinue(Postcard postcard) {_navigation(postcard, requestCode, callback);}/*** Interrupt process, pipeline will be destory when this method called.** @param exception Reson of interrupt.*/@Overridepublic void onInterrupt(Throwable exception) {if (null != callback) {callback.onInterrupt(postcard);}logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());}});} else {return _navigation(postcard, requestCode, callback);}return null;}
可以看到最终拦截器是通过interceptorService类调用,它的实现类是InterceptorServiceImpl,实现方法如下:
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {checkInterceptorsInitStatus();if (!interceptorHasInit) {callback.onInterrupt(new HandlerException("Interceptors initialization takes too much time."));return;}LogisticsCenter.executor.execute(new Runnable() {@Overridepublic void run() {CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());try {_execute(0, interceptorCounter, postcard);interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);if (interceptorCounter.getCount() > 0) { // Cancel the navigation this time, if it hasn't return anythings.callback.onInterrupt(new HandlerException("The interceptor processing timed out."));} else if (null != postcard.getTag()) { // Maybe some exception in the tag.callback.onInterrupt((Throwable) postcard.getTag());} else {callback.onContinue(postcard);}} catch (Exception e) {callback.onInterrupt(e);}}});} else {callback.onContinue(postcard);}}
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() {@Overridepublic void onContinue(Postcard postcard) {// Last interceptor excute over with no exception.counter.countDown();_execute(index + 1, counter, postcard); // When counter is down, it will be execute continue ,but index bigger than interceptors size, then U know.}@Overridepublic void onInterrupt(Throwable exception) {// Last interceptor execute over with fatal exception.postcard.setTag(null == exception ? new HandlerException("No message.") : exception); // save the exception message for backup.counter.cancel();// Be attention, maybe the thread in callback has been changed,// then the catch block(L207) will be invalid.// The worst is the thread changed to main thread, then the app will be crash, if you throw this exception!
// if (!Looper.getMainLooper().equals(Looper.myLooper())) { // You shouldn't throw the exception if the thread is main thread.
// throw new HandlerException(exception.getMessage());
// }}});}}
_execute在内部一直调用的递归,遍历map中所有而编译器用的TreeMap保证了优先级,好了到这拦截器的流程基本走完了,那么还有一个问题就是拦截器的集合 Warehouse.interceptors是什么时候填充的呢?当然是ARouter初始化的时候,如下:
protected static synchronized boolean init(Application application) {mContext = application;LogisticsCenter.init(mContext, executor);logger.info(Consts.TAG, "ARouter init success!");hasInit = true;mHandler = new Handler(Looper.getMainLooper());return true;}
最终调用LogisticsCenter.init,如下:
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {mContext = context;executor = tpe;try {long startInit = System.currentTimeMillis();//load by plugin firstloadRouterMap();if (registerByPlugin) {logger.info(TAG, "Load router map by arouter-auto-register plugin.");} else {Set<String> routerMap;// It will rebuild router map every times when debuggable.if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {logger.info(TAG, "Run with debug mode or new install, rebuild router map.");// These class was generated by arouter-compiler.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); // Save new version name when router map update finishes.} else {logger.info(TAG, "Load router map from cache.");routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));}logger.info(TAG, "Find router map finished, map size = " + routerMap.size() + ", cost " + (System.currentTimeMillis() - startInit) + " ms.");startInit = System.currentTimeMillis();for (String className : routerMap) {if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {// This one of root elements, load root.((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {// Load interceptorMeta((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {// Load providerIndex((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);}}}logger.info(TAG, "Load root element finished, cost " + (System.currentTimeMillis() - startInit) + " ms.");if (Warehouse.groupsIndex.size() == 0) {logger.error(TAG, "No mapping files were found, check your configuration please!");}if (ARouter.debuggable()) {logger.debug(TAG, String.format(Locale.getDefault(), "LogisticsCenter has already been loaded, GroupIndex[%d], InterceptorIndex[%d], ProviderIndex[%d]", Warehouse.groupsIndex.size(), Warehouse.interceptorsIndex.size(), Warehouse.providersIndex.size()));}} catch (Exception e) {throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");}}
其中getFileNameByPackageName真正实现了class的储存加载,如下代码:
public static Set<String> getFileNameByPackageName(Context context, final String packageName) throws PackageManager.NameNotFoundException, IOException, InterruptedException {final Set<String> classNames = new HashSet<>();List<String> paths = getSourcePaths(context);final CountDownLatch parserCtl = new CountDownLatch(paths.size());for (final String path : paths) {DefaultPoolExecutor.getInstance().execute(new Runnable() {@Overridepublic void run() {DexFile dexfile = null;try {if (path.endsWith(EXTRACTED_SUFFIX)) {//NOT use new DexFile(path), because it will throw "permission error in /data/dalvik-cache"dexfile = DexFile.loadDex(path, path + ".tmp", 0);} else {dexfile = new DexFile(path);}Enumeration<String> dexEntries = dexfile.entries();while (dexEntries.hasMoreElements()) {String className = dexEntries.nextElement();if (className.startsWith(packageName)) {classNames.add(className);}}} catch (Throwable ignore) {Log.e("ARouter", "Scan map file in dex files made error.", ignore);} finally {if (null != dexfile) {try {dexfile.close();} catch (Throwable ignore) {}}parserCtl.countDown();}}});}parserCtl.await();Log.d(Consts.TAG, "Filter " + classNames.size() + " classes by packageName <" + packageName + ">");return classNames;}
用的DexFile加载所有编译期产生的新java类的class,然后将class反射实例化,实现如下:
for (String className : routerMap) {if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {// This one of root elements, load root.((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {// Load interceptorMeta((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {// Load providerIndex((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);}}}
最终InterceptorServiceImpl的init方法实现,如下:
public void init(final Context context) {LogisticsCenter.executor.execute(new Runnable() {@Overridepublic 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;logger.info(TAG, "ARouter interceptors init over.");synchronized (interceptorInitLock) {interceptorInitLock.notifyAll();}}}});}
发布评论