美文网首页
ARouter源码分析

ARouter源码分析

作者: houtrry | 来源:发表于2021-11-28 16:46 被阅读0次

前置知识

APT

Annotation Processing Tool,自定义注解处理器。
搞Android的基本上都知道这个吧。许多第三方库都使用了APT去实现自己的功能,比如butterknife,比如X2C,比如我们要讲的ARouter。
其基本做法是:

  1. 自定义编译期注解(比如ARouter源码中的arouter-annotation模块)
  2. 自定义AbstractProcessor,实现process方法,在该方法中扫描步骤1定义的注解,根据注解信息生成辅助文件(.java文件)(比如ARouter源码中的arouter-cmpiler模块)
  3. Runtime时期,通过反射创建辅助类(获取步骤2生成的文件的全路径,反射),调用辅助类中的方法(比如ARouter源码中的arouter-api模块)
自定义Gradle Plugin

一般是自定义gradle Transform + ASM,实现AOP,可以在编译期修改project和第三方依赖库中的class文件(比如ARouter源码中的arouter-gradle-plugin模块),与APT主要是生成.java文件不同,ASM操作的是.class文件。
自定义gradle Transform功能很强大,可以与ASM结合,修改.class,也可以操作资源文件(比如统一压缩图片,转png大图为webp等)。
至于ASM,基于修改.class文件,我们即可以用ASM来插桩统计方法耗时,也可以用来实现自动化埋点,甚至是修改第三方lib中的crash...

写在前面

使用方法可以看ARouter
带着问题看源码,这里主要的问题是:

  1. 初始化都做了什么?
  2. ARouter是如何实现组件间的路由跳转的?
  3. 拦截器是如何生效的?
  4. IProvider 的实现机制
  5. ARouter的Gradle Plugin做了哪些优化?

初始化

ARouter.init(getApplication());

ARouter的核心方法。

ARouter#init

    public static void init(Application application) {
    //如果没初始化过,就执行初始化
    if (!hasInit) {
        logger = _ARouter.logger;
        _ARouter.logger.info(Consts.TAG, "ARouter init start.");
        //关键在这里,初始化路由表
        hasInit = _ARouter.init(application);

        if (hasInit) {
            //加载好路由表以后,执行其他操作
            //这里是初始化拦截器
            _ARouter.afterInit();
        }

        _ARouter.logger.info(Consts.TAG, "ARouter init over.");
    }
}

_ARouter#init

protected static synchronized boolean init(Application application) {
    mContext = application;
    //核心方法
    LogisticsCenter.init(mContext, executor);
    logger.info(Consts.TAG, "ARouter init success!");
    hasInit = true;
    //创建mainHandler
    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 first
        //这是问题5的关键,该方法默认空实现(不使用Gradle plugin的时候)
        //暂时跳过该方法,后面分析
        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.
            //如果是debug模式
            //或者App版本有更新(这里比较的是versionName)
            //重新加载路由表
            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();
                }
                //更新sp中的versionName
                //这里跟上面的PackageUtils.isNewVersion(context)对应
                PackageUtils.updateVersion(context);    // Save new version name when router map update finishes.
            } else {
                logger.info(TAG, "Load router map from cache.");
                //从sp中取出缓存的路由表(扫描加载路由表是耗时的IO操作,因此使用缓存,提高加载速度)
                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();
            //遍历路由表
            //初始化路由表的各个Group
            for (String className : routerMap) {
                if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
                    // This one of root elements, load root.
                    //如果是IRouteRoot的话(文件名以com.alibaba.android.arouter.routes.ARouter$$Root),反射创建IRouteRoot实例
                    //并执行其IRouteRoot#loadInto方法
                    ((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的话(文件名以com.alibaba.android.arouter.routes.ARouter$$Interceptors),反射创建IInterceptorGroup实例
                    //并执行其IInterceptorGroup#loadInto方法
                    ((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的话(文件名以com.alibaba.android.arouter.routes.ARouter$$Providers),反射创建IProviderGroup实例
                    //并执行其IProviderGroup#loadInto方法
                    ((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() + "]");
    }
}

这个方法算是核心中的核心了。
其实也只做了两件事情

  • 获取routerMap
  • 遍历routerMap,反射并执行IRouteRoot#loadInto/IInterceptorGroup#loadInto/IProviderGroup#loadInto

ClassUtils#getFileNameByPackageName

public static Set<String> getFileNameByPackageName(Context context, final String packageName) throws PackageManager.NameNotFoundException, IOException, InterruptedException {
    //记住,这里packageName的值是com.alibaba.android.arouter.routes
    final Set<String> classNames = new HashSet<>();
    //获取当前Apk目录下的所有dex文件
    List<String> paths = getSourcePaths(context);
    final CountDownLatch parserCtl = new CountDownLatch(paths.size());
    //遍历dex
    for (final String path : paths) {
        //使用线程池(默认核心线程数为CPU数+1,最大线程数CPU+1(也即是只有核心线程),等待队列ArrayBlockingQueue(容量64))
        DefaultPoolExecutor.getInstance().execute(new Runnable() {
            @Override
            public void run() {
                DexFile dexfile = null;

                try {
                    //加载dex文件
                    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);
                    }
                    //找到所有的以com.alibaba.android.arouter.routes开头的文件
                    //也就是找到包com.alibaba.android.arouter.routes下所有的文件
                    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;
}

总的来说,只干了一件事情,扫描所有的dex文件,找到com.alibaba.android.arouter.routes包下的所有文件并返回。这里的操作都是耗时操作。
但是com.alibaba.android.arouter.routes包下都是什么文件呢?
比如:

public class ARouter$$Root$$modulejava implements IRouteRoot {
    @Override
    public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
        routes.put("m2", ARouter$$Group$$m2.class);
        routes.put("module", ARouter$$Group$$module.class);
        routes.put("test", ARouter$$Group$$test.class);
        routes.put("yourservicegroupname", ARouter$$Group$$yourservicegroupname.class);
 }
}

比如:

public class ARouter$$Interceptors$$modulejava implements IInterceptorGroup {
    @Override
    public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) {
        interceptors.put(7, Test1Interceptor.class);
        interceptors.put(90, TestInterceptor90.class);
    }
}

比如:

public class ARouter$$Providers$$modulejava implements IProviderGroup {
    @Override
    public void loadInto(Map<String, RouteMeta> providers) {
        providers.put("com.alibaba.android.arouter.demo.service.HelloService", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/yourservicegroupname/hello", "yourservicegroupname", null, -1, -2147483648));
        providers.put("com.alibaba.android.arouter.facade.service.SerializationService", RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, "/yourservicegroupname/json", "yourservicegroupname", null, -1, -2147483648));
        providers.put("com.alibaba.android.arouter.demo.module1.testservice.SingleService", RouteMeta.build(RouteType.PROVIDER, SingleService.class, "/yourservicegroupname/single", "yourservicegroupname", null, -1, -2147483648));
    }
}

比如:

public class ARouter$$Group$$test implements IRouteGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> atlas) {
    atlas.put("/test/activity1", RouteMeta.build(RouteType.ACTIVITY, Test1Activity.class, "/test/activity1", "test", new java.util.HashMap<String, Integer>(){{put("ser", 9); put("ch", 5); put("fl", 6); put("dou", 7); put("boy", 0); put("url", 8); put("pac", 10); put("obj", 11); put("name", 8); put("objList", 11); put("map", 11); put("age", 3); put("height", 3); }}, -1, -2147483648));
    atlas.put("/test/activity2", RouteMeta.build(RouteType.ACTIVITY, Test2Activity.class, "/test/activity2", "test", new java.util.HashMap<String, Integer>(){{put("key1", 8); }}, -1, -2147483648));
    atlas.put("/test/activity3", RouteMeta.build(RouteType.ACTIVITY, Test3Activity.class, "/test/activity3", "test", new java.util.HashMap<String, Integer>(){{put("name", 8); put("boy", 0); put("age", 3); }}, -1, -2147483648));
    atlas.put("/test/activity4", RouteMeta.build(RouteType.ACTIVITY, Test4Activity.class, "/test/activity4", "test", null, -1, -2147483648));
    atlas.put("/test/fragment", RouteMeta.build(RouteType.FRAGMENT, BlankFragment.class, "/test/fragment", "test", new java.util.HashMap<String, Integer>(){{put("ser", 9); put("pac", 10); put("ch", 5); put("obj", 11); put("fl", 6); put("name", 8); put("dou", 7); put("boy", 0); put("objList", 11); put("map", 11); put("age", 3); put("height", 3); }}, -1, -2147483648));
    atlas.put("/test/webview", RouteMeta.build(RouteType.ACTIVITY, TestWebview.class, "/test/webview", "test", null, -1, -2147483648));
  }
}

这些都是APT生成的辅助类。

这个时候,我们停下来想一想,到现在为止,ARouter做了哪些事情?

①项目编译期,通过APT,生成辅助类(所有的辅助类包名都是com.alibaba.android.arouter.routes)
包括

  • 接口IRouteRoot的实现类(比如ARouter$$Root$$modulejava.java)
  • 接口IProviderGroup的实现类(比如ARouter$$Providers$$modulejava.java)
  • 接口IInterceptorGroup的实现类(比如ARouter$$Interceptors$$modulejava.java)
  • 接口IRouteGroup的实现类(比如ARouter$$Group$$test.java)

②ARouter#init初始化的时候,扫描dex文件,找到①生成的辅助类文件(也即是包com.alibaba.android.arouter.routes下的文件),放到routerMap中

③遍历routerMap,找到IRouteRoot/IProviderGroup/IInterceptorGroup的实现类,反射生成实例,并调用其loadInto方法

注意,这里没有实例化IRouteGroup,IRouteGroup的信息都在IRouteRoot中,这样做的目的是为了实现分组route的加载,用到了哪个group的route的信息,才会加载这个group的信息,没用到就不加载。这里可以仔细想想IProviderGroup/IInterceptorGroup/IRouteGroup的区别。
该方法执行完了以后,

  • Warehouse#groupsIndex存放所有的IRouteGroup信息
  • Warehouse#interceptorsIndex存放所有的IProvider信息
  • Warehouse#providersIndex存放所有的IInterceptor信息

至此,就完成了初始化路由表的操作。
我们回过头来瞄一眼ARouter#init,里面初始化路由表以后,执行了_ARouter#afterInit

_ARouter#afterInit

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

这一句看着很熟悉。

  • 跳转页面ARouter.getInstance().build("/test/activity").navigation();
  • 获取其他组件接口HelloService helloService3 = (HelloService) ARouter.getInstance().build("/yourservicegroupname/hello").navigation();

页面路由跳转/IProvider/拦截器都是ARouter.getInstance().build("/test/activity").navigation()这种形式的话,我们就先从拦截器interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();开始分析吧。

“/arouter/service/interceptor”

@Route(path = "/arouter/service/interceptor")
public class InterceptorServiceImpl implements InterceptorService{
    //方法省略
}

public interface InterceptorService extends IProvider {

    /**
     * Do interceptions
     */
    void doInterceptions(Postcard postcard, InterceptorCallback callback);
}

虽然这里我们是想看拦截器的实现,但是要明确一点:InterceptorServiceImpl是IProvider的实现类,获取InterceptorService也就是获取一个IProvider。有一点绕,简单来说,ARouter使用一个IProvider来实现拦截器的初始化。
后面的逻辑就变成了获取一个IProvider上了。

ARouter#build(java.lang.String)

public Postcard build(String path) {
    return _ARouter.getInstance().build(path);
}

_ARouter#build(java.lang.String)

protected Postcard build(String path) {
    if (TextUtils.isEmpty(path)) {
        throw new HandlerException(Consts.TAG + "Parameter is invalid!");
    } else {
        //这里PathReplaceService的逻辑先跳过,后面再回头分析
        PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
        if (null != pService) {
            path = pService.forString(path);
        }
        //extractGroup:根据path获取group
        //InterceptorService这里获取到的是"arouter"
        return build(path, extractGroup(path), true);
    }
}

_ARouter#build(java.lang.String, java.lang.String, java.lang.Boolean)

protected Postcard build(String path, String group, Boolean afterReplace) {
    if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
        throw new HandlerException(Consts.TAG + "Parameter is invalid!");
    } else {
        if (!afterReplace) {
            //同上,这里PathReplaceService的逻辑先跳过,后面再回头分析
            PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
            if (null != pService) {
                path = pService.forString(path);
            }
        }
        return new Postcard(path, group);
    }
}

也即是ARouter.getInstance().build("/arouter/service/interceptor").navigation()方法中,ARouter.getInstance().build("/arouter/service/interceptor")做的事情就是创建一个Postcard,其path是"/arouter/service/interceptor",group是"arouter".

Postcard#navigation()

public Object navigation() {
    return navigation(null);
}
public Object navigation(Context context) {
    return navigation(context, null);
}
public Object navigation(Context context, NavigationCallback callback) {
    return ARouter.getInstance().navigation(context, this, -1, callback);
}

ARouter#navigation(Context, Postcard, int, NavigationCallback)

public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) {
    return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);
}

_ARouter#navigation(Context, Postcard, int, NavigationCallback)

protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    //预处理
    //执行时机早于拦截器
    //这里,我们可以加log/拦截路由等
    //默认为空
    PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
    if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
        // Pretreatment failed, navigation canceled.
        return null;
    }

    // Set context to postcard.
    postcard.setContext(null == context ? mContext : context);

    try {
        //关键方法,待分析
        //tips:实际做的事情是:去路由表中查找路由信息,如果是IProvider,就反射创建实例         
        LogisticsCenter.completion(postcard);
    } catch (NoRouteFoundException ex) {
        logger.warning(Consts.TAG, ex.getMessage());

        if (debuggable()) {
            // Show friendly tips for user.
            runInMainThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(mContext, "There's no route matched!\n" +
                            " Path = [" + postcard.getPath() + "]\n" +
                            " Group = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show();
                }
            });
        }
        //看NoRouteFoundException也可以猜到,这里是没找到路由信息(比如path写错了,没匹配上)
        //如果没找到路由信息
        //执行回调
        if (null != callback) {
            //如果设置了callback
            callback.onLost(postcard);
        } else {
            // No callback for this invoke, then we use the global degrade service.
            //如果没设置callback,则执行全局的降级策略(暂时记住DegradeService,后面再分析)
            DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
            if (null != degradeService) {
                degradeService.onLost(context, postcard);
            }
        }
        //没找到路由信息的话,navigation方法就执行完了
        return null;
    }
    //找到了路由信息,执行回调
    if (null != callback) {
        callback.onFound(postcard);
    }
    //判断是否需要执行拦截器逻辑
    //这里只是一个Boolean变量标记值
    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);
    }

    return null;
}

简单来说,这里做的事情有

  • LogisticsCenter.completion(postcard):根据group和path查找路由信息
  • 拦截器拦截
  • _navigation(postcard, requestCode, callback)

LogisticsCenter#completion

public synchronized static void completion(Postcard postcard) {
    if (null == postcard) {
        throw new NoRouteFoundException(TAG + "No postcard!");
    }

    RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
    //去Warehouse.routes中找,有没有创建过path对应的RouteMeta
    //Warehouse.routes是HashMap,key是path,value是RouteMeta
    if (null == routeMeta) {
        //如果Warehouse.routes中没找到
        //比如第一次加载的时候
        // Maybe its does't exist, or didn't load.
        if (!Warehouse.groupsIndex.containsKey(postcard.getGroup())) {
            //如果找不到group信息,则抛出异常
            throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
        } else {
            //找到了group信息,则按照group加载
            //比如我们获取InterceptorService,这里的group就是"arouter"
            // Load route and cache it into memory, then delete from metas.
            try {
                if (ARouter.debuggable()) {
                    logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] starts loading, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
                }
                //加载group "arouter"
                addRouteGroupDynamic(postcard.getGroup(), null);

                if (ARouter.debuggable()) {
                    logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] has already been loaded, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
                }
            } catch (Exception e) {
                throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
            }

            completion(postcard);   // Reload
        }
    } else {
        //...暂时省略
    }
}
//通过groupName去Warehouse.groupsIndex找对应的class
//反射创建class,并执行其loadInto方法
public synchronized static void addRouteGroupDynamic(String groupName, IRouteGroup group) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    if (Warehouse.groupsIndex.containsKey(groupName)){
        // If this group is included, but it has not been loaded
        // load this group first, because dynamic route has high priority.
        Warehouse.groupsIndex.get(groupName).getConstructor().newInstance().loadInto(Warehouse.routes);
        Warehouse.groupsIndex.remove(groupName);
    }
    //加载一次后就从group中移除
    //从而保证只load一次
    // cover old group.
    if (null != group) {
        group.loadInto(Warehouse.routes);
    }
}

注意addRouteGroupDynamic(postcard.getGroup(), null)这个方法,通过groupName去groupIndex中查找,那"arouter"对应的是谁呢?正是ARouter$$Group$$arouter.class。
反射创建ARouter$$Group$$arouter对象,并执行ARouter$$Group$$arouter#loadInto方法

ARouter$$Root$$arouterapi和ARouter$$Group$$arouter

public class ARouter$$Root$$arouterapi implements IRouteRoot {
  @Override
  public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
    routes.put("arouter", ARouter$$Group$$arouter.class);
  }
}

public class ARouter$$Group$$arouter implements IRouteGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> atlas) {
    //该方法只做了一件事情
    //把AutowiredServiceImpl.class和InterceptorServiceImpl.class信息加载到Warehouse.routes中
    //Warehouse.routes
    atlas.put("/arouter/service/autowired", RouteMeta.build(RouteType.PROVIDER, AutowiredServiceImpl.class, "/arouter/service/autowired", "arouter", null, -1, -2147483648));
    atlas.put("/arouter/service/interceptor", RouteMeta.build(RouteType.PROVIDER, InterceptorServiceImpl.class, "/arouter/service/interceptor", "arouter", null, -1, -2147483648));
  }
}

现在我们回过头来继续看LogisticsCenter#completion

public synchronized static void completion(Postcard postcard) {
    //第一次加载,Warehouse.routes中找不到""/arouter/service/interceptor""
    RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
    if (null == routeMeta) {
        // Maybe its does't exist, or didn't load.
        if (!Warehouse.groupsIndex.containsKey(postcard.getGroup())) {
            throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
        } else {                
            //那就执行ARouter$$Group$$arouter#loadInto
            //执行完这个方法后,Warehouse.routes中就多了两个元素
            //"/arouter/service/autowired" -> AutowiredServiceImpl.class
            //"/arouter/service/interceptor" -> InterceptorServiceImpl.class
            addRouteGroupDynamic(postcard.getGroup(), null);
            //再执行一遍completion           
            completion(postcard);   // Reload
        }
    } else {
        //第二次加载的时候,Warehouse.routes中已经有了"/arouter/service/interceptor" -> InterceptorServiceImpl.class
        //设置参数
        postcard.setDestination(routeMeta.getDestination());
        postcard.setType(routeMeta.getType());
        postcard.setPriority(routeMeta.getPriority());
        postcard.setExtra(routeMeta.getExtra());                       

        switch (routeMeta.getType()) {
            case PROVIDER:  // if the route is provider, should find its instance
                // Its provider, so it must implement IProvider
                //如果是PROVIDER
                //恰巧我们要找的InterceptorServiceImpl的类型就是PROVIDER
                Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
                IProvider instance = Warehouse.providers.get(providerMeta);
                //找找Warehouse.providers中有没有已经初始化的实例
                if (null == instance) { // There's no instance of this provider
                    //没初始化过就执行反射,完成初始化
                    //针对InterceptorServiceImpl.class来说,这里就是创建InterceptorServiceImpl实例,然后执行InterceptorServiceImpl#init方法
                    //再把初始化好的实例存到Warehouse.providers中
                    IProvider provider;
                    try {
                        provider = providerMeta.getConstructor().newInstance();
                        provider.init(mContext);
                        Warehouse.providers.put(providerMeta, provider);
                        instance = provider;
                    } catch (Exception e) {
                        logger.error(TAG, "Init provider failed!", e);
                        throw new HandlerException("Init provider failed!");
                    }
                }
                postcard.setProvider(instance);
                //跳过拦截器拦截,结合上面讲的ARouter#navigation(Context, Postcard, int, NavigationCallback)
                postcard.greenChannel();    // Provider should skip all of interceptors
                break;
            case FRAGMENT:
                //跳过拦截器拦截,结合上面讲的ARouter#navigation(Context, Postcard, int, NavigationCallback)
                postcard.greenChannel();    // Fragment needn't interceptors
            default:
                break;
        }
    }
}

总结一下LogisticsCenter#completion方法做了啥:

  1. 去Warehouse.groupsIndex找到group对应的IRouteGroup,反射创建其实例,执行其IRouteGroup#loadInto方法,这样,就把group中的path->RouteMeta信息加载到Warehouse.routes中
  2. 从Warehouse.routes中找到path对应的RouteMeta信息(包括class类信息)
  3. 如果RouteMeta类型是PROVIDER,则反射创建其实例,执行其init方法,并把实例保存到Warehouse.providers中

以ARouter.getInstance().build("/arouter/service/interceptor").navigation()举例说明就是:

  1. Warehouse.groupsIndex找到group为"arouter"的IRouteGroup,这里找到的是ARouter$$Group$$arouter.class
  2. 反射ARouter$$Group$$arouter.class并执行其ARouter$$Group$$arouter#loadInto方法
  3. 把"/arouter/service/interceptor"->InterceptorServiceImpl.class信息加载到Warehouse.routes中
  4. 根据path="/arouter/service/interceptor"从Warehouse.routes中找到InterceptorServiceImpl.class
  5. 反射实例化InterceptorServiceImpl
  6. 执行InterceptorServiceImpl#init方法

InterceptorServiceImpl#init

public void init(final Context context) {
    //线程池中执行
    LogisticsCenter.executor.execute(new Runnable() {
        @Override
        public void run() {
            if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
                //遍历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中
                        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();
                }
            }
        }
    });
}

init方法做的事情很单一,就是一次性实例化全部的拦截器,存到 Warehouse.interceptors中。(想想为什么要这么做?)
这样,ARouter.getInstance().build("/arouter/service/interceptor").navigation()就分析完了,ARouter#init的时候,会创建所有的拦截器实例。ARouter.getInstance().build("/arouter/service/interceptor").navigation()方法返回的是InterceptorServiceImpl的实例。

另外,_ARouter#navigation(Context, Postcard, int, NavigationCallback)方法的最后,调用了_ARouter#_navigation

_ARouter#_navigation

private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    final Context currentContext = postcard.getContext();

    switch (postcard.getType()) {
        case ACTIVITY:
            //如果是ACTIVITY(页面跳转)
            //就构建Intent
            // Build intent
            final Intent intent = new Intent(currentContext, postcard.getDestination());
            intent.putExtras(postcard.getExtras());

            // Set flags.
            int flags = postcard.getFlags();
            if (0 != flags) {
                intent.setFlags(flags);
            }

            // Non activity, need FLAG_ACTIVITY_NEW_TASK
            if (!(currentContext instanceof Activity)) {
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            }

            // Set Actions
            String action = postcard.getAction();
            if (!TextUtils.isEmpty(action)) {
                intent.setAction(action);
            }

            // Navigation in main looper.
            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;
}

方法看着长,内容却很简单:

  • 如果是页面跳转,就构建Intent,调系统的ActivityCompat.startActivityForResult方法
  • 如果是PROVIDER,返回上一步LogisticsCenter.completion中已经创建好的实例
  • 如果是BOARDCAST/CONTENT_PROVIDER/FRAGMENT,反射创建实例

这样,ARouter#init就分析完了,总结一下:

  1. 扫描所有的dex文件,找到包com.alibaba.android.arouter.routes下的全部文件(耗时操作)
  2. 如果是IRouteRoot/IInterceptorGroup/IProviderGroup,就反射创建实例,执行其loadInto方法,这样,以group为单位的路由表信息就被存放到Warehouse.groupsIndex/Warehouse.interceptorsIndex/Warehouse.providersIndex中
  3. 初始化拦截器(创建所有的拦截器实例,存到Warehouse.interceptors中)

另外使用ARouter.getInstance().build("path").navigation()方法获取IProvider的流程如下:

  1. 如果Warehouse.routes中已经有path对应的RouteMeta,则执行步骤3,如果没有(第一次初始化),则执行步骤2
  2. Warehouse.groupsIndex中查找group对应的类,实例化并执行loadInto方法,将RouteMeta信息加载到Warehouse.routes,然后重新执行步骤1
  3. 取出Warehouse.routes中path对应的RouteMeta,通过反射实例化class对象,并执行其init方法,实例存到Warehouse.providers中,并返回该实例对象

Activity跳转的流程如下:

  1. 同上
  2. 同上
  3. 取出Warehouse.routes中path对应的RouteMeta,创建Intent对象,调用ActivityCompat.startActivityForResult实现页面跳转

至此,我们回答了问题1/问题2和问题4.
下面我们来看下剩下的问题
问题3:拦截器是如何生效的?
我们可以看看_ARouter

private static InterceptorService interceptorService;
static void afterInit() {
    // Trigger interceptor init, use byName.
    interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
}

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() {
            @Override
            public void onContinue(Postcard postcard) {
                _navigation(postcard, requestCode, callback);
            }
            @Override
            public void onInterrupt(Throwable exception) {
                if (null != callback) {
                    callback.onInterrupt(postcard);
                }
                logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());
            }
        });
    }
    //省略...
}

执行_ARouter#navigation的时候,执行了interceptorService.doInterceptions方法,前面我们已经知道,执行了interceptorService实际上是InterceptorServiceImpl。

InterceptorServiceImpl#doInterceptions

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);
                    //等待拦截器全部执行完,超时时间(默认300s)
                    interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
                    if (interceptorCounter.getCount() > 0) {    // Cancel the navigation this time, if it hasn't return anythings.
                        //如果还有列表Warehouse.interceptors中拦截器没执行完
                        //报超时
                        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);
    }
}

InterceptorServiceImpl#_execute

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

这里一个一个调用拦截器,如果有拦截器拦截,就中断调用,否则,调用下一个拦截器进行拦截。

所以,拦截器总结如下

  1. ARouter#init时,反射创建全部拦截器实例,放到Warehouse.interceptors中
  2. Postcard#navigation()时,遍历Warehouse.interceptors调用各个拦截器拦截

最后,我们来看下最后一个问题。
问题5 ARouter的Gradle Plugin做了哪些优化?
该问题的关键是LogisticsCenter#loadRouterMap

LogisticsCenter#loadRouterMap

private static void loadRouterMap() {
    registerByPlugin = false;
    // auto generate register code by gradle plugin: arouter-auto-register
    // looks like below:
    // registerRouteRoot(new ARouter..Root..modulejava());
    // registerRouteRoot(new ARouter..Root..modulekotlin());
}

private static void loadRouterMap() {
    registerByPlugin = false;
    // auto generate register code by gradle plugin: arouter-auto-register
    // looks like below:
    register("com.alibaba.android.arouter.routes.ARouter$$Root$$modulejava");
    register("com.alibaba.android.arouter.routes.ARouter$$Root$$modulekotlin");
    register("com.alibaba.android.arouter.routes.ARouter$$Root$$arouterapi");
    register("com.alibaba.android.arouter.routes.ARouter$$Interceptors$$modulejava");
    register("com.alibaba.android.arouter.routes.ARouter$$Providers$$modulejava");
    register("com.alibaba.android.arouter.routes.ARouter$$Providers$$modulekotlin");
    register("com.alibaba.android.arouter.routes.ARouter$$Providers$$arouterapi");
}

上面的是gradle plugin修改之前的,下面的loadRouterMap是gradle plugin修改之后的。

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, e);
        }
    }
}
private static void registerRouteRoot(IRouteRoot routeRoot) {
    markRegisteredByPlugin();
    if (routeRoot != null) {
        routeRoot.loadInto(Warehouse.groupsIndex);
    }
}
private static void registerInterceptor(IInterceptorGroup interceptorGroup) {
    markRegisteredByPlugin();
    if (interceptorGroup != null) {
        interceptorGroup.loadInto(Warehouse.interceptorsIndex);
    }
}
private static void registerProvider(IProviderGroup providerGroup) {
    markRegisteredByPlugin();
    if (providerGroup != null) {
        providerGroup.loadInto(Warehouse.providersIndex);
    }
}
private static void markRegisteredByPlugin() {
    if (!registerByPlugin) {
        registerByPlugin = true;
    }
}

瞄一眼register方法我们就能明白,这还是之前的那一套,跟不使用gradle plugin不同的地方在于,这里不需要扫描dex去找IRouteRoot/IInterceptorGroup/IProviderGroup,在编译期,gradle plugin就已经找到了这些,然后生成新的loadRouterMap方法。

相关文章

  • 阿里ARouter拦截器使用及源码解析(二)

    阿里ARouter的分析计划 阿里ARouter使用及源码解析(一) 阿里ARouter拦截器使用及源码解析(二)...

  • Android之旅 -- ARouter 源码分析(二)

    Android之旅 -- ARouter 源码分析(一) 主要介绍了 ARouter 启动 activity 的基...

  • [Android] ARouter

    开源最佳实践:Android平台页面路由框架ARouter Alibaba-ARouter 源码分析笔记 ARou...

  • ARouter源码解析(二)

    arouter-api version : 1.4.1 前言 前几天对 ARouter 的页面跳转源码进行了分析,...

  • ARouter源码分析

    ARouter源码解读 以前看优秀的开源项目,看到了页面路由框架ARouter,心想页面路由是个啥东东,于是乎网上...

  • ARouter源码分析

    前言 Android app开发中,经常会用到组件化技术,具体表现就是将各个模块以module的方式集成在主模块(...

  • ARouter源码分析

    目录 1.源码思维导图2.运行时源码分析3.编译期原理3.总结 1.ARouter源码思维导图 这个图是最近总结组...

  • Arouter源码分析

    Arouter核心请求类图 Arouter 和 _Arouter 的关系 _Arouter主要是处理Arouter...

  • ARouter源码分析

    前置知识 APT Annotation Processing Tool,自定义注解处理器。搞Android的基本上...

  • ARouter的源码分析

    分析需要储备以下知识点: Java注解知识 Java反射知识 javapoet库(在注释里也有简单分析) auto...

网友评论

      本文标题:ARouter源码分析

      本文链接:https://www.haomeiwen.com/subject/baqhxrtx.html