ARouter调用过程

ARouter调用过程

ARouter在使用过程中,无论是通过path,还是类名(Provider类型的服务节点即可以通过path获取,也可以通过类名获取),都是先构造Postcard作为信息的载体(包括path,group,携带的参数等)为起点,最后通过调用navigation方法进行页面跳转或获取相应的服务、Fragment。

path调用方式

先看看通过path的调用:

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);
        //path预处理,需要自己实现PathReplaceService类,否则这里返回null
        if (null != pService) {
            path = pService.forString(path);
        }
        //构建postcard
        return new Postcard(path, group);
    }
}

Postcard通可以过一系列的链式调用添加参数,最后调用自己的navigation方法,此方法有很多重载方法,但最终是调用Arouter的navigation方法,Arouter只是个代理类,最后走的还是_Arouter类的navigation方法:

protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    try {
        //完善postcard,很重要的方法,涉及每个group下节点列表的加载等,后面单独介绍
        LogisticsCenter.completion(postcard);
    } catch (NoRouteFoundException ex) {//根据postcard中的path信息没有找到路由节点的情况   
        //... 省略log代码
        if (null != callback) {
            callback.onLost(postcard);
        } else {    // No callback for this invoke, then we use the global degrade service.
            DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
            if (null != degradeService) {
                degradeService.onLost(context, postcard);
            }
        }

        return null;
    }

    if (null != callback) {
        callback.onFound(postcard);
    }

    //判断是否需要做拦截处理,IProvider、Fragment不需要做拦截处理
    if (!postcard.isGreenChannel()) {   // It must be run in async thread, maybe interceptor cost too mush time made ANR.
        //doInterceptions方法里会处理拦截逻辑
        //interceptorService是一个Provider类型的服务接口实例
        //实现类是来自Arouter内部的InterceptorServiceImpl,后面会详细分析
        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) {
                    callback.onInterrupt(postcard);
                }
            }
        });
    } else {
        //跳过拦截器,直接执行路由逻辑
        return _navigation(context, postcard, requestCode, callback);
    }

    return null;
}

先继续看_navigation方法:

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: //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: //provider类型的节点则返回该provider服务
            return postcard.getProvider();
        case BOARDCAST:
        case CONTENT_PROVIDER:
        case FRAGMENT:
            //Fragment类型的就返回该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类型的,就跳转到相应的Activity页面,如果是Provider类型,就返回对应的服务实例,如果是Fragment类型,就返回对应的Fragment实例。

回过头先看看LogisticsCenter的completion方法:

public synchronized static void completion(Postcard postcard) {
    //...
    //根据path从Warehouse类的routes中获取缓存的节点
    RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
    //第一次获取为null
    if (null == routeMeta) {    // Maybe its does't exist, or didn't load.
        //Warehouse.groupsIndex在初始化的时候以group值为key,缓存了编译期自动生成的某个group路由节点列表类
        //根据group值,获取group类
        Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup());  // Load route meta.
        if (null == groupMeta) {
            throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
        } else {
            // Load route and cache it into memory, then delete from metas.
            try {
                //创建该group类的实例
                IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
                //以Warehouse.routes为参数调用其loadInto方法
                //loadInto方法会将该group下的所有节点都缓存到Warehouse.routes中
                //以path为key,value以RouteMeta为载体
                iGroupInstance.loadInto(Warehouse.routes);
                //加载完,将该group从缓存中移除
                Warehouse.groupsIndex.remove(postcard.getGroup());               
            } catch (Exception e) {
                //...
            }
            //重新调用completion方法,这时候获取的RouteMeta不为null,走else的逻辑
            completion(postcard);   // Reload
        }
    } else {
        //根据RouteMeta信息完善postcard信息
        postcard.setDestination(routeMeta.getDestination());//设置节点对应的类名
        postcard.setType(routeMeta.getType());//设置节点的类型
        postcard.setPriority(routeMeta.getPriority());//设置节点的优先级
        postcard.setExtra(routeMeta.getExtra());//设置携带的参数

        Uri rawUri = postcard.getUri();
        //只有通过uri跳转时才不为null
        if (null != rawUri) {   // Try to set params into bundle.
            //从uri中获取参数,然后保存到postcard中
            Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);
            Map<String, Integer> paramsType = routeMeta.getParamsType();

            if (MapUtils.isNotEmpty(paramsType)) {
                // Set value by its type, just for params which annotation by @Param
                for (Map.Entry<String, Integer> params : paramsType.entrySet()) {
                    setValue(postcard,
                            params.getValue(),
                            params.getKey(),
                            resultMap.get(params.getKey()));
                }
                // Save params name which need auto inject.
                postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));
            }

            // Save raw uri
            postcard.withString(ARouter.RAW_URI, rawUri.toString());
        }

        //对Fragment和provider类型的节点做特殊处理
        switch (routeMeta.getType()) {
            case PROVIDER:  // if the route is provider, should find its instance
                // Its provider, so it must be implememt IProvider
                //如果是provider类型的节点,则获取其实例
                //获取具体实现类名
                Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
                //尝试从缓存中获取实例,第一次返回null
                IProvider instance = Warehouse.providers.get(providerMeta);
                if (null == instance) { // There's no instance of this provider
                    IProvider provider;
                    try {
                        //创建provider的实例对象
                        provider = providerMeta.getConstructor().newInstance();
                        //调用其初始化方法
                        provider.init(mContext);
                        //将改provider实例缓存到Warehouse.providers中
                        Warehouse.providers.put(providerMeta, provider);
                        instance = provider;
                    } catch (Exception e) {
                        throw new HandlerException("Init provider failed! " + e.getMessage());
                    }
                }
                //无论是缓存的还是新建的,都将实例对象保存到postcard中
                postcard.setProvider(instance);
                //设置provider类型的路由不能被拦截器拦截
                postcard.greenChannel();    // Provider should skip all of interceptors
                break;
            case FRAGMENT:
                //设置Fragment类型的路由不能被拦截器拦截
                postcard.greenChannel();    // Fragment needn't interceptors
            default:
                break;
        }
    }
}

通过completion方法,完善了作为路由载体的postcard,这过程中还包含了对路由节点的加载缓存,provider类型的服务创建和缓存。

接着看看Arouter的拦截器的工作,Arouter内部提供了接口和实现类,看下接口定义和实现类:

接口InterceptorService如下:

public interface InterceptorService extends IProvider { //继承自IProvider,是个provider类型节点服务

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

Arouter提供的实现类InterceptorServiceImpl,而这个服务是在Arouter初始化的最后创建的,定位到_Arouter类中的相关代码如下:

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

可以看出,内部的provider服务也是通过Arouter这种统一路由的方式获取的,上述代码中的path对应的是InterceptorServiceImpl这个实现类,从之前的分析得知,通过navigation方法,Arouter会创建InterceptorServiceImpl的实例,然后调用其init方法,最后返回给服务,先看下InterceptorServiceImpl的定义和init方法:

@Route(path = "/arouter/service/interceptor")
public class InterceptorServiceImpl implements InterceptorService {
    private static boolean interceptorHasInit;
    private static final Object interceptorInitLock = new Object();

    //...省略其他代码
    
    @Override
    public void init(final Context context) {
        LogisticsCenter.executor.execute(new Runnable() {
            @Override
            public void run() {
                if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
                    //编译初始化阶段缓存在Warehouse.interceptorsIndex中的各个IInterceptor实现类,即用户自己添加的拦截器
                    for (Map.Entry<Integer, Class<? extends IInterceptor>> entry : Warehouse.interceptorsIndex.entrySet()) {
                        Class<? extends IInterceptor> interceptorClass = entry.getValue();
                        try {
                            //创建拦截器的实例
                            IInterceptor iInterceptor = interceptorClass.getConstructor().newInstance();
                            //调用其init方法,可以根据需求在里面做些初始化操作
                            iInterceptor.init(context);
                            //将拦截器的实例缓存起来
                            Warehouse.interceptors.add(iInterceptor);
                        } catch (Exception ex) {
                            //...
                        }
                    }

                    interceptorHasInit = true;

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

所以,在Arouter初始化结束后,就会主动去获取InterceptorServiceImpl服务,并将实例赋值给_Arouter的全局变量interceptorService,方便以后使用。而在获取InterceptorServiceImpl服务期间会调用其init方法,在init方法中,会加载所有用户定义的拦截器,然后缓存在Warehouse.interceptors中。当进行页面跳转时,Arouter会调用interceptorService的doInterceptions方法,看下该方法的实现:

@Override
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
    if (null != Warehouse.interceptors && Warehouse.interceptors.size() > 0) {
        
        //...省略不分代码
        
        LogisticsCenter.executor.execute(new Runnable() {
            @Override
            public void run() {
                //CancelableCountDownLatch相对于CountDownLatch只是增加了cancel方法,可以提早结束CountDownLatch的阻塞状态
                CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
                try {
                    //每个拦截器的执行过程,后面分析
                    _excute(0, interceptorCounter, postcard);
                    //可以设置超时时间
                    interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
                    //理论上到这里interceptorCounter.getCount()的大小应该等于0,异常情况如:超时
                    //所有拦截器都已处理,或者某个拦截器进行了拦截操作(会调用CancelableCountDownLatch的cancel方法,将CancelableCountDownLatch的count递减到0)
                    if (interceptorCounter.getCount() > 0) {
                        callback.onInterrupt(new HandlerException("The interceptor processing timed out."));
                    } else if (null != postcard.getTag()) {    
                        // Maybe some exception in the tag.
                        callback.onInterrupt(new HandlerException(postcard.getTag().toString()));
                    } else {
                        callback.onContinue(postcard);
                    }
                } catch (Exception e) {
                    callback.onInterrupt(e);
                }
            }
        });
    } else {
        callback.onContinue(postcard);
    }
}

每个拦截器的遍历执行放在_excute方法中:

    private static void _excute(final int index, final CancelableCountDownLatch counter, final Postcard postcard) {
        if (index < Warehouse.interceptors.size()) {
            //从0开始遍历拦截器,拦截器是按优先级降序排序的,也就是从高优先级的开始遍历
            IInterceptor iInterceptor = Warehouse.interceptors.get(index);
            //调用拦截器的process方法
            iInterceptor.process(postcard, new InterceptorCallback() {
                @Override
                public void onContinue(Postcard postcard) {
                    // Last interceptor excute over with no exception.
                    counter.countDown();
                    //递归调用
                    _excute(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) {

                    postcard.setTag(null == exception ? new HandlerException("No message.") : exception.getMessage());    // save the exception message for backup.
                    counter.cancel();

                }
            });
        }
    }

以上就是拦截器的相关处理逻辑。

类名调用方式

这个只支持provider类型,定位下_Arouter的代码:

protected <T> T navigation(Class<? extends T> service) {
    try {
        Postcard postcard = LogisticsCenter.buildProvider(service.getName());

        // Compatible 1.0.5 compiler sdk.
        if (null == postcard) { // No service, or this service in old version.
            postcard = LogisticsCenter.buildProvider(service.getSimpleName());
        }

        LogisticsCenter.completion(postcard);
        return (T) postcard.getProvider();
    } catch (NoRouteFoundException ex) {
        logger.warning(Consts.TAG, ex.getMessage());
        return null;
    }
}

除了构建postcard的逻辑有点不一样,其他的基本和path调用的一样,看下LogisticsCenter的buildProvider方法:

public static Postcard buildProvider(String serviceName) {
    //根据provider的类名,从缓存中获取节点信息,然后构造postcard
    //Warehouse.providersIndex中的缓存信息是Arouter初始化的时候加载进去的,key就是provider的类名
    RouteMeta meta = Warehouse.providersIndex.get(serviceName);

    if (null == meta) {
        return null;
    } else {
        //后面的逻辑就和path调用的一样了
        return new Postcard(meta.getPath(), meta.getGroup());
    }
}
自动赋值逻辑

之前分析调用过程并没有涉及到自动赋值逻辑,因为自动赋值需要用户在需要自动赋值的activity或fragment中主动调用Arouter的inject方法,比如在Activity的onCreate方法中调用:

    @Autowired
    String name;

    @Autowired(required = true)
    TestObj obj;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_test1);
    //用户需要单独调用此方法
    ARouter.getInstance().inject(this);
    //...
}

看下这个方法做了些什么:

static void inject(Object thiz) {
    AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation());
    if (null != autowiredService) {
        autowiredService.autowire(thiz);
    }
}

代码很简单,做了两件事:

1.获取AutowiredService服务

2.调用AutowiredService的autowire方法(自动赋值)

“/arouter/service/autowired”这个path对应的是Arouter提供的AutowiredServiceImpl类,同样是通过Arouter获取的服务,我们知道过程中会调用AutowiredServiceImpl的init方法,看下AutowiredServiceImpl的定义:

@Route(path = "/arouter/service/autowired")
public class AutowiredServiceImpl implements AutowiredService {
    private LruCache<String, ISyringe> classCache;
    private List<String> blackList;

    @Override
    public void init(Context context) {
        classCache = new LruCache<>(66);
        blackList = new ArrayList<>();
    }

    @Override
    public void autowire(Object instance) {
        //获取要自动赋值的类的类名,而在编译期通过AutowireProcessor自动生成的类命名都是按照当前类名+固定后缀的形式
        String className = instance.getClass().getName();
        try {
            if (!blackList.contains(className)) {
                //先从缓存中获取ISyringe实例
                ISyringe autowiredHelper = classCache.get(className);
                if (null == autowiredHelper) {  // No cache.
                    //第一次没有缓存,利用类名,通过反射构造实例
                    autowiredHelper = (ISyringe) Class.forName(instance.getClass().getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance();
                }
                //以需要被自动赋值的类为参数,调用实例的inject方法
                //inject的方法体是在编译期自动生成的,主要是根据不同类型对变量进行赋值
                autowiredHelper.inject(instance);
                //将实例缓存起来
                classCache.put(className, autowiredHelper);
            }
        } catch (Exception ex) {
            //加入黑名单
            blackList.add(className);    // This instance need not autowired.
        }
    }
}

在init方法中创建了缓存的集合,以及黑名单列表,AutowiredServiceImpl会把自动赋值过程中抛异常的类加入到黑名单列表中,下次直接跳过。通过调用AutowiredServiceImpl的autowire方法,就能达到自动赋值的目的。

所以用户需要对变量自动赋值,一定要在类中主动调用Arouter的inject方法。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容