ARouter实现原理解析

相关角色:

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,则创建其需要的实体,并填充数据,再返回该实体对象
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值