美文网首页
ARouter实现原理解析

ARouter实现原理解析

作者: 魔焰之 | 来源:发表于2019-06-02 20:16 被阅读0次

    相关角色:

    ARouter:负责提供客户端使用的Api接口,采用了门面模式,实际上内部委托给了_ARouter去处理
    _ARouter:路由中心控制器,负责控制整个路由的流程,通过Postcard中的信息导航客户端到目标地址(启动某个Activity或者获取某个服务的实现等)
    LogisticsCenter:后勤中心,负责注册路由信息到Warehouse和根据path或者Postcard到数据仓库中获取数据,再生成相关对象
    Warehouse:数据仓库,负责存储路由配置信息和具体生成的IProvider对象等。该类基本上都是一些数据集合,没有任何逻辑处理
    Postcard:明信片,RouteMeta类的子类,用于描述一个路由的具体信息,比如,目标组件类型(Activity||IProvider等)、目标组件需要的参数,
    RouteMeta:路由信息描述类,存储目标地址的类型,路径,参数等信息,LogisticsCenter根据RouteMeta对象描述的信息创建明信片。
    IRouteGroup:多个RouteMeta数据的容器,类似ViewGroup与View的关系
    IProvider:服务提供者,每一个实现该接口的类视为一个独立的服务,外部客户端可以根据path获取到该服务。
    IInterceptor:拦截器,客户端可以通过注册IInterceptor的实现类来实现路由的拦截,其拦截流程控制是在子线程中按照注册顺序依次调用拦截器的process方法将拦截权释放给客户端。其拦截控制器的实现在InterceptorServiceImpl类中。
    PathReplaceService:路径替换服务接口,实现者需要将path转换为另一个path

    初始化流程:

    ARouter框架能将多个服务提供者隔离,减少相互之间的依赖。其实现的流程和我们平常的快递物流管理很类似,每一个具体的快递包裹就是一个独立的服务提供者(IProvider),每一个快递信息单就是一个RouteMeta对象,客户端就是快递的接收方,而使用@Route注解中的path就是快递单号。在初始化流程中,主要完成的工作就是将所有注册的快递信息表都在物流中心(LogisticsCenter)注册,并将数据存储到数据仓库中(Warehouse)。
    初始化的入口是ARouter的init方法,其主要是控制初始化的流程,自身不处理具体实现,而是都委托给_ARouter去处理。

    public static void init(Application application) {
            if (!hasInit) {
                //委托给_ARouter去初始化
                hasInit = _ARouter.init(application);
                if (hasInit) {
                   //初始化之后调用afterInit
                    _ARouter.afterInit();
                }
            }
        }
    

    _ARouter方法的init方法实际是调用LogisticsCenter的init方法,下面是其核心代码:

                    Set<String> routerMap;
                    if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
                        //扫描apk中所有类,找到ROUTE_ROOT_PAKCAGE包下的类,实在子线程中完成的
                        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 finish.
                    } 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>()));
                    }
                    for (String className : routerMap) {
                        if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_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);
                        }
                    }
    

    LogisticsCenter的init方法主要完成了下面几件事:

    • 找到com.alibaba.android.arouter.routes包下的所有class文件类名,如果本地缓存的数据有效就从本地获取,如果有更新或者是debug模式,则通过扫描安装包的dex文件获取
    • 根据找到的类名去加载相关的实例到Warehouse中(类似与快递信息表入库)
      实际上com.alibaba.android.arouter.routes包下的class是由注解解析器自动生成的,主要IRouteRoot,IRouteGroup和IProviderGroup的实现类,比如当我们使用@Route注解某个类时,会自动将这个类的信息注入的到自动生成的上述实现类中。
      完成初始化之后会调用afterInit方法,其主要就是注入拦截控制服务(InterceptorServiceImpl)
    static void afterInit() {
            interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
        }
    
    //build方法主要是将path和group封装到Postcard中,可以理解成根据快递号生成一个快递信息表
    protected Postcard build(String path, String group) {
            if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
                throw new HandlerException(Consts.TAG + "Parameter is invalid!");
            } else {
                PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
                if (null != pService) {
                    path = pService.forString(path);
                }
                return new Postcard(path, group);
            }
        }
    

    而navigation方法是根据快递信息表来生成具体的实例,这里的拦截服务控制器实际是InterceptorServiceImpl对象,后面分析具体路由的实现时再看其具体的代码实现。整个初始化流程到注入InterceptorServiceImpl后就基本完成了

    如何实现路由功能:

    这里以一个具体的使用场景来看下路由的具体实现,我们实现了一个微博分享的服务,并使用@Route标注该服务。如下:

    @Route(path = "/service/WBShareService")
    public class WBShareServiceImp implements IShareService extends IProvider{
          @Override
        public void doShareImage(String text, String title, String path, boolean onlyClient) {
        }
    }
    

    然后客户端需要分享图片到微博时的使用代码如下:

    Object obj = ARouter.getInstance().build("/service/WBShareService").navigation();
    if (obj instanceof IShareService) {
       ((IShareService)obj).doShareImage("", "", "", false);
    }
    

    前面我们分析过ARouter的build方法了,其就是根据path生成一个Postcard对象,这里我们接着分析navigation方法,postcard对象的navigation方法最终都是委托给_ARouter的navigation方法来处理。

    protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
            //到物流中心完成postcard的信息填充,因为最初生成的postcard对象只包含path信息,不包含其他有效信息,比如路由类型,携带的参数等
            LogisticsCenter.completion(postcard);
            //如果不是绿色通道,则通过拦截控制器依次调用不同的拦截器处理信息(类似与一个包裹在检查通道了经过多个扫描检查)
            if (!postcard.isGreenChannel()) {  
                //每个拦截器的拦截方法调用都是在子线程中执行的
                interceptorService.doInterceptions(postcard, new InterceptorCallback() {
                    @Override
                    public void onContinue(Postcard postcard) {
                        _navigation(context, postcard, requestCode, callback);
                    }
                    @Override
                    public void onInterrupt(Throwable exception) {
                        if (null != callback) {
                            //只要有一个拦截器拦截该包裹,则回调onInterrupt方法宣告本次路由被终止
                            callback.onInterrupt(postcard);
                        }
                    }
                });
            } else {
                //绿色通道则直接调用_navigation方法进行具体的导航
                return _navigation(context, postcard, requestCode, callback);
            }
            return null;
        }
    

    可以看出navigation方法主要做了如下事情:

    1. 根据只包含path(理解成只有快递单号的快递信息表)的postcard去物流中心查找具体的路由信息(由编译时生成,在init时注入),完成后续步骤需要的数据填充。
    2. 如果不是绿色通道,则将postcard交予拦截控制器,委托各个拦截器在子线程执行检查是否拦截。
    3. 如果未拦截,则执行具体的导航功能
      这里先看下LogisticsCenter是怎么去填充信息到postcard中:
    public synchronized static void completion(Postcard postcard) {
            //去数据仓库获取路由信息,该信息在初始化ARouter时已经注入
            RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
            if (null == routeMeta) { 
               //如果没有路由信息,则尝试去数据仓库查找
            } else {
                //找到路由信息后,则将配置的路由信息填充到Postcard对象中
                postcard.setDestination(routeMeta.getDestination());
                postcard.setType(routeMeta.getType());
                postcard.setPriority(routeMeta.getPriority());
                postcard.setExtra(routeMeta.getExtra());
    
                Uri rawUri = postcard.getUri();
                if (null != rawUri) {   // Try to set params into bundle.
                    //这里主要是完成参数的填充
                }
                //针对不同的路由类型进行处理
                switch (routeMeta.getType()) {
                    case PROVIDER:  
                        //如果是服务提供者,则尝试获取其具体实例,如果没有,则根据路由信息构造一个实例,初始化并存储到数据仓库,
                        Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
                        IProvider instance = Warehouse.providers.get(providerMeta);
                        if (null == instance) { // There's no instance of this provider
                            IProvider provider;
                            provider = providerMeta.getConstructor().newInstance();
                                provider.init(mContext);
                                Warehouse.providers.put(providerMeta, provider);
                                instance = provider;
                        }
                        postcard.setProvider(instance);
                        //服务提供者被设置成绿色渠道,不用接受拦截检查
                        postcard.greenChannel();   
                        break;
                    case FRAGMENT:
                       //fragment也不用拦截检查
                        postcard.greenChannel();  
                    default:
                        break;
                }
            }
        }
    

    信息填充完之后,看一下具体的路由实现:

    private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
            final Context currentContext = null == context ? mContext : context;
    
            switch (postcard.getType()) {
                case ACTIVITY:
                    // Build intent
                    final Intent intent = new Intent(currentContext, postcard.getDestination());
                    intent.putExtras(postcard.getExtras());
    
                    // Set flags.
                    int flags = postcard.getFlags();
                    if (-1 != flags) {
                        intent.setFlags(flags);
                    } else if (!(currentContext instanceof Activity)) {    // Non activity, need less one flag.
                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    }
    
                    // Navigation in main looper.
                    new Handler(Looper.getMainLooper()).post(new Runnable() {
                        @Override
                        public void run() {
                            if (requestCode > 0) {  // Need start for result
                                ActivityCompat.startActivityForResult((Activity) currentContext, intent, requestCode, postcard.getOptionsBundle());
                            } else {
                                ActivityCompat.startActivity(currentContext, intent, postcard.getOptionsBundle());
                            }
    
                            if ((0 != postcard.getEnterAnim() || 0 != postcard.getExitAnim()) && currentContext instanceof Activity) {    // Old version.
                                ((Activity) currentContext).overridePendingTransition(postcard.getEnterAnim(), postcard.getExitAnim());
                            }
    
                            if (null != callback) { // Navigation over.
                                callback.onArrival(postcard);
                            }
                        }
                    });
    
                    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;
        }
    

    从上述代码我们可以看出,不同类型的路由其导航的方式也不一样

    • 如果是Activity类型,则将数据填充到intent中之后,调用ActivityCompat的startActivity或者startActivityForResult方法启动activity。
    • 如果是PROVIDER类型,则直接返回其服务提供者
    • 如果是BOARDCAST || CONTENT_PROVIDER || FRAGMENT,则创建其需要的实体,并填充数据,再返回该实体对象

    相关文章

      网友评论

          本文标题:ARouter实现原理解析

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