源码分析 — ARouter路由框架


一、ARouter整体结构

这里写图片描述

主要的类:

类名类的描述
ARouterARouter facade(门面模式);
_ARouterARouter core (Facade patten)
LogisticsCenterLogisticsCenter contain all of the map.
PostcardA container that contains the roadmap.
WarehouseStorage of route meta and other data.
ClassUtils获取ARouter自动生成的代码文件

二、ARouter初始化流程的源码分析

1. ARouter初始化时序图

这里写图片描述

2. ARouter初始化代码分析

在Application中初始化代码:ARouter.init(application)

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

        if (hasInit) {
            // 启动InterceptorService
            _ARouter.afterInit();
        }

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

// _ARouter.class
protected static synchronized boolean init(Application application) {
    mContext = application;
    // LogisticsCenter的初始化
    LogisticsCenter.init(mContext, executor);
    logger.info(Consts.TAG, "ARouter init success!");
    hasInit = true;
    return true;
}

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

    try {
        loadRouterMap();
        if (registerByPlugin) {
            logger.info(TAG, "Load router map by arouter-auto-register plugin.");
        } else {
            Set<String> routerMap;

            // 如果是Debug模式,则执行从if代码块;
            // 如果是Release模式:通过PackageUtils.isNewVersion(context)判断当前应用是否是第一次启动;
            if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
                // 获取arouter-compiler生成的文件
                routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
                // 存储在sp中,下次启动应用的时候,直接从sp缓存中读取
                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 {
                // 从sp缓存中读取arouter-compiler生成的文件
                routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
            }

            // 遍历arouter-compiler生成的文件,将他们按照类型分别存储到Warehouse的对应字段中
            for (String className : routerMap) {
                // 类名前缀为com.alibaba.android.arouter.routes.ARouter$$Root的文件
                if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
                    // 将类名前缀为com.alibaba.android.arouter.routes.ARouter$$Group的文件添加进Warehouse.groupsIndex中
                    ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);

                } 
                // 类名前缀为com.alibaba.android.arouter.routes.ARouter$$Interceptors的文件
                else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {

                    ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
                }
                // 类名前缀为com.alibaba.android.arouter.routes.ARouter$$Providers的文件
                else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {

                    ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
                }
            }
        }
        //...代码省略...
    } catch (Exception e) {
        throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
    }
}

LogisticsCenter.init() 小结:

  1. 查找指定包名(com.alibaba.android.arouter)下的文件,并将之缓存到SP中;
  2. 按照文件的前缀不同,将他们添加到路由映射表WarehousegroupsIndex、interceptorsIndex、providersIndex 中;

PackageUtils 类:用来管理应用的版本

// PackageUtils .class
// 判断当前版本是否是新版本(第一次启动)
public static boolean isNewVersion(Context context) {
    //...代码省略...
    SharedPreferences sp = context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE);
    // 从sp文件中获取之前存储的VersionName和VersionCode
    if (!versionName.equals(sp.getString(LAST_VERSION_NAME, null)) || versionCode != sp.getInt(LAST_VERSION_CODE, -1)) {
        //...代码省略...
        return true;
    } else {
        return false;
    }
}

// 保存当前版本的VersionName和VersionCode
public static void updateVersion(Context context) {
    if (TextUtils.isEmpty(NEW_VERSION_NAME) && NEW_VERSION_CODE != 0) {
        SharedPreferences sp = context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE);
        sp.edit()
          .putString(LAST_VERSION_NAME, NEW_VERSION_NAME)
          .putInt(LAST_VERSION_CODE, NEW_VERSION_CODE)
          .apply();
    }
}

ClassUtils 类:获取com.alibaba.android.arouter.routes包下 的文件:

// ClassUtils.class
// 通过指定包名,扫描包下面包含的所有的ClassName
public static Set<String> getFileNameByPackageName(Context context, final String packageName) throws PackageManager.NameNotFoundException, IOException, InterruptedException {
    // Set类型的数据结构,防止重复
    final Set<String> classNames = new HashSet<>();

    List<String> paths = getSourcePaths(context);
    final CountDownLatch parserCtl = new CountDownLatch(paths.size());

    for (final String path : paths) {
        DefaultPoolExecutor.getInstance().execute(new Runnable() {
            @Override
            public void run() {
                DexFile dexfile = null;
                try {
                    if (path.endsWith(EXTRACTED_SUFFIX)) {
                        //NOT use new DexFile(path), because it will throw "permission error in /data/dalvik-cache"
                        dexfile = DexFile.loadDex(path, path + ".tmp", 0);
                    } else {
                        dexfile = new DexFile(path);
                    }
                    // 从DexFile中获取所有的文件
                    Enumeration<String> dexEntries = dexfile.entries();
                    while (dexEntries.hasMoreElements()) {
                        String className = dexEntries.nextElement();
                        // 匹配所有包名路径为com.alibaba.android.arouter.routes的文件
                        if (className.startsWith(packageName)) {
                            classNames.add(className);
                        }
                    }
                } catch (Throwable ignore) {
                    Log.e("ARouter", "Scan map file in dex files made error.", ignore);
                } finally {
                    //...代码省略...
                }
            }
        });
    }
    //...代码省略...
    return classNames;// 返回所有包名路径为com.alibaba.android.arouter.routes的文件集合
}

Warehouse类:路由文件映射表

class Warehouse {
    // Cache route and metas
    // LogisticsCenter.init() 中被赋值
    static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
    // LogisticsCenter.completion(postcard) 中被赋值
    static Map<String, RouteMeta> routes = new HashMap<>();

    // Cache provider
    // LogisticsCenter.completion(postcard) 中被赋值
    static Map<Class, IProvider> providers = new HashMap<>();
    // LogisticsCenter.init() 中被赋值
    static Map<String, RouteMeta> providersIndex = new HashMap<>();


    /* 
     * Cache interceptor
     * LogisticsCenter.init() 中被赋值;
     * 此处拦截器的存储使用TreeMap,存储的时候,已经对拦截器的优先级进行排序
     */
    static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");
    // InterceptorServiceImpl.init()中被赋值
    static List<IInterceptor> interceptors = new ArrayList<>();
}

至此,_ARouter.init(application)的整个流程结束;

下面开始分析_ARouter.afterInit()代码:

// _ARouter.class
static void afterInit() {
    // Trigger interceptor init, use byName.
    interceptorService = (InterceptorService) 
            ARouter.getInstance()
                .build("/arouter/service/interceptor") //生成一个Postcard对象
                //这个navigation()经过多次调用之后,
                //最终调用的是_ARouter.navigation(context, postcard, requestCode, navigationCallback)方法
                .navigation();
}

build("/arouter/service/interceptor") 代码分析:

// _ARouter.class
protected Postcard build(String path) {
    // navigation(clazz)这种方式是属于根据类型查找,而build(path)是根据名称进行查找
    // 如果应用中没有实现PathReplaceService这个接口,则pService=null
    PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
   if (null != pService) {
        // PathReplaceService可以对所有的路径进行预处理,然后返回一个新的值
        path = pService.forString(path);
    }
    //extractGroup(path)是获取分组名称,默认为path中第一部分;
    return build(path, extractGroup(path));
}

// _ARouter.class
protected Postcard build(String path, String group) {
    PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
    if (null != pService) {
        path = pService.forString(path);
    }
    // 返回含有path和group组别的Postcard
    return new Postcard(path, group);
}

public interface PathReplaceService extends IProvider {
    String forString(String path);
    Uri forUri(Uri uri);
}

navigation() 代码分析:

// _ARouter.class
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    try {
        // 重点:分析部分在下面
        // 1. 将IProvider映射到`Warehouse.providers`中;
        // 2. 将添加@Route注解的类映射到`Warehouse.routes`中
        LogisticsCenter.completion(postcard);
    } catch (NoRouteFoundException ex) {
        // ...代码省略...

        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);
    }
    // 忽略拦截器(在LogisticsCenter.completion(postcard)中,PROVIDER和FRAGMENT是忽略拦截器的)
    if (!postcard.isGreenChannel()) {   // It must be run in async thread, maybe interceptor cost too mush time made ANR.

        // interceptorService对象就是通过_ARouter.afterInit()实例化的;
        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;
}

// _ARouter.class
private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    // 这里如果navigation()不传入Activity作为context,则使用Application作为context
    final Context currentContext = null == context ? mContext : context;

    switch (postcard.getType()) {
        case ACTIVITY:
            // Build intent
            final Intent intent = new Intent(currentContext, postcard.getDestination());
            // Activity的参数传递
            intent.putExtras(postcard.getExtras());

            // Set flags.
            int flags = postcard.getFlags();
            if (-1 != flags) {// 如果在跳转时,设置了flags,且没有设置Activity作为context,则下面的startActivity()方法会发生错误,因为缺少Activity的Task栈;
                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 {
                        // Activity跳转
                        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:
            //在LogisticsCenter.completion(postcard)中,我们将IProvider对象设置进postcard中,所以可以通过postcard.getProvider()获取;
            return postcard.getProvider();
        case BOARDCAST:
        case CONTENT_PROVIDER:
        case FRAGMENT:
            Class fragmentMeta = postcard.getDestination();
            try {
                Object instance = fragmentMeta.getConstructor().newInstance();
                // Fragment的参数传递
                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;
}


LogisticsCenter.completion() 小结:

  1. 将IProvider映射到Warehouse.providers中;
  2. 将添加@Route注解的类映射到Warehouse.routes中;
// LogisticsCenter.class
public synchronized static void completion(Postcard postcard) {
   if (null == postcard) {
        throw new NoRouteFoundException(TAG + "No postcard!");
    }

    /*
     * 根据path在Warehouse.routes映射表中查找对应的RouteMeta;
     * 
     * 如果是第一次启动,要查找path = "/arouter/service/interceptor",
     * 而此时Warehouse.routes里面是没有元素的(在LogisticsCenter.init()中,只有`Warehouse`的`groupsIndex、interceptorsIndex、providersIndex` 有数据),因此routeMeta=null
     */
    RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
    if (null == routeMeta) {
        // 从Warehouse.groupsIndex中取出类名前缀为com.alibaba.android.arouter.routes.ARouter$$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 {
                IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
                // 将我们添加@Route注解的类映射到Warehouse.routes中;
                iGroupInstance.loadInto(Warehouse.routes);
                // 将已经加载过的组从Warehouse.groupsIndex中移除,避免重复添加进Warehouse.routes
                Warehouse.groupsIndex.remove(postcard.getGroup());

            } catch (Exception e) {
                throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
            }
            // 这个时候Warehouse.routes已经有值了,所以重新调用本方法执行else代码块
            completion(postcard);   // Reload
        }
    } else {
        postcard.setDestination(routeMeta.getDestination());
        postcard.setType(routeMeta.getType());//设置routeMeta的Type
        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 be implememt 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();
                        // IProvider的子类进行初始化
                        provider.init(mContext);
                        // 将IProvider的子类添加进Warehouse.providers中
                        Warehouse.providers.put(providerMeta, provider);
                        instance = provider;
                    } catch (Exception e) {
                        throw new HandlerException("Init provider failed! " + e.getMessage());
                    }
                }
                // 将IProvider的实现类保存在postcard中,因此可以从postcard获取IProvider的实例对象;
                postcard.setProvider(instance);
                // greenChannel()会忽略拦截器
                postcard.greenChannel();    // Provider should skip all of interceptors
                break;
            case FRAGMENT:
                // greenChannel()会忽略拦截器
                postcard.greenChannel();    // Fragment needn't interceptors
            default:
                break;
        }
    }
}

到这里,我们看到了Warehouse.providers、Warehouse.routes的赋值;那么Warehouse.interceptors又是在哪里赋值的呢?

InterceptorServiceImpl 的初始化:

// InterceptorService是IProvider的子类
public interface InterceptorService extends IProvider {
    void doInterceptions(Postcard postcard, InterceptorCallback callback);
}
/*
 * 因此InterceptorServiceImpl也是IProvider的子类;
 * 在LogisticsCenter.completion()中,有provider.init(context)的初始化;
 */
public class InterceptorServiceImpl implements InterceptorService {

    // ...代码省略...

    @Override
    public void init(final Context context) {
        LogisticsCenter.executor.execute(new Runnable() {
            @Override
            public void run() {
                if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
                    for (Map.Entry<Integer, Class<? extends IInterceptor>> entry : Warehouse.interceptorsIndex.entrySet()) {
                        Class<? extends IInterceptor> interceptorClass = entry.getValue();
                        try {
                            IInterceptor iInterceptor = interceptorClass.getConstructor().newInstance();
                            // 具体的拦截器的初始化
                            iInterceptor.init(context);
                            // 将拦截器添加到Warehouse.interceptors映射表中
                            Warehouse.interceptors.add(iInterceptor);
                        } catch (Exception ex) {
                            throw new HandlerException(TAG + "ARouter init interceptor error! name = [" + interceptorClass.getName() + "], reason = [" + ex.getMessage() + "]");
                        }
                    }
                    // ...代码省略...
                }
            }
        });
    }
}

至此,整个路由的初始化已经结束;


三、ARouter使用时常见的问题:

3.1 ARouter拦截器优先级的问题


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

public class UniqueKeyTreeMap<K, V> extends TreeMap<K, V> {
    private String tipText;

    public UniqueKeyTreeMap(String exceptionText) {
        super();
        tipText = exceptionText;
    }

    @Override
    public V put(K key, V value) {
        /*
         * 将拦截器添加进UniqueKeyTreeMap时,以优先级priority作为键,
         * 所以应用中拦截器的优先级不能重复,否则会抛出异常;
         */
        if (containsKey(key)) {
            throw new RuntimeException(String.format(tipText, key));
        } else {
            return super.put(key, value);
        }
    }
}

小结:

将拦截器添加进UniqueKeyTreeMap时,以优先级priority作为键,所以应用中拦截器的优先级不能重复,否则会抛出异常;


3.2 Group分组的问题

在使用@Route(path= "/main/index") 进行路由标注时,会默认使用第一部分的 /main 作为分组;在 build 应用后,会生成对应的 ARouter$$Group$$main.java 文件;

注意:

当一个 Module 使用过 /main 组别后,其他 Module 不能再使用此作为组别,否则会出现无法找到指定文件的错误;

原因:

因为在 Warehouse 类中,有一个 Map 类型的字段 interceptorsIndex,他是以组别作为Key (例如上面提到的/main),value则是 ARouter$$Group$$main.java ,所以当不同的 Module 中存在相同的组别(如 /main)时,由于Map键的唯一性,因此Warehouse.interceptorsIndex 也只能存入其中一个ARouter$$Group$$main.java


四、其他

Activity跳转的原理:

  1. 从DexFile文件中查找到被@Route注解修饰的类;
  2. 存储在Warehouse中;
  3. 在跳转的时候,将所需查找的Class赋值给Postcard对象;
  4. 最后使用的时候从postcard的Destination中获取需跳转的Class对象;
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
            // 这里从postcard的Destination中获取需跳转的Class对象
            final Intent intent = new Intent(currentContext, postcard.getDestination());
            intent.putExtras(postcard.getExtras());        

            // ...代码省略...
            ActivityCompat.startActivity(currentContext, intent, postcard.getOptionsBundle());

            //...代码省略...
    }
    return null;
}

// 另一种方式:通过类名直接跳转
try {
    // GoodsQueryActivity.class为KitBundle组件中的页面,当前组件为MainBundle组件;通过类名查找到对应的Class,也能进行页面跳转;
    Class clazz = Class.forName("com.elson.kitbundle.ui.GoodsQueryActivity");
    Intent intent = new Intent(this, clazz);
    startActivity(intent);
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

五、总结

  1. 由于拦截器的优先级具有唯一性(数据结构决定的),当在组件化开发的时候,不同组件如果都设置了拦截器的优先级,那么可能出现优先级重复的问题;

  2. 关于路由启动Activity,在启动Activity的时候添加了withFlags(),如果 navigation() 方法没有传入 Activity作为 Context,那默认使用Application作为Context;此时会出现异常,因为用Application启动的Activity,都需要添加一个Activity栈来管理;

  3. 关于拦截器:无法拦截 IProvider 的子类和Fragment,可在 LogisticsCenter.completion() 中找到答案;

  4. 在跳转Activity时,使用了 Postcard.withObject() 方法,则必须实现 SerializationService 接口;

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值