你需要知道的Arouter

423 阅读5分钟

辅助工具类生成;

  • arouter-annotation: 项目中的注解Autowired,Interceptor,Route
  • arouter-compiler: 过apt,javapoet 来实现编译器的辅助代码生成;

APT:APT(Annotation Processing Tool)是java的注解处理技术

javapoet: 是用于生成 .java 源文件的 Java  Api

页面注册跳转

Group注册:

首先进行初始化

ARouter.init();

     _ARouter.init(application)
     
           LogisticsCenter.init(mContext, executor);

_ARouter 为整个Arouter 的实现类,此种模式为装饰者模式.客户端通过配置达成逻辑并进行解耦;

主要分析下:LogisticsCenter.init(mContext, executor);

public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
   mContext = context;
   executor = tpe;

   try {
       long startInit = System.currentTimeMillis();
       //load by plugin first  先通Gradle 插件来实现。如果插件失败通过线程池来实现
       loadRouterMap();
       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);
               }
               // 实现的加载 loadInto

           }
       }

       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() + "]");
   }
}

可以看到,测试环境加载所有 包名为 ROUTE_ROOT_PAKCAGE = "com.alibaba.android.arouter.routes"; 的Dex 类信息文件;因为这是个非常消耗性能的操作;Arroter 提供了两周形式;一直为通过线程池;第二种为Glade 来实现; 并做了缓存机制来提升性能; 加载 SUFFIX_ROOT = "Root";UFFIX_INTERCEPTORS = "Interceptors";String SUFFIX_PROVIDERS = "Providers" 通过反射实例化对象并调用方法,将注解的一些元素添加到static集合中:

image.png

具体代码实现(通过辅助生成的代码如下)

image.png

在此已经完成model 组的注册; 这里有个bug 需要知道issue 目前新版Arouter 会有异常抛出,所以在使用的时候分组名称和当前的modelf名称分组一致:

A Module 中注解了/a/target1
B Module 中注解了/b/target2 /a/target3

A Module的apt会生成Arouter$$Group$$a\
B Module的apt会生成Arouter$$Group$$b 和 Arouter$$Group$$a

其中Arouter$$Group$$a是同包名同类名,因此打包apk的时候会覆盖,只保留其中一个类,进而导致A Module 的注解类无法跳转。

具体执行目标的注册

辅助的生成代码: image.png 动态代码生成逻辑;

RouteProcessor
/*
  Build input param name.
 */
ParameterSpec groupParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "atlas").build();
/*
  Build method : 'loadInto'
 */
MethodSpec.Builder loadIntoMethodOfRootBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
        .addAnnotation(Override.class)
        .addModifiers(PUBLIC)
        .addParameter(rootParamSpec);

RouteMeta,存储一些类的一些信息。RouteType 类的类型为枚举,group 组名,destination 目标类等; image.png

页面跳转

执行跳转;

ARouter.getInstance().build("/test/activity2").navigation();

这里其实是一个 Postcard 继承 RouteMeta,携带了跳转的需要的各种信息,

image.png

其中 navigation 是调用到_Arouter.navigation;

image.png

PretreatmentService 跳转拦截,并可以做跳转前的逻辑处理; LogisticsCenter.completion(postcard);动态注册目标之外,还有对 postcard 的一些赋值;

image.png

callback.onFound(postcard); 跳转的处理的回调。丢失,到达等回调;

最终 实现跳转的是Arouter._navigation ,并没有什么过人之处,依然是使用的Android 的startActivity; postcard.getDestination(),为跳转了类的目标,在上面说的页面注册赋值;
image.png

image.png 至此页面的跳转完成;

页面的数据接受;

到达页面后,会有控制反转,对属性进行一些赋值;

ARouter.getInstance().inject(this);

_ARouter.inject(thiz);

AutowiredServiceImpl 获取AutowiredService的具体实现; image.png

然后通过具体的反射到辅助生成的类,进行数据的赋值; image.png

辅助生成工具类为:AutowiredProcessor,生成类示例:

image.png 后来完成最终的赋值;

service

  1. 注册service 通过apt注册所有的服务到容器内;
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
  1. postcard 进行通道赋值 IProvider,这里有句postcard.greenChannel();所以拦截器对所有的service 是没有作用的;

image.png 3. 执行 navigation 获取到最终的 class type 调用到最终的service 的实现类后调用具体实现函数

protected <T> T navigation(Class<? extends T> service)
return (T) postcard.getProvider();

拦截器

InterceptorProcessor 和 Interceptor 为拦截器辅助代码生成器。在使用的时候我们可以面对切面编程aop做到很好的解耦

拦截逻辑

  1. 在 navigation 跳转的时候会进行一个 interceptorService 调用检测;
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
         */
        @Override
        public void onContinue(Postcard postcard) {
            _navigation(postcard, requestCode, callback);
        }

        /**
         * Interrupt process, pipeline will be destory when this method called.
         *
         * @param exception Reson of interrupt.
         */
        @Override
        public 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);
}

postcard.isGreenChannel() 会有绿通道,在Build 的时候进行设置所有的拦截器不会调用;

  1. 看下 InterceptorService 的具体实现;
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;

                logger.info(TAG, "ARouter interceptors init over.");

                synchronized (interceptorInitLock) {
                    interceptorInitLock.notifyAll();
                }
            }
        }
    });
}

初始化所有的拦截器,并把拦截器放到 Warehouse.interceptors ,我们可以看到子线程进行的;完成初始化后通知主线程的,checkInterceptorsInitStatus 来检测是否初始化成功然后进行下一步的操作;

  1. 执行实践;
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() {
            @Override
            public 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);
    }
}
  • checkInterceptorsInitStatus : 等待初始化执行完成
  • _execute(0, interceptorCounter, 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() {
                @Override
                public 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.
                }

                @Override
                public 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();
//                    }
                }
            });
        }
    }

我们从这里可以看到拦截器的代码是进行子线程,所以拦截器内不能直接做ui的展示和更新; 同时 _execute(index + 1, counter, postcard) 这里有个递归调用可以看出 Warehouse.interceptors 的存放顺序 决定了拦截器的执行顺序;通过init 可以发现 Warehouse.interceptorsIndex 决定了Warehouse.interceptors ; Warehouse.interceptors 的添加在动态代码内

InterceptorProcessor

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 meta
                logger.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 exceptions
                    throw 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());
            }
        }

通过 interceptors.put(interceptor.priority(), element);可以看出在设置 @Interceptor(priority = 7) 数值越小 优先级越高;