源码分析 — ARouter路由框架

    • 一、ARouter整体结构
    • 二、ARouter初始化流程的源码分析
      • 1. ARouter初始化时序图
      • 2. ARouter初始化代码分析
    • 三、ARouter使用时常见的问题:
      • 3.1 ARouter拦截器优先级的问题
      • 3.2 Group分组的问题
    • 四、其他
    • 五、总结


一、ARouter整体结构

源码分析 — ARouter路由框架_第1张图片

主要的类:

类名 类的描述
ARouter ARouter facade(门面模式);
_ARouter ARouter core (Facade patten)
LogisticsCenter LogisticsCenter contain all of the map.
Postcard A container that contains the roadmap.
Warehouse Storage of route meta and other data.
ClassUtils 获取ARouter自动生成的代码文件

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

1. ARouter初始化时序图

源码分析 — ARouter路由框架_第2张图片

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

            // 遍历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 getFileNameByPackageName(Context context, final String packageName) throws PackageManager.NameNotFoundException, IOException, InterruptedException {
    // Set类型的数据结构,防止重复
    final Set classNames = new HashSet<>();

    List 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 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> groupsIndex = new HashMap<>();
    // LogisticsCenter.completion(postcard) 中被赋值
    static Map routes = new HashMap<>();

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


    /* 
     * Cache interceptor
     * LogisticsCenter.init() 中被赋值;
     * 此处拦截器的存储使用TreeMap,存储的时候,已经对拦截器的优先级进行排序
     */
    static Map> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");
    // InterceptorServiceImpl.init()中被赋值
    static List 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 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 resultMap = TextUtils.splitQueryParameters(rawUri);
            Map paramsType = routeMeta.getParamsType();

            if (MapUtils.isNotEmpty(paramsType)) {
                // Set value by its type, just for params which annotation by @Param
                for (Map.Entry 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 providerMeta = (Class) 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> entry : Warehouse.interceptorsIndex.entrySet()) {
                        Class 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> 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 接口;

你可能感兴趣的:(Android,源码分析,开源框架源码分析)