全面把握组件化ARouter,马吊颈打口试官!

开发者 2024-10-6 07:22:56 112 0 来自 中国
媒介

为了实现组件化要解决的几个题目点,此中 页面跳转组件间通讯 的题目是使用了 ARouter 这个框架来解决的。ARouter确实是专门用于做组件化改造,官方是这么介绍的:
一个用于资助 Android App 举行组件化改造的框架 —— 支持模块间的路由、通讯、解耦
是时间对 ARouter 这个强盛的框架做一个剖析了:它是怎样做到 页面跳转、组件间通讯 的?我们能从ARrouter中学到哪些东西?
由于内容预计算多,这篇紧张先梳理ARouter的实现原理,看看它是怎样做到跨组件跳转页面、获取服务。
一、路由认知

ARouter从命名即可知,这是一个路由框架。那么路由是个啥呢?
路由routing)就是通过互联的网络把信息源地点传输到目的地点的运动。-- 百科 可见 路由 是个动词,这是网络传输中的概念,完成路由这个操作的实体装备就是 路由器(Router)。
别的,生存中的 信件邮寄 也可以明白为一个 路由过程:邮局把信件从邮寄方 传输到吸收人的手上。首先 邮寄方 和 吸收人 是无法打仗的(无耦合依靠),只能通过 邮局这个第三方 完成邮寄;邮局根据信封上的地点,比方 “深圳市 深圳大学粤海校区”,决定分发到 开往深圳的车上,然后深圳的邮递员找到 深圳大学粤海校区 对应的 "南山区南海大道3688号”,终极找到吸收人。
1.png 对应地, ARouter 也是个“路由器”,也是个“邮政体系”。通行根据组件化介绍的,ARouter 资助 无相互依靠的组件间 举行跳转和通讯。
抽象一下,邮局、ARouter 都是 路由体系 ——— 给 无依靠的两边 提供 通讯和路由的本事。
官方文档 有详细的引入和各种功能使用介绍,包罗根本使用步调、参数剖析、拦截器、服务获取等进阶用法。这里不再搬运。
二、原明白析

使用ARouter在举行Activity跳转非常简朴:初始化ARouter、添加注解@Route、发起路由。
// 在module app中//1.初始化SDKARouter.init(mApplication); // 尽大概早,保举在Application中初始化// moduleA// 2.在支持路由的页面上添加注解(必选)// 路径留意至少需有两级,/xx/xx@Route(path = "/test/activity")public class YourActivity extend Activity {    ...}// moduleB(没有依靠 moduleA)// 3.发起路由跳转ARouter.getInstance().build("/test/activity").navigation();这样就使得 没有依靠moduleA的moduleB能跳转到moduleA的Activity了。服务获取也是类似简朴的代码就可实现。
那么 ARouter 是怎样做到只通过简朴2步 就完成 解耦组件间的路由操作呢?我们通过源码一步步明白。
2.1 构建PostCard
我们知道 想要跳转Activity终极肯定是走到了 startActivity(intent)方法,而intent是一样平常必要目的Activity的Class。以是我们料想 navigation()中应该是有探求目的Activity的Class这一过程的。
下面就来跟踪源码分析这一过程。先看ARouter.getInstance():
public static ARouter getInstance() {    if (!hasInit) { // 未初始化则报非常        throw new InitException("ARouter::Init::Invoke init(context) first!");    } else {        if (instance == null) {            synchronized (ARouter.class) {                if (instance == null) {                    instance = new ARouter();                }            }        }        return instance;    }}获取ARouter单实例,没有初始化则报非常。再看它的build(string)方法:
public Postcard build(String path) {    return _ARouter.getInstance().build(path);}这里是调用了 ARouter 的同名方法,返回了 Postcard(意为明信片)。ARouter实际是使用了表面模式(设计模式的一种),其全部方法都是调用了ARouter的同名方法。 进入_ARouter:
protected Postcard build(String path) {    if (TextUtils.isEmpty(path)) { //path不能为空        throw new HandlerException(Consts.TAG + "arameter is invalid!");    } else {        //path替换,这是预处理处罚        PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);        if (null != pService) {            path = pService.forString(path);        }        return build(path, extractGroup(path), true);    }}这里对path做了空校验和预处理处罚替换。假如想重写path,可以写一个PathReplaceService实现类。接着又走到重载方法:
protected Postcard build(String path, String group, Boolean afterReplace) {    if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {        throw new HandlerException(Consts.TAG + "arameter is invalid!");    } else {        ...        return new Postcard(path, group);    }}此中参数group是通过extractGroup(path)获取,也就是path的第一级,即"/test/activity"中的"test"。group的作用是作为路由的默认分组。
路由中的分组概念:

  • SDK中针对全部的路径(/test/1、/test/2)举行分组,分组只有在分组中的某一个路径第一次被访问的时间,该分组才会被初始化
  • 可以通过 @Route 注解主动指定分组,否则使用路径中第一段字符串(/*/)作为分组
  • 留意:一旦主动指定分组之后,应用内路由必要使用 ARouter.getInstance().build(path, group) 举行跳转,手动指定分组,否则无法找到
    @Route(path = "/test/1", group = "app")
末了返回创建的Postcard实例。Postcard是明信片的意思,承载了一次跳转/路由 必要的全部信息,它继续自路由元信息 RouteMeta
public final class Postcard extends RouteMeta {    // Base    private Uri uri;                //使用Uri方式发起的路由    private Object tag;             // A tag prepare for some thing wrong. inner params, DO NOT USE!    private Bundle mBundle;         //启动activity的传入的Bundle    private int flags = 0;         // 启动activity的Flags    private int timeout = 300;      // 路由超时时间    private IProvider provider;     // 假如是获取provider,就有值    private boolean greenChannel;  //是绿色通道    private SerializationService serializationService;    private Context context;        //context    private String action;          //启动activity的action        // activity转场动画相干    private Bundle optionsCompat;    private int enterAnim = -1;    private int exitAnim = -1;        public Postcard(String path, String group) {        this(path, group, null, null);    }    public Postcard(String path, String group, Uri uri, Bundle bundle) {        setPath(path);        setGroup(group);        setUri(uri);        this.mBundle = (null == bundle ? new Bundle() : bundle);    }    ...}public class RouteMeta {    private RouteType type;         // 路由范例;activity、service、fragment、IProvider等,编译时会根据被@Route注解的类的范例来设置    private Element rawType;        // 路由原始范例,在编译时用来判定    private Class<?> destination;   // 目的地:详细的 XxxActivity.class等    private String path;            // Path    private String group;           // Group    private int priority = -1;      // 优先级,越小优先级越高    private int extra;              // Extra    private Map<String, Integer> paramsType;  // 参数范例,比方activity中使用@Autowired的参数范例    private String name; //路由名字,用于天生javadoc    private Map<String, Autowired> injectConfig;  // 参数设置(对应paramsType).}

  • Postcard:路由的信息。 明白为是生存中的明信片。继续自RouteMeta。比方Postcard中的mBundle等则是 明信片上寄件人写的 “你好,xxx 节日快乐!” 这种笔墨内容。
  • RouteMeta:路由元信息,即根本信息。 明白就是 明信片上的 收件人地点 这种必备的根本信息。 明信片上可以没有笔墨内容,但想要被邮寄就必须有收件人地点
2.2 路由过程

  • 2.2.1 团体步调
通过path构建了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方法,而且把自己传了进去。ARouter的navigation方法同样会走到_ARouter的同名方法:
// @param context     Activity or null. // @param postcard    Route metas // @param requestCode RequestCode,startActivity的requestCode // @param callback    cb,路由回调:找到目的地、未找到、停止、到达protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {    //如有PretreatmentService的实现,就举行预处理处罚。可以在真正路由前举行一些判定然后停止路由。    PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);    if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {        // 预处理处罚失返回false,路由取消.        return null;    }    // 给路由设置context,比方启动activity必要。假如没有传就使用mContext,即初始化ARouter时传入的Application    postcard.setContext(null == context ? mContext : context);    try {        // 1. 美满postcard信息(如今只有path、group,还必要知道详细目的地,比方要跳转到的activity信息)        LogisticsCenter.completion(postcard);    } catch (NoRouteFoundException ex) {        //这里就是debug包中,没找到路由目的地时 经常出现的提示        if (debuggable()) {            runInMainThread(new Runnable() {                public void run() {                    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 {            // 没有callback的话, (假如有)就回调到低沉服务             DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);            if (null != degradeService) {                degradeService.onLost(context, postcard);            }        }        return null;    }    //找到的回调    if (null != callback) {        callback.onFound(postcard);    }    // 2. 不是绿色通道的话,要先走拦截器    if (!postcard.isGreenChannel()) {        interceptorService.doInterceptions(postcard, new InterceptorCallback() {                        //拦截器处理处罚结果:继续路由            @Override            public void onContinue(Postcard postcard) {                // 3. 获取路由结果                _navigation(postcard, requestCode, callback);            }             //拦截器处理处罚结果:停止路由,回调停止            @Override            public void onInterrupt(Throwable exception) {                if (null != callback) {                    callback.onInterrupt(postcard);                }            }        });    } else {        //绿色通道,不走拦截器,就获取路由结果        return _navigation(postcard, requestCode, callback);    }    return null;}使用前面构建好的PastCard颠末团体3个步调,就完成了路由:

  • 美满postcard信息:只有path、group还不行,还必要知道详细目的地,比方要跳转到的Activity信息。
  • 拦截器处理处罚:不是绿色通道的话,要先颠末路由器的处理处罚,结果是停止大概继续。
  • 获取路由结果:比方举行目的Activity的跳转、获取IProvider实现对象、获取Fragment等。


  • 2.2.2 获取路由结果
先来看比力简朴的末了一个步调——路由结果获取过程,也就是_navigation()方法:
private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {    final Context currentContext = postcard.getContext();    //根据路由范例来走对应逻辑    switch (postcard.getType()) {        case ACTIVITY:            // Activity, 使用postcard.getDestination()来构建intent、传入Extras、设置 flags、action            final Intent intent = new Intent(currentContext, postcard.getDestination());            intent.putExtras(postcard.getExtras());            int flags = postcard.getFlags();            if (0 != flags) {                intent.setFlags(flags);            }            // 当前的context不是activity,必要添加flag:FLAG_ACTIVITY_NEW_TASK            //(启动startActivity需有使命栈的,application/service启动activity没有使命栈,以是必须要new_task_flag新建一个使命栈)            if (!(currentContext instanceof Activity)) {                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);            }            String action = postcard.getAction();            if (!TextUtils.isEmpty(action)) {                intent.setAction(action);            }            // 末了在主线程实验 熟悉的startActivity,            runInMainThread(new Runnable() {                @Override                public void run() {                    startActivity(requestCode, currentContext, intent, postcard, callback);                }            });            break;        case PROVIDER:            //provider,指的是想要获取的服务,即IProvider的实现类。直接从postCard中获取。            return postcard.getProvider();        case BOARDCAST:        case CONTENT_PROVIDER:        case FRAGMENT:        //Broadcast、ContentProvider、Fragment,都是使用postcard.getDestination()反射创建实例            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;            } ...    }    return null;}从上面可见,postcard 颠末美满后,路由范例type、目的地destination等都已经被赋了值。destination就是目的类的class对象。
方法内容就是根据路由范例来走对应逻辑:

  • Activity, 使用postcard.getDestination()来构建intent、传入Extras、设置 flags、action,末了在主线程实验 熟悉的startActivity
  • provider,指的是想要获取的服务,即IProvider的实现类。是直接从postCard中获取的,由于服务类是单例,只会在初次获取时创建()。
  • Broadcast、ContentProvider、Fragment,都是使用postcard.getDestination()反射创建实例
团体逻辑还是比力简朴的。这里你大概好奇 destination的值是怎样获取的,由于无论哪种范例的路由,都是要使用目的class,这个就是ARouter最为焦点的内容——怎样获取 无直接依靠的模块的 class对象,也就是美满postcard信息的过程。 不外我们先来把拦截器逻辑分析完,末了再来看这个焦点点。

  • 2.2.3 拦截器
拦截器模式是开发中常用设计模式之一,路由中也可以设置拦截器,对路径举行判定决定是否必要停止。
未设置绿色通道的路由必要颠末拦截器处理处罚,也就是interceptorService的doInterceptions()方法。interceptorService是啥呢?
final class ARouter {    ...    private static InterceptorService interceptorService;    ...    //ARouter的初始化方法    public static void init(Application application) {        if (!hasInit) {            logger = _ARouter.logger;            hasInit = _ARouter.init(application);            if (hasInit) {                _ARouter.afterInit();            }        }    }   ...}    //_ARouter.java    static void afterInit() {        interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();    }InterceptorService继续IProvider,可见interceptorService也是一个服务,在ARouter初始化后 获取,用于处理处罚拦截器的逻辑。详细的实现类是InterceptorServiceImpl:
@Route(path = "/arouter/service/interceptor")public class InterceptorServiceImpl implements InterceptorService {...    @Override    public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {        //有拦截器        if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {            ...            LogisticsCenter.executor.execute(new Runnable() { //放入线程池异步实验                @Override                public void run() { //interceptorCounter 用于包管全部拦截器都走完,而且设置了超时                    CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());                    try {//实验第一个拦截器,假如没有停止 则递归调用继续背面的拦截器                        _execute(0, interceptorCounter, postcard);                        interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);                        if (interceptorCounter.getCount() > 0) {    // count>0阐明超时了,拦截器还没处理处罚完.                            callback.onInterrupt(new HandlerException("The interceptor processing timed out."));                        } else if (null != postcard.getTag()) {    //Tag!=null阐明被某个拦截器回调停止了                            callback.onInterrupt((Throwable) postcard.getTag());                        } else {                            callback.onContinue(postcard); // 全部拦截器处理处罚完、没超时、也没非常,则继续路由                        }                    }...                }            });        } else {            //没有拦截器则继续路由            callback.onContinue(postcard);        }    }    private static void _execute(final int index, final CancelableCountDownLatch counter, final Postcard postcard) {        if (index < Warehouse.interceptors.size()) {            //从Warehouse.interceptors中获取第index个拦截器,走process方法,假如回调到onContinue就继续下一个;            IInterceptor iInterceptor = Warehouse.interceptors.get(index);            iInterceptor.process(postcard, new InterceptorCallback() {                @Override                public void onContinue(Postcard postcard) {                    counter.countDown();                    _execute(index + 1, counter, postcard);  // 继续下一个                }                @Override                public void onInterrupt(Throwable exception) {                    postcard.setTag(null == exception ? new HandlerException("No message.") : exception);    // save the exception message for backup.                    counter.cancel();                    ...                }            });        }    }        @Override //此init方法会在服务被创建后调用。这里就是反射创建全部的拦截器实例    public void init(final Context context) {        LogisticsCenter.executor.execute(new Runnable() {            @Override            public void run() {                if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {                    //遍历Warehouse.interceptorsIndex ,使用存储与此中的拦截器class对象反射创建拦截器实例                    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);                        }...                    }                    interceptorHasInit = true;                    ...                }            }        });    }...}doInterceptions()方法中判定假如有拦截器,就放入线程池异步实验第一个拦截器,且使用interceptorCounter 包管全部拦截器都走完,同时也设置了超时。 假如第一个拦截器没有回调停止 则递归调用继续背面的拦截器。
拦截器的实验,是从Warehouse.interceptors中获取第index个拦截器,走process方法,假如回调到onContinue就继续下一个;若回调onInterrupt就停止路由。
拦截器的实验逻辑还是比力清楚的。那么拦截器是怎么获取的呢?我们来看下InterceptorServiceImpl的init方法:init()方法会在服务被创建后立即调用,如上所示就是遍历
Warehouse.interceptorsIndex ,使用存储在此中的拦截器class对象 反射创建拦截器实例,然后存在存入 Warehouse.interceptors。 也便是说,ARouter初始化完成后就获取到了全部拦截器实例
那么Warehouse又是啥呢?interceptorsIndex是怎样存储的全部拦截器的class的?

  • 2.2.4 路由元信息的收集
Warehouse意为堆栈,用于存放被 @Route、@Interceptor表明的 路由相干的信息,也就是我们关注的destination等信息。既然是堆栈,那么就是有存有取
前面举的例子:moduleB发起路由跳转到moduleA的activity,moduleB没有依靠moduleA,只是在moduleA的activity上增长了@Route注解。 由于举行activity跳转必要目的Activity的class对象来构建intent,以是必须有一个中心人,把路径"/test/activity"翻译成Activity的class对象,然后moduleB才华实现跳转。(因此在ARouter的使用中 moduleA、moduleB 都是必要依靠 arouter-api的)
这个中心人那就是ARouter了,而这个翻译工所作用到的辞书就是 Warehouse,它存着全部路由信息。
class Warehouse {    //全部IRouteGroup实现类的class对象,是在ARouter初始化中赋值,key是path第一级    //(IRouteGroup实现类是编译时天生,代表一个组,即path第一级类似的全部路由,包罗Activity和Provider服务)    static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();     //全部路由元信息,是在completion中赋值,key是path    //初次举行某个路由时就会加载整个group的路由,即IRouteGroup实现类中全部路由信息。包罗Activity和Provider服务    static Map<String, RouteMeta> routes = new HashMap<>();        //全部服务provider实例,在completion中赋值,key是IProvider实现类的class    static Map<Class, IProvider> providers = new HashMap<>();    //全部provider服务的元信息(实现类的class对象),是在ARouter初始化中赋值,key是IProvider实现类的全类名。    //紧张用于使用IProvider实现类的class发起的获取服务的路由,比方ARouter.getInstance().navigation(HelloService.class)    static Map<String, RouteMeta> providersIndex = new HashMap<>();        //全部拦截器实现类的class对象,是在ARouter初始化时收集到,key是优先级    static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("...");    //全部拦截器实例,是在ARouter初始化完成后立即创建    static List<IInterceptor> interceptors = new ArrayList<>();...}Warehouse存了哪些信息呢?

  • groupsIndex全部路由组元信息。是全部IRouteGroup实现类的class对象,是在ARouter初始化中赋值,key是path第一级。IRouteGroup实现类是编译时天生,代表一个组,即path第一级类似的全部路由,包罗Activity和Provider服务)。
  • routes全部路由元信息。是在LogisticsCenter.completion中赋值,key是path。初次举行某个路由时就会加载整个group的路由,即IRouteGroup实现类中全部路由信息。包罗Activity和Provider服务
  • providers全部服务provider实例。在LogisticsCenter.completion中赋值,key是IProvider实现类的class
  • providersIndex全部provider服务元信息(实现类的class对象)。是在ARouter初始化中赋值,key是IProvider实现类的全类名。用于使用IProvider实现类class发起的获取服务的路由,比方ARouter.getInstance().navigation(HelloService.class)
  • interceptorsIndex全部拦截器实现类class对象。是在ARouter初始化时收集到,key是优先级
  • interceptors全部拦截器实例。是在ARouter初始化完成后立即创建
此中groupsIndex、providersIndex、interceptorsIndex是ARouter初始化时就准备好的根本信息,为业务中随时发起路由操作(Activity跳转、服务获取、拦截器处理处罚)做好准备。
那么Warehouse的信息是怎样收集到的呢? 下面就先来看下ARouter初始化详细做了哪些事故:
final class _ARouter {    ...    protected static synchronized boolean init(Application application) {        mContext = application;        LogisticsCenter.init(mContext, executor);        ...        return true;    }_ARouter的init方法中 调用了LogisticsCenter的init方法。LogisticsCenter意为物流中央,上面提到的美满postcard的completion操作也是此类提供。
//LogisticsCenter.java//LogisticsCenter初始化,加载全部的路由元信息public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {...    try {        long startInit = System.currentTimeMillis();        //先实验使用AGP transform 收集 根资助类 后 写好的注入代码(要先引入插件才行 apply plugin: 'com.alibaba.arouter')        loadRouterMap();        if (registerByPlugin) {            //registerByPlugin为true阐明使用AGP加载ok了(通常都会用AGP,即registerByPlugin为true)            logger.info(TAG, "Load router map by arouter-auto-register plugin.");        } else {            //若没有使用 AGP transform,就用ClassUtils.getFileNameByPackageName来搜集dex中ROUTE_ROOT_PAKCAGE包下的全部类,即编译时天生的全部资助类            //这样的话,就是运行时 遍历搜集 会比力耗时,也就是init会较为耗时;而AGP transform 是在编译时完成收集的。            //当前app是新安装时才会走(收集到的资助类会缓存到SP文件)            Set<String> routerMap;            if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {                logger.info(TAG, "Run with debug mode or new install, rebuild router map.");                // 这写资助类是在编译时由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);            } else {                //不是新安装的版本,就从SP文件中读取                routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));            }...            //遍历资助类,区分是哪种资助类,然后反射创建资助类实例后,调用其loadInto方法来添补Warehouse相应的Map            for (String className : routerMap) {                if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {                    //类名开头:com.alibaba.android.arouter.routes.ARouter$$Root                    //添补Warehouse.groupsIndex,即全部IRouteGroup实现类的class对象                    ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);                } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {                    //类名开头:com.alibaba.android.arouter.routes.ARouter$$Interceptors                    //添补Warehouse.interceptorsIndex,即全部IInterceptor实现类的class对象                    ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);                } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {                    //类名开头:com.alibaba.android.arouter.routes.ARouter$$Providers                    //添补Warehouse.providersIndex,即全部provider的RouteMeta                    ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);                }            }        }...    }}LogisticsCenter初始化 就是加载全部的路由元信息的过程,有两种方式:

  • 走loadRouterMap()方法:直接使用在编译时收集好的资助类信息,然后反射创建资助类实例后,调用其loadInto方法来添补Warehouse相应的Map。这必要开发者要先引入插件才行 apply plugin: 'com.alibaba.arouter'。假如加载乐成,registerByPlugin这个值就为true,否则false。
  • 若第一步没有加载乐成,即registerByPlugin为false:就会使用ClassUtils.getFileNameByPackageName在运行时搜集dex中"com.alibaba.android.arouter.routes"包下的全部类(即资助类),然后遍历,区分是哪种资助类。接着反射创建资助类实例后,调用其loadInto方法来添补Warehouse相应的Map。
两种方式对比:

  • 类似点:都是使用资助类信息反射创建资助类实例后,调用其loadInto方法来添补Warehouse相应的Map。
  • 差异点:在于资助类信息的收集方式。前者是在编译时搜集就完毕了,后者是运行时。
一样平常都是使用第一种,由于运行时遍历dex搜集会集会比力耗时,而第一种在编译时已经收集好了。 第一种方式的loadRouterMap()方法的实现逻辑 我们稍后再看。
先看两个题目:

  • 上面提到的资助类是个啥? 怎样使用资助类添补Warehouse的?
  • 为啥资助类还要收集?还分 编译时收集、运行时收集?


  • 2.2.4.1 拦截器元信息
我们先来看拦截器元信息(拦截器class信息)是怎样通过资助类添补的:
上图是ARouter工程编译后module-java的build目次,ARouter$$ 开头的这些类都是在ARouter在编译过程中天生,它们就是所谓的资助类:
ARouter$$Interceptors$$modulejava 这个类就是一个资助类,资助WareHouse添补
WareHouse.interceptorsIndex。它实现接口IInterceptorGroup,loadInfo方法继续一个Map<Integer, Class<? extends IInterceptor>>,也就是
WareHouse.interceptorsIndex的范例。loadInfo方法体内,是用吸收的map来put当前module全部拦截器的class,即使用 @Interceptor 注解并实现 IInterceptor 接口的类。
在上面LogisticsCenter的init方法中第二种加载方式中看到,确实是遍历收集到的资助类,然后使用类名判定是 ARouter$$Interceptors$$modulejava,接着就调用loadInfo方法,这就实现了对
WareHouse.interceptorsIndex的赋值。 也就是说,有了ARouter$$Interceptors$$modulejava ,我们就能在ARouter初始化时对
WareHouse.interceptorsIndex举行赋值,就为创建全部拦截器实例做好了准备。
那么到这里,关于拦截器尚有一个题目,ARouter$$Interceptors$$modulejava的loadInfo方法中 拦截器实现类class是怎样获取的呢?—— 固然是编译时对注解 @Interceptor 的剖析,剖析过程将在下篇中介绍。
拦截器资助类我们看完了,再来看看其他三种资助类。

  • 2.2.4.2 路由组元信息
路由组元信息的收集是通过 —— ARouter$$Root$$xxx —— 根资助类:即用来资助对 WareHouse.groupsIndex 赋值。这样就会把path第一级类似的以是路由分到同一个组中。 一个module对应一个根资助类。xxx是module名,就是在build.gradle中设置的 AROUTER_MODULE_NAME 。
如上图,ARouter$$Root$$modulejava 就是根资助类,资助WareHouse添补Warehouse.groupsIndex。实现自IRouteRoot接口,loadInfo方法继续一个Map<String, Class<? extends IRouteGroup>>,也就是WareHouse.groupsIndex 的范例。loadInfo方法体内,是用吸收的map来put当前module全部路由组资助类的class
在上面LogisticsCenter的init方法中同样 对遍历收集到的资助类判定类名,接着就调用loadInfo方法,这就实现了对WareHouse.groupsIndex 的赋值。
根资助类,目的就是对路由举行分组,分组的利益是制止一次性加载全部路由,淘汰反射耗时和内存占用的性能题目。
根资助类也是在编译时天生,详细天生过程下次介绍。

  • 2.2.4.3 路由元信息
路由元信息的收集是通过 —— ARouter$$Group$$xxx —— 组资助类:即用来资助对 WareHouse.routes 赋值。也就是把同组的路由put到WareHouse.routes。 一个module大概有多个组,即对应有多个根资助类,xxx是组名,即path第一级。
如上图,ARouter$$Group$$test 就是组资助类,资助WareHouse添补Warehouse.routes。实现自IRouteGroup接口,loadInfo方法继续一个Map<String, RouteMeta>,也就是WareHouse.routes 的范例。loadInfo方法体内,是用吸收的map来put 当前组 的全部路由元信息此中最紧张的就是 每个路由的目的类class
在上面LogisticsCenter的init方法中 没有看到对组资助类的处理处罚。ARouter的设计是:在使用时才举行加载,即初次使用某个组的路由时,才会使用组资助类对 WareHouse.routes 举行添补。
组资助类,目的就是 初次使用时 一次性加载本组全部路由元信息。这比力符合实际使用场景:一样平常同组的路由都是同业务的内容,当前用户进入此业务时,就把本组路由元信息准备好,是比力公道的。
组资助类也是在编译时天生。

  • 2.2.4.4 provider元信息
provider元信息 着实在上面 路由元信息 中已经包罗了,为啥还要单独拎出来呢?我们转头看下 _ARouter的navigation方法:
protected <T> T navigation(Class<? extends T> service) {    Postcard postcard = LogisticsCenter.buildProvider(service.getName());    if (null == postcard) {        postcard = LogisticsCenter.buildProvider(service.getSimpleName());    }    if (null == postcard) {        return null;    }    postcard.setContext(mContext);    LogisticsCenter.completion(postcard);    return (T) postcard.getProvider();    ...}protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {    ...}前面介绍了4个参数的方法,而上面这个传服务class的重载方法 就是单独给获取provider服务使用的。看到通过LogisticsCenter使用服务类name获取到了PostCard,然后颠末美满PostCard,直接获取provider服务。
//LogisticsCenter.javapublic static Postcard buildProvider(String serviceName) {    RouteMeta meta = Warehouse.providersIndex.get(serviceName);    if (null == meta) {        return null;    } else {        return new Postcard(meta.getPath(), meta.getGroup());    }}此中Postcard着实就是从 Warehouse.providersIndex 中获取到的RouteMeta后构建的。而Warehouse.providersIndex的赋值就是通过 ——ARouter$$Providers$$xxx —— Provider资助类
资助类大概在其他文章中叫路由表,之前想叫做署理类,但以为资助类更符合,它们就是用来资助添补WareHouse中的元数据的。


  • 2.2.5 AGP方式加载路由
我们来看下路由信息收集的第一种方式,这是一样平常都会使用到的方式,也就是loadRouterMap()方法:
public class LogisticsCenter {    private static boolean registerByPlugin;        private static void loadRouterMap() {        registerByPlugin = false;        //主动注册插件 会在此处插入代码。调用此方法就注册了全部的 Routers、Interceptors、Provider            }   ...你会惊讶地发现,loadRouterMap()竟然只有一行代码?!
反编译 ARouter demo APK后,查察LogisticsCenter:
看到编译后的loadRouterMap()方法,多了几行register()方法的调用,而参数就是 全部的根资助类、拦截器资助类、provider资助类。
//LogisticsCenter.java... 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);                } ...            } ...        }    }    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; //标志通过AGP加载乐成了        }    }register()方法很简朴,就是反射创建资助类实例,调用loadInto方法对Warehouse举行添补,和第二种路由信息收集方式的是划一的。 而第二种是在运行时遍历dex才找到的资助类,在第一种方式就神奇的直接出现了?
这个神奇的操作,我们下次再做详细介绍,如今只需知道是在编译时举行扫描并动态在loadRouterMap()中插入代码就可以了。

  • 2.2.3 路由信息的美满
上面兜了一大圈,从路由团体过程、获取路由结果、拦截器、路由信息纪录,到各个资助类的介绍,也便是说 我们了解了 路由的发起、路由团体过程、路由结果获取,以及路由元信息的加载,那么如今就来看看路由元信息是怎样使用的。
//LogisticsCenter.javapublic synchronized static void completion(Postcard postcard) {    //美满postcard信息(如今只有path、group,还必要知道详细目的地,比方要跳转到的activity信息)    RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());    if (null == routeMeta) {        //没有从Warehouse.routes获取到:要么不存在、要么还没有加载本组路由        //先看路由堆栈中是否有这个组资助类,没就非常。(堆栈里的已有 组资助类 是谁放进堆栈的呢?就是 在 ARouter.init中调用 LogisticsCenter.init的时间。它内里的 loadRouterMap() 中实验代码是 transform时收集到的 APT 天生 根资助类 的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 {            try {                //这里,堆栈中有这个组的资助类,那么就可以 加载 这个组的全部路由 到内存                addRouteGroupDynamic(postcard.getGroup(), null);            } catch (Exception e) {                throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");            }            //堆栈有了这个组的路由信息,再重新美满            completion(postcard);        }    } else {        //有路由信息,就美满postcard        postcard.setDestination(routeMeta.getDestination());        postcard.setType(routeMeta.getType());        postcard.setPriority(routeMeta.getPriority());        postcard.setExtra(routeMeta.getExtra());        ...        switch (routeMeta.getType()) {            case PROVIDER:  //provider, 获取实例                // 要实现自IProvider                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) {...                    }                }                postcard.setProvider(instance); //实例通过PostCard带出去                postcard.greenChannel();    // Provider 不用颠末拦截器                break;            case FRAGMENT:                postcard.greenChannel();    // Fragment 不用颠末拦截器        }    }}public synchronized static void addRouteGroupDynamic(String groupName, IRouteGroup group) {    if (Warehouse.groupsIndex.containsKey(groupName)){//这里,堆栈中 有这个组的资助类        //拿到这个 组资助类,实例化,调loadInfo 把 这个组全部的路由信息加载 到 堆栈中的routes。        Warehouse.groupsIndex.get(groupName).getConstructor().newInstance().loadInto(Warehouse.routes);        Warehouse.groupsIndex.remove(groupName);    }...}

  • 实验通过path从堆栈中获取对应的路由元信息,假如没有获取到:要么不存在、要么还没有加载本组路由。
  • 先看路由堆栈中是否有这个组资助类,没就抛出非常;有就通过addRouteGroupDynamic()加载这个组的全部路由,然后再调completion
  • 有了path对应的路由元信息,就同步到postCard中,此中最紧张的就是 目的类class——routeMeta.getDestination()。而且判定如是provider就创建服务实例并存入堆栈。
好了,到这里,我们终于可以解答最开始提出的题目了:ARouter最为焦点的内容——怎样获取 无直接依靠的模块的 class对象

  • 编译时ARouter根据注解 @Route 天生了各个资助类,资助类的loadInfo方法中包罗了路由目的信息,最紧张的是注解的类class,然后在ARouter初始化时根据 根资助类、provider资助类、拦截器资助类 对堆栈WareHouse的groupsIndex举行赋值(以及providersIndex、interceptorsIndex),然后在路由发起后,根据path通过WareHouse的groupsIndex 加载 同组的全部路由元信息,也就是拿到了目的class。
  • 抽象一下就是:moduleA先把目的class存入第三方堆栈——ARouter的WareHouse,然后muduleB发起路由时从堆栈中根据path获取目的class,ARouter就是这个堆栈的管理者。 就好比 邮政是信件的管理者,它是两方通讯者的中心人。
2.3 流程图
以上分析内容梳理成流程图:
7.png 三、总结

我们从路由发起开始使,介绍了整个路由详细过程:moduelA通过中心人ARouter把路由信息的存到堆栈WareHouse;moduleB发起路由时,再通过中心人ARouter从堆栈WareHouse取出路由信息,这要就实现了没有依靠的两者之间的跳转与通讯。此中涉及Activity的跳转、服务provider的获取、拦截器的处理处罚等。
必要重点明白的是:路由框架的团体思绪,通过中心人ARouter使用WareHouse加载和获取路由信息;路由信息加载实现原理,各资助类作用和路由美满过程。
此中ARouter在编译时天生的资助类,是用于对全部使用@Route、@Interceptor注解的类信息的分组和收集,编译运行时对路由信息堆栈Warehouse的添补和使用。这里涉及到的是Annotation Process ToolAPT)技能,即注解处理处罚工具。
怎样使用编译时天生的资助类呢?除了运行时查找dex,还可以在编译时扫描资助类信息,而且直接在物流中央LogisticsCenter loadRouterMap()方法中直接插入使用资助类的代码,这里涉及 Android Gradle PluginAGP)技能,即Android的gradle插件相干技能。
Android开发组件化,是在项目发展到一定规模后 肯定要使用的技能,学习至完全把握非常必要。
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-11-22 02:10, Processed in 0.198803 second(s), 36 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表