ARouter源码解析(二)api模块

ARouter源码解析(二)api模块

一、基本类

@Autowired,@Interceptor,@Route注释

RouteType, TypeKind,RouteMeta,TypeWrapper

PostCard

路由信息的容器,包含一次页面跳转的所有信息

二、Arouter.init()

ARouter.init(mApplication)

//ARouter.java

public static void init(Application application) {
        if (!hasInit) {
            logger = _ARouter.logger;
            _ARouter.logger.info(Consts.TAG, "ARouter init start.");
            hasInit = _ARouter.init(application);

            if (hasInit) {
                _ARouter.afterInit();
            }

            _ARouter.logger.info(Consts.TAG, "ARouter init over.");
        }
    }

显然,ARouter类使用了代理模式,实际调用的是_ARouter的init方法

//_ARouter.java

protected static synchronized boolean init(Application application) {
        mContext = application;
        LogisticsCenter.init(mContext, executor);
        logger.info(Consts.TAG, "ARouter init success!");
        hasInit = true;
        mHandler = new Handler(Looper.getMainLooper());

        return true;
    }

这里的mHandler只有一个作用

private void runInMainThread(Runnable runnable) {
        if (Looper.getMainLooper().getThread() != Thread.currentThread()) {
            mHandler.post(runnable);
        } else {
            runnable.run();
        }
    }

最主要的是调用了LogisticsCenter的init方法,这个方法完成后,在warehouse就会存有定义的路由关系

public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
        mContext = context;
        executor = tpe;

        try {
            long startInit = System.currentTimeMillis();
            //billy.qi modified at 2017-12-06
            //load by plugin first
            loadRouterMap();
            if (registerByPlugin) {
                logger.info(TAG, "Load router map by arouter-auto-register plugin.");
            } else {
                Set<String> routerMap;

                // It will rebuild router map every times when debuggable.
                if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
                    logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
                    // These class was generated by 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);    // Save new version name when router map update finishes.
                } 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>()));
                }

                logger.info(TAG, "Find router map finished, map size = " + routerMap.size() + ", cost " + (System.currentTimeMillis() - startInit) + " ms.");
                startInit = System.currentTimeMillis();

                for (String className : routerMap) {
                    if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
                        // This one of root elements, load 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);
                    }
                }
            }

            logger.info(TAG, "Load root element finished, cost " + (System.currentTimeMillis() - startInit) + " ms.");

            if (Warehouse.groupsIndex.size() == 0) {
                logger.error(TAG, "No mapping files were found, check your configuration please!");
            }

            if (ARouter.debuggable()) {
                logger.debug(TAG, String.format(Locale.getDefault(), "LogisticsCenter has already been loaded, GroupIndex[%d], InterceptorIndex[%d], ProviderIndex[%d]", Warehouse.groupsIndex.size(), Warehouse.interceptorsIndex.size(), Warehouse.providersIndex.size()));
            }
        } catch (Exception e) {
            throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
        }
    }

这段代码很长,我们逐段来分析

首先是loadRouterMap();这个方法

    /**
     * arouter-auto-register plugin will generate code inside this method
     * call this method to register all Routers, Interceptors and Providers
     * @author billy.qi Contact me.
     * @since 2017-12-06
     */
    private static void loadRouterMap() {
        registerByPlugin = false;
        //auto generate register code by gradle plugin: arouter-auto-register
        // looks like below:
        // registerRouteRoot(new ARouter..Root..modulejava());
        // registerRouteRoot(new ARouter..Root..modulekotlin());
    }

官方的注释已经很清楚了,这里可以使用ARouter官方推出的一个AndroidStudio插件,但这里没有使用所以直接把registerByPlugin赋值为了false,目前这个项目还在维护,可能是官方接下来要在这里有所改动,我们不用理会

再回到init()方法上来,由于registerByPlugin直接赋值为了false,所以直接进入到了else块

    Set<String> routerMap;

    // It will rebuild router map every times when debuggable.
    if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
        logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
        // These class was generated by 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);    // Save new version name when router map update finishes.
    } 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>()));
    }

routerMap 存的是各个帮助类的类名

这段if else判断的意思是,如果是处于开发环境或者是新版本,则重新扫描一次配置文件,以保证获得的routerMap含有最新的帮助类信息,否则就直接从缓存中读取,以加快初始化速度。

关于如何扫描配置文件获得帮助类的类名,这里不做深究,有兴趣的同学可以自行阅读源码

接着来看下一段代码

startInit = System.currentTimeMillis();

for (String className : routerMap) {
    if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
        // This one of root elements, load 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);
    }
}

注:

  • ROUTE_ROOT_PAKCAGE = “com.alibaba.android.arouter.routes”;
  • DOT = “.”;
  • SDK_NAME = “ARouter”;
  • SEPARATOR = “$$”;
  • SUFFIX_ROOT = “Root”;
  • SUFFIX_INTERCEPTORS = “Interceptors”;
  • SUFFIX_PROVIDERS = “Providers”;

所以其实就分成了三类:

  • 以 com.alibaba.android.arouter.routes.Arouter&&Root 开头的根节点帮助类
  • 以 com.alibaba.android.arouter.routes.Arouter&&Interceptors 开头的拦截器帮助类
  • 以 com.alibaba.android.arouter.routes.Arouter&&Providers 开头的服务帮助类

然后这里使用了反射去获得这三种帮助类的实例,分别调用了它们的loadInto方法,三个方法的参数都是Warehouse类里的静态变量,不难想到```Warehouse``这个类其实是用来储存路由信息的。

我们来看看loadInto方法中做了什么

首先是根节点帮助类的loadInto方法,它传入的参数是Warehouse.groupsIndex

public class ARouter$$Root$$app implements IRouteRoot {
  @Override
  public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
    routes.put("service", ARouter$$Group$$service.class);
    routes.put("test", ARouter$$Group$$test.class);
  }
}

前面说了,ARouter采用了分组加载机制,这里实际上就是将各个组的名称和对应的帮助类的映射关系储存在Warehouse的goupIndex中

再来看看拦截器帮助类的loadInto方法,它传入的参数是Warehouse.interceptorsIndex

public class ARouter$$Interceptors$$app implements IInterceptorGroup {
  @Override
  public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) {
    interceptors.put(7, Test1Interceptor.class);
  }
}

与根节点帮助类类似,这里存放的是拦截器的优先级和对应的拦截器的映射关系,这里着重关注下传进来的Warehouse.interceptorsIndex参数,先看看它的实例

static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");

我们可以看到,它的实例是UniqueKeyTreeMap类型,这就保证了拦截器的优先级必须是唯一的,且调用顺序是有序的

最后再来看看服务帮助类的loadInto方法,它传入的参数是Warehouse.providersIndex

public class ARouter$$Providers$$app implements IProviderGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> providers) {
    providers.put("com.alibaba.android.arouter.demo.testservice.HelloService", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/service/hello", "service", null, -1, -2147483648));
    providers.put("com.alibaba.android.arouter.facade.service.SerializationService", RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, "/service/json", "service", null, -1, -2147483648));
    providers.put("com.alibaba.android.arouter.demo.testservice.SingleService", RouteMeta.build(RouteType.PROVIDER, SingleService.class, "/service/single", "service", null, -1, -2147483648));
  }
}

与上面两个帮助类类似,这里存入的是每一个服务的接口名和服务实现类的信息的映射关系。

Arouter.init()方法小结

这个方法主要完成了几件事情:

  • 将各个组的组名和帮助类之间的映射关系存到Warehouse.groupsIndex中
  • 将各个拦截器的优先级和拦截器的Class对象的映射关系存到Warehouse.interceptorsIndex中
  • 将各个服务的完整类名和RouteMeta之间的映射关系存到Warehouse.providersIndex中

三、 简单跳转

这里以ARouter.getInstance().build("/test/activity").navigation(); 做例子

首先最直观的就是这里使用了链式调用的设计

先来看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.init()方法进行初始化,没有就抛出异常,然后使用了单例模式,常见的双重校验写法。

接着来看Arouter中的.build()方法

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

这里依旧是代理模式,实际调用的是_Arouter的build(String path)方法

protected Postcard build(String path) {
        if (TextUtils.isEmpty(path)) {
            throw new HandlerException(Consts.TAG + "Parameter is invalid!");
        } else {
            PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
            if (null != pService) {
                path = pService.forString(path);
            }
            return build(path, extractGroup(path));
        }
    }

这个方法很简单,pService的相关部分就是实现上文提到的URL的功能

我们来关注一下ARouter.getInstance().navigation(PathReplaceService.class); ,我们发现这里实际上就是使用了上文提到的发现服务的功能,并且是byType类型,这也是为什么PathPreplaceService只能有一个实现

这里的extractGoup(path)方法实际上就是从path字符串中取出默认分组

接着来看_Arouter的build(String path, String group)方法

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);
        }
    }

我们可以看到,它们的实现基本是一样的,最终返回了一个Postcard的实例,Postcard就是一次路由信息的载体。

接着回到链式调用的最后一个部分.navigaton(),这个方法在PostCard类里

/**
     * Navigation to the route with path in postcard.
     * No param, will be use application context.
     */
    public Object navigation() {
        return navigation(null);
    }

    /**
     * Navigation to the route with path in postcard.
     *
     * @param context Activity and so on.
     */
    public Object navigation(Context context) {
        return navigation(context, null);
    }

    /**
     * Navigation to the route with path in postcard.
     *
     * @param context Activity and so on.
     */
    public Object navigation(Context context, NavigationCallback callback) {
        return ARouter.getInstance().navigation(context, this, -1, callback);
    }

这里关注下最后一个重载的方法,这个就是含回调的跳转要调用的方法

最终调用的还是Arouter里的navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback)

//ARouter.java

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

protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
        try {
            LogisticsCenter.completion(postcard);
        } catch (NoRouteFoundException ex) {
            logger.warning(Consts.TAG, ex.getMessage());

            if (debuggable()) {
                // Show friendly tips for user.
                runInMainThread(new Runnable() {
                    @Override
                    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 {    // 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);
        }

        if (!postcard.isGreenChannel()) {   // It must be run in async thread, maybe interceptor cost too mush time made ANR.
            interceptorService.doInterceptions(postcard, new InterceptorCallback() {
                /**
                 * Continue process
                 *
                 * @param postcard route meta
                 */
                @Override
                public void onContinue(Postcard postcard) {
                    _navigation(context, postcard, requestCode, callback);
                }

                /**
                 * Interrupt process, pipeline will be destory when this method called.
                 *
                 * @param exception Reson of interrupt.
                 */
                @Override
                public void onInterrupt(Throwable exception) {
                    if (null != callback) {
                        callback.onInterrupt(postcard);
                    }

                    logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());
                }
            });
        } else {
            return _navigation(context, postcard, requestCode, callback);
        }

        return null;
    }

这个方法虽然比较长,但是逻辑比较简单

先来看下最前面的LogisticsCenter.completion(postcard);方法,在调用这个方法之前,我们的postcard里只含有path和group的信息,而这个方法就是用来完善postcard中的其它路由信息。

public synchronized static void completion(Postcard postcard) {
        if (null == postcard) {
            throw new NoRouteFoundException(TAG + "No postcard!");
        }

        RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
        if (null == routeMeta) {    // Maybe its does't exist, or didn't load.
            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 {
                    if (ARouter.debuggable()) {
                        logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] starts loading, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
                    }

                    IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
                    iGroupInstance.loadInto(Warehouse.routes);
                    Warehouse.groupsIndex.remove(postcard.getGroup());

                    if (ARouter.debuggable()) {
                        logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] has already been loaded, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
                    }
                } catch (Exception e) {
                    throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
                }

                completion(postcard);   // Reload
            }
        } else {
            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.
                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());
            }

            switch (routeMeta.getType()) {
                case PROVIDER:  // if the route is provider, should find its instance
                    // Its provider, so it must implement IProvider
                    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;
                        try {
                            provider = providerMeta.getConstructor().newInstance();
                            provider.init(mContext);
                            Warehouse.providers.put(providerMeta, provider);
                            instance = provider;
                        } catch (Exception e) {
                            throw new HandlerException("Init provider failed! " + e.getMessage());
                        }
                    }
                    postcard.setProvider(instance);
                    postcard.greenChannel();    // Provider should skip all of interceptors
                    break;
                case FRAGMENT:
                    postcard.greenChannel();    // Fragment needn't interceptors
                default:
                    break;
            }
        }
    }

前文提过,ARouter的分组机制的加载实际上是使用了懒加载的机制,在一开始时ARouter只会将根节点的配置文件加载进来,各个组的配置文件不会被加载。

Warehouse.routes 里存放的就是已经被加载的分组

Warehouse.groupsIndex 里存放的就是还没有被加载的分组

先来看前半段代码

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 {
        // Load route and cache it into memory, then delete from metas.
        try {
            if (ARouter.debuggable()) {
                logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] starts loading, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
            }    

            IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
            iGroupInstance.loadInto(Warehouse.routes);
            Warehouse.groupsIndex.remove(postcard.getGroup());

            if (ARouter.debuggable()) {
                logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] has already been loaded, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
            }
        } catch (Exception e) {
            throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
        }

        completion(postcard);   // Reload
    }
} else {
    ...
}

这里先试图靠path从Warehouse.routes中获得代表路由目标(目标有可能是Activity,Fragment, Provider)的RouteMeta对象,如果返回null则说明这个目标所在的分组还没有被加载,这时候就会再试图根据目标所在的分组名称从Warehouse.groupIndex中获得还没有被加载的分组,如果还是没有找到则抛出异常,找到了就将这个分组加载进来。

加载的过程很简单,通过Class对象获得实例,调用loadInto方法把映射关系加载到Warehouse.routes中,然后从Warehouse.groupsIndex中将这个分组移除,代表这个分组已经被加载。

最后再重新调用completion方法,这个时候已经确保了Warehouse.routes中已经含有本次路由的目标。

这个时候就会进入到else块

if (...) {
    
}   else {
    postcard.setDestination(routeMeta.getDestination());//路由目标
    postcard.setType(routeMeta.getType());//路由
    postcard.setPriority(routeMeta.getPriority());//优先级
    postcard.setExtra(routeMeta.getExtra());//extra

    Uri rawUri = postcard.getUri();
    if (null != rawUri) {   // 如果是通过Uri跳转,则需要解析出Uri中的参数
        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());
}

Completion方法到这就结束了,接着继续回到_Arouternavigation方法,这里先对是否是GreenChannel,是的话就调用拦截器拦截,但无论是否经过拦截器,最后都是调用了_navigation()

		//回调callback的onFound
		if (null != callback) {
            callback.onFound(postcard);
        }

        if (!postcard.isGreenChannel()) {   // It must be run in async thread, maybe interceptor cost too mush time made ANR.
            interceptorService.doInterceptions(postcard, new InterceptorCallback() {
                /**
                 * Continue process
                 *
                 * @param postcard route meta
                 */
                @Override
                public void onContinue(Postcard postcard) {
                    _navigation(context, postcard, requestCode, callback);
                }

                /**
                 * Interrupt process, pipeline will be destory when this method called.
                 *
                 * @param exception Reson of interrupt.
                 */
                @Override
                public void onInterrupt(Throwable exception) {
                    if (null != callback) {
                        callback.onInterrupt(postcard);
                    }

                    logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());
                }
            });
        } else {
            return _navigation(context, postcard, requestCode, callback);
        }

        return null;

_navigation方法是路由跳转的最后一步,经过了上面的步骤,传进来的postcard参数中已经具有了完成一次跳转所需要的所有信息。

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,则通过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)) {   
                	//activity上下文之外调用startActivity需要FLAG_ACTIVITY_NEW_TASK属性
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                }

                // Set Actions
                String action = postcard.getAction();
                if (!TextUtils.isEmpty(action)) {
                    intent.setAction(action);
                }

                // Navigation in main looper.
                runInMainThread(new Runnable() {
                    @Override
                    public void run() {
                        startActivity(requestCode, currentContext, intent, postcard, callback);
                    }
                });

                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;
    }

你可能感兴趣的:(android)