【ARouter】 跳转 Activity 过程分析

4 篇文章 0 订阅
1 篇文章 0 订阅

本篇内容主要分析一下 ARouter 的跳转过程——本篇只分析 Activity 的跳转过程。
Arouter系列:
【ARouter】接入笔记
【ARouter】初始化过程分析
【ARouter】跳转 Activity 过程分析

跳转

ARouter 的简单跳转比如这样子 ARouter.getInstance().build("/main/mainActivityTest").navigation() 就实现了一个简单跳转,那么它的具体是怎么运作的呢,下面就一步一步来分析。

生成跳转信息

build

在简单跳转的调用过程中,进入 build 方法:

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

会发现,它实际上是把 build 操作给了 _ARouter 最终返回了一个 PostcardPostcard 这个类是包含了本次跳转中所有的路由映射信息的类。

下面看一下它具体的 build 方法的实现:

    protected Postcard build(String path) {
            ...
            return build(path, extractGroup(path));
    }

    protected Postcard build(String path, String group) {
        if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
            throw new HandlerException(Consts.TAG + "Parameter is invalid!");
        } else {
            // 这里我们可以先忽略掉 ARouter 会查找我们是否实现了 URL 重写。这里可以先看做没有。
            PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
            if (null != pService) {
                path = pService.forString(path);
            }
            return new Postcard(path, group);
        }
    }
    // 如果是URI 略有不同
    protected Postcard build(Uri uri) {
            return new Postcard(uri.getPath(), extractGroup(uri.getPath()), uri, null);
    }

extractGroup 方法会自动从路径当中提取出组名。如果是 URI 的形式跳转的话,会把 Uri 一块传递进去。然后我们看一下 Postcard 具体内容:

Postcard

public final class Postcard extends RouteMeta {
    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);
    }
}

可以看到它内容很简单,就是初始化了路径以及组 URI 信息,同时生成了一个 bundle 实例。

设置参数

我们知道 ARouter 的 api 提供了一系列的 withxxx 方法来供跳转时传递参数使用。而这些方法都是由 Postcard 提供的:

    public Postcard withString(@Nullable String key, @Nullable String value) {
        mBundle.putString(key, value);
        return this;
    }

    ...

    public Postcard withObject(@Nullable String key, @Nullable Object value) {
        serializationService = ARouter.getInstance().navigation(SerializationService.class);
        mBundle.putString(key, serializationService.object2Json(value));
        return this;
    }

这些方法都很简单,就是把参数放在了构造时生成的 bundle 中,唯一不同的就是 withObject 方法,这个方法通过了一个序列化服务类来将传入的 object 进行序列化,所以在使用它的时候一定要实现这个服务。使用方法参看这里

navigation 发起跳转

上面的步骤把跳转所需的信息都准备好了,然后就可以调用 navigation 方法进行跳转,

    public Object navigation() {
        return navigation(null);
    }

    ...

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

navigation 有一系列的重载:

  • navigation()
  • navigation(Context context)
  • navigation(Context context, NavigationCallback callback)
  • navigation(Activity mContext, int requestCode)
  • navigation(Activity mContext, int requestCode, NavigationCallback callback)

这一系列的重载最终都调用了 _ARouter.getInstance().navigation 方法:

    protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
        try {
            // 核心方法 通过路由信息完善 Postcard
            LogisticsCenter.completion(postcard);
        } catch (NoRouteFoundException ex) {
            logger.warning(Consts.TAG, ex.getMessage());
            ...
            if (null != callback) {
                // 如果设置了跳转回调 则调用 onLost 方法
                callback.onLost(postcard);
            } else {
                // 如果没有设置回调,则查看是否有全局处理服务
                DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
                if (null != degradeService) {
                    degradeService.onLost(context, postcard);
                }
            }
            return null;
        }
        if (null != callback) {
            callback.onFound(postcard);// 回调 onFound
        }
        // 如果没有设置绿色通道则执行拦截器
        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) {
                        // 被拦截了,进行回调
                        callback.onInterrupt(postcard);
                    }
                }
            });
        } else {
            // 进行下一步的跳转
            return _navigation(context, postcard, requestCode, callback);
        }
        return null;
    }

上面的代码也很简单

  • 首先完善了需要跳转的信息
  • 进行一系列的回调
  • 检查拦截器
  • 进行下一步的跳转

LogisticsCenter.completion(postcard)

LogisticsCenter 是 ARouter 的核心类,completion 方法通过缓存在内存中的路由信息来完善 postcard,因为在我们之前 build 方法只是给 postcard 设置了路径以及一些跳转携带的参数,这个时候我们还不知道这个路径具体映射到哪个 activity 或者其他什么类,所以需要在这里进行完善才能继续进行跳转。下面看一下它的代码:

    public synchronized static void completion(Postcard postcard) {
        // 从路由缓存中根据路径来获取路由信息
        RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
        if (null == routeMeta) {   
            // 如果没有获取到,则开始加载它
            // 先获取到它所在的组信息
            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 {
                try {
                    // 将一组路由信息缓存到内存——Warehouse.routes中,
                    //并将当前组信息从 Warehouse.groupsIndex 删除
                    // 这里体现了分组加载机制  —— ARouter$$Group$$xxx
                    IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
                    iGroupInstance.loadInto(Warehouse.routes);
                    Warehouse.groupsIndex.remove(postcard.getGroup());
                } catch (Exception e) {
                    throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
                }
                // 重新调用,此时缓存中的路由信息已经不为空了
                completion(postcard);   
            }
        } else {
            // 如果找到了 则设置信息
            postcard.setDestination(routeMeta.getDestination());
            postcard.setType(routeMeta.getType());
            postcard.setPriority(routeMeta.getPriority());
            postcard.setExtra(routeMeta.getExtra());

            Uri rawUri = postcard.getUri();
            if (null != rawUri) {
                /*
                这一部分代码很好的解释了,为什么说在使用 URI 跳转时,即使不需要依赖注入
                也需要加入 @Autowired 注解的原因。
                */
                Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);
                // 获取需要依赖注入的字段
                Map<String, Integer> paramsType = routeMeta.getParamsType();

                if (MapUtils.isNotEmpty(paramsType)) {
                    //如果是 uri的形式,则解析 uri 中的参数,并重新设置参数
                    for (Map.Entry<String, Integer> params : paramsType.entrySet()) {
                        setValue(postcard,
                                params.getValue(),
                                params.getKey(),
                                resultMap.get(params.getKey()));
                    }
                    // 将需要自动注入的参数名保存下来
                    postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));
                }
                postcard.withString(ARouter.RAW_URI, rawUri.toString());
            }

            switch (routeMeta.getType()) {
                case PROVIDER:
                    ...
                    postcard.greenChannel();    // 如果是 provider 则设置绿色通道
                    break;
                case FRAGMENT:
                    postcard.greenChannel();    // 如果是 fragment 则设置绿色通道
                default:
                    break;
            }
        }
    }

上面的代码注释已经写得很清楚,其实不止 activity 跳转,任何 ARouter 中通过路径来 navigation 方法的功能都需要经由此方法。主要有三个:

  • 将一组路由信息缓存到 Warehouse.routes 中
  • 完善 postcard 的信息
  • 如果是URI跳转,则设置参数

执行拦截器

在完善完路由信息以后,ARouter 会检测是否设置了绿色通道,如果没有设置绿色通道则会触发拦截器。interceptorService初始化过程分析中有分析过,在初始化完成以后会执行拦截器的初始化。这里就触发了拦截器:

	// class InterceptorServiceImpl
    @Override
    public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
        // 首先检查是否设置了自定义的拦截器
        if (null != Warehouse.interceptors && Warehouse.interceptors.size() > 0) {
            // 检查拦截器是否初始化成功
            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 {
                        // 循环遍历拦截器来处理本次跳转
                        _excute(0, interceptorCounter, postcard);
                        interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
                        if (interceptorCounter.getCount() > 0) {  
                            // 如果超时 则把这次跳转拦截掉
                            callback.onInterrupt(new HandlerException("The interceptor processing timed out."));
                        } else if (null != postcard.getTag()) {
                            // 如果 tag 不为空也拦截掉
                            callback.onInterrupt(new HandlerException(postcard.getTag().toString()));
                        } else {
                            // 不拦截
                            callback.onContinue(postcard);
                        }
                    } catch (Exception e) {
                        callback.onInterrupt(e);
                    }
                }
            });
        } else {
            callback.onContinue(postcard);// 不拦截
        }
    }

interceptorService 主要就是检测了是否设置了拦截器,如果没有设置拦截器则直接执行了 onContinue 方法,如果设置了拦截器,则在一个线程池中调用 _excute 方法来循环触发拦截器,这个方法如下,它根据优先级来进行了一次递归循环调用了设置的拦截器:

    private static void _excute(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) {
                    counter.countDown();
                    // 递归调用
                    _excute(index + 1, counter, postcard);  // When counter is down, it will be 
                }

                @Override
                public void onInterrupt(Throwable exception) {
                    // 给它设置一个拦截掉的tag
                    postcard.setTag(null == exception ? new HandlerException("No message.") : exception.getMessage());    // save the exception message for backup.
                    // 停止计数
                    counter.cancel();
                }
            });
        }
    }

执行跳转

经过拦截器的过滤,然后就要进行真正的跳转了,上面分析可以看到它最终调用了 _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:
                // 创建 intent
                final Intent intent = new Intent(currentContext, postcard.getDestination());
                intent.putExtras(postcard.getExtras());
                // 设置 flag
                // 设置 action
                ...
                // 放在主线程中执行
                runInMainThread(new Runnable() {
                    @Override
                    public void run() {
                        startActivity(requestCode, currentContext, intent, postcard, callback);
                    }
                });

                break;
            case BOARDCAST:
            case CONTENT_PROVIDER:
            case 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()));
                }
        }

        return null;
    }

_navigation 方法对各种类型进行了判断,如果是 activity 的话就组装了 intent,并在调用了 startActivity 方法。

    private void startActivity(int requestCode, Context currentContext, Intent intent, Postcard postcard, NavigationCallback callback) {
        // 调用 startActivity
        if (requestCode >= 0) {  // 根据是否设置了 requestCode 来判断需要调用的方法
            if (currentContext instanceof Activity) {
                ActivityCompat.startActivityForResult((Activity) currentContext, intent, requestCode, postcard.getOptionsBundle());
            } else {
                logger.warning(Consts.TAG, "Must use [navigation(activity, ...)] to support [startActivityForResult]");
            }
        } else {
            ActivityCompat.startActivity(currentContext, intent, postcard.getOptionsBundle());
        }

        // 设置动画
        if ((-1 != postcard.getEnterAnim() && -1 != postcard.getExitAnim()) && currentContext instanceof Activity) {    // Old version.
            ((Activity) currentContext).overridePendingTransition(postcard.getEnterAnim(), postcard.getExitAnim());
        }
        // 回调
        if (null != callback) { // Navigation over.
            callback.onArrival(postcard);
        }
    }

在这里我们终于看到了熟悉的 startActivity 方法,至此一次完整的 activity 跳转完成。

总结

经过上面的分析,一次 activity 跳转的过程已经很清晰了,总结一下可以分为下面几步:

  • 通过 build 生成跳转信息
  • LogisticsCenter.completion(postcard) 方法对跳转信息进行完善,找到最终要跳转的目标
  • 检查拦截器,并判断是否需要进行拦截
  • 完成跳转

其实分析过程中不难发现,一些其他的功能比如服务的生成,fragment,广播等的生成的过程和 activity 的跳转几乎是一样的。

以上就是本篇的全部内容,希望对你有帮助。


版权声明:本文为博主原创文章,转载请声明出处,请尊重别人的劳动成果,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值