敞开成长之旅!这是我参加「日新计划 12 月更文应战」的第23天,点击检查活动详情

这篇内容是曾经在CSDN更新过的,由于CSDN用的少了就干脆转移到上来,内容或许会有一些收支,还请各位看官指正。

1.编译

Arouter在编译器的首要作业便是生成中间件的代码,在gradle中加入Arouter的依靠后在编译的时分就会在对应的module下增加com.alibaba.android.arouter.routes目录,这个目录中首要寄存Arouter生成的文件,比方

public class ARouter$$Root$$app implements IRouteRoot {
  @Override
  public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
    //routes是一个map类型,key值便是path途径中的group,value值便是下面的那个文件
    routes.put("app", ARouter$$Group$$app.class);
  }
}
public class ARouter$$Group$$app implements IRouteGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> atlas) {
    //key是通过@Route装备的途径,value是保存了发动的组件类型以及对应的文件
    atlas.put("/app/LoginActivity", RouteMeta.build(RouteType.ACTIVITY, LoginActivity.class, "/app/loginactivity", "app", new java.util.HashMap<String, Integer>(){{put("path", 8); }}, -1, -2147483648));
    atlas.put("/app/MainActivity", RouteMeta.build(RouteType.ACTIVITY, MainActivity.class, "/app/mainactivity", "app", null, -1, -2147483648));
    atlas.put("/app/SecondActivity", RouteMeta.build(RouteType.ACTIVITY, SecondActivity.class, "/app/secondactivity", "app", null, -1, -2147483648));
    atlas.put("/app/ThirdActivity", RouteMeta.build(RouteType.ACTIVITY, ThirdActivity.class, "/app/thirdactivity", "app", new java.util.HashMap<String, Integer>(){{put("name", 8); put("hero", 0); put("age", 3); }}, -1, 0));
  }
}
/**
 * For versions of 'compiler' greater than 1.0.7
 *
 * @param type        type				类型(Activity、Service等)
 * @param destination destination		详细类(MainActivity.class)
 * @param path        path				途径(app/MainActivity)
 * @param group       group				app
 * @param paramsType  paramsType		null
 * @param priority    priority
 * @param extra       extra
 * @return this
 */
public static RouteMeta build(RouteType type, Class<?> destination, String path, String group, Map<String, Integer> paramsType, int priority, int extra) {
    return new RouteMeta(type, null, destination, path, group, paramsType, priority, extra);
}

由于编译生成的类的前缀都是 Arouter,所以moudle下的完好包名便是com.alibaba.android.arouter.routes.Arouter

2.运行

  • 运行时首要作业便是注入,从Application中的Arouter初始化看起
ARouter.init(this);
  • init最终是调用到了这儿
//_Arouter#init
protected static synchronized boolean init(Application application) {
    mContext = application;
    LogisticsCenter.init(mContext, executor);
    logger.info(Consts.TAG, "ARouter init success!");
    hasInit = true;
    return true;
}
  • 初始化的init办法中又调用了LogisticsCenter
//LogisticsCenter#init
//第一个参数context是Application它来自Arouter.init
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;
            // 这儿做了是否是debug或者是否是新版别的判别,假如成果为true就需要从头获取包下面一切的class
            if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
                logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
                // 这儿会获取com.alibaba.android.arouter.routes目录下的一切class文件
                routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
                if (!routerMap.isEmpty()) {
                    //获取到class后将其存储到SharedPreferences中
                    context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
                }
                //router保存完结时还要保存最新的版别
                PackageUtils.updateVersion(context);    
            } else {
                logger.info(TAG, "Load router map from cache.");
                //不是新的版别或者没有debug时可直接从SharedPreference中获取一切的class
                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();
            //遍历获取到的class文件名,然后对不同类名的前缀做比照后通过loadInto注入到Warehouse
            for (String className : routerMap) {
                //com.alibaba.android.arouter.routes.Arouter$$Root
                if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
                    ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
                } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
                    //com.alibaba.android.arouter.routes.Arouter$$Interceptors
                    ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
                } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
                    //com.alibaba.android.arouter.routes.Arouter$$Provider
                    ((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() + "]");
    }
}
  • LogisticsCenter办法的流程如下:
    • 判别是否是新版别或者是debug模式
    • 假如判别的条件成立就调用ClassUtils.getFileNameByPackageName办法从头获取className然后加入到SharedPreference中缓存
    • 假如条件不成立就直接从SharedPreference获取
    • 遍历获取到的ClassName,然后依据不同类名的前缀做比照后通过loadinto办法注入到Warehouse
//ClassUtils#getFileNameByPackageName
public static Set<String> getFileNameByPackageName(Context context, final String packageName) throws PackageManager.NameNotFoundException, IOException, InterruptedException {
    final Set<String> classNames = new HashSet<>();
    //获取一切dex文件的途径
    List<String> paths = getSourcePaths(context);
    final CountDownLatch parserCtl = new CountDownLatch(paths.size());
    //遍历获取到的dex文件的途径
    for (final String path : paths) {
        DefaultPoolExecutor.getInstance().execute(new Runnable() {
            @Override
            public void run() {
                DexFile dexfile = null;
                try {
                    if (path.endsWith(EXTRACTED_SUFFIX)) {
                        //假如是.zip结尾的途径就用loadDex加载,假如创立新的DexFile就会报出权限过错
                        dexfile = DexFile.loadDex(path, path + ".tmp", 0);
                    } else {
                        //创立新的DexFile
                        dexfile = new DexFile(path);
                    }
                    Enumeration<String> dexEntries = dexfile.entries();
                    //遍历dexFile下面的元素
                    while (dexEntries.hasMoreElements()) {
                        String className = dexEntries.nextElement();
                        if (className.startsWith(packageName)) {
                            //将开头为"com.alibaba.android.arouter.routes"的className增加到classNames集合中
                            classNames.add(className);
                        }
                    }
                } catch (Throwable ignore) {
                    Log.e("ARouter", "Scan map file in dex files made error.", ignore);
                } finally {
                    if (null != dexfile) {
                        try {
                            dexfile.close();
                        } catch (Throwable ignore) {
                        }
                    }
                    parserCtl.countDown();
                }
            }
        });
    }
    parserCtl.await();
    Log.d(Consts.TAG, "Filter " + classNames.size() + " classes by packageName <" + packageName + ">");
    return classNames;
}
  • 获取classNames的流程如下:
    • 获取dexFile途径
    • 遍历dexFile途径以及下面的元素
    • 假如是以com.alibaba.android.arouter.routes开头则增加到ClassNames集合中
//ClassUtils#getSourcePaths
public static List<String> getSourcePaths(Context context) throws PackageManager.NameNotFoundException, IOException {
    ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);
    File sourceApk = new File(applicationInfo.sourceDir);
    List<String> sourcePaths = new ArrayList<>();
    //增加默认的apk途径
    sourcePaths.add(applicationInfo.sourceDir); 
    //the prefix of extracted file, ie: test.classes
    String extractedFilePrefix = sourceApk.getName() + EXTRACTED_NAME_EXT;
    //假如VM已经支撑了MultiDex,就不要去Secondary Folder加载 Classesx.zip了,那里已经么有了
    //通过是否存在sp中的multidex.version是不精确的,由于从低版别升级上来的用户,是包括这个sp装备的
    //假如已经支撑了MutilDex
    if (!isVMMultidexCapable()) {
        //获取一切dex文件的数量
        int totalDexNumber = getMultiDexPreferences(context).getInt(KEY_DEX_NUMBER, 1);
        File dexDir = new File(applicationInfo.dataDir, SECONDARY_FOLDER_NAME);
        //遍历获取途径
        for (int secondaryNumber = 2; secondaryNumber <= totalDexNumber; secondaryNumber++) {
            //for each dex file, ie: test.classes2.zip, test.classes3.zip...
            String fileName = extractedFilePrefix + secondaryNumber + EXTRACTED_SUFFIX;
            File extractedFile = new File(dexDir, fileName);
            if (extractedFile.isFile()) {
                //增加途径
                sourcePaths.add(extractedFile.getAbsolutePath());
                //we ignore the verify zip part
            } else {
                throw new IOException("Missing extracted secondary dex file '" + extractedFile.getPath() + "'");
            }
        }
    }
    if (ARouter.debuggable()) { // Search instant run support only debuggable
        sourcePaths.addAll(tryLoadInstantRunDexFile(applicationInfo));
    }
    return sourcePaths;
}
  • 获取dex文件途径的流程如下:
    • 增加apk默认途径
    • 是否支撑MutilDex
      • 假如支撑则先获取dex文件的总数
      • 然后开始遍历获取途径
      • 增加到sourcePaths集合中
class Warehouse {
    // Cache route and metas
    static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
    static Map<String, RouteMeta> routes = new HashMap<>();
    // Cache provider
    static Map<Class, IProvider> providers = new HashMap<>();
    static Map<String, RouteMeta> providersIndex = new HashMap<>();
    // Cache interceptor
    static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");
    static List<IInterceptor> interceptors = new ArrayList<>();
    static void clear() {
        routes.clear();
        groupsIndex.clear();
        providers.clear();
        providersIndex.clear();
        interceptors.clear();
        interceptorsIndex.clear();
    }
}
  • Warehouse中包括三种缓存类型
  • Arouter在运行时的完好作业流程如下:
    • Application中初始化
    • 初始化时中先调用ClassUtils.getFileNameByPackageNam()获取一切com.alibaba.android.arouter.route目录下的dex文件途径
    • 遍历dex文件途径获取一切的class文件的完好类名
    • 遍历获取到的class,获取指定的前缀,调用loadinto办法依据不同前缀注入到Warehouse对应的成员变量中
    • 这其中就包括Arouter在编译时生成的com.alibaba.android.arouter.routes.Arouter$$Root.Arouter$$Root$$app

3.调用

  • 调用时的作业首要是获取,先看源码
 ARouter.getInstance().build(ActivityConsts.ACTIVITY_URL_THIRD)
                        .navigation(this, new CustomNavigationCallback()));
  • 直接从build看起
//_Arouter#build
public Postcard build(String path) {
    return _ARouter.getInstance().build(path);
}
//_Arouter#build
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);
        }
        //extractGroup获取到group,便是代码中的【app】
        return build(path, extractGroup(path));
    }
}
//_Arouter#extractGroup
private String extractGroup(String path) {
    //校验path格式是否正确
    if (TextUtils.isEmpty(path) || !path.startsWith("/")) {
        throw new HandlerException(Consts.TAG + "Extract the default group failed, the path must be start with '/' and contain more than 2 '/'!");
    }
    try {
        //通过截取path获取到group
        String defaultGroup = path.substring(1, path.indexOf("/", 1));
        if (TextUtils.isEmpty(defaultGroup)) {
            throw new HandlerException(Consts.TAG + "Extract the default group failed! There's nothing between 2 '/'!");
        } else {
            return defaultGroup;
        }
    } catch (Exception e) {
        logger.warning(Consts.TAG, "Failed to extract default group! " + e.getMessage());
        return null;
    }
}
//_Arouter#build
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);
        }
        //创立一个Postcard
        return new Postcard(path, group);
    }
}
public Postcard(String path, String group) {
    this(path, group, null, null);
}
public Postcard(String path, String group, Uri uri, Bundle bundle) {
    //保存途径
    setPath(path);
    //保存group,便是【app】
    setGroup(group);
    setUri(uri);
    //创立了个Bundle()
    this.mBundle = (null == bundle ? new Bundle() : bundle);
}
  • Arouter.getInstance().build()的流程用一句话总结便是,创立了一个Postcard保存了pathgroup
  • navigation的源码如下
public Object navigation(Context context, NavigationCallback callback) {
    return ARouter.getInstance().navigation(context, this, -1, callback);
}
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    try {
        //中心1
        LogisticsCenter.completion(postcard);
    } catch (NoRouteFoundException ex) {
        logger.warning(Consts.TAG, ex.getMessage());
        if (debuggable()) { // Show friendly tips for user.
            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);
    }
    //判别是否调用拦截器
    //要在异步线程中调用,否则或许会由于拦截器耗时过长出现ANR
    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);
                }
                logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());
            }
        });
    } else {
        //中心2
        return _navigation(context, postcard, requestCode, callback);
    }
    return null;
}
  • 中心1
//LogisticsCenter#completion
public synchronized static void completion(Postcard postcard) {
    if (null == postcard) {
        throw new NoRouteFoundException(TAG + "No postcard!");
    }
    //先从Warehouse.routes获取RouteMeta,上面的代码中只用到了Warehouse.groupsIndex,所以肯定是null
    RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
    if (null == routeMeta) {    
        //这儿通过group获取详细的class,而这个class便是前面编译时缓存进去的
        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 {
            //加载路由并缓存到内存中,然后将元数据删除,这儿的元数据指的是group
            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();
                //通过反射将其存入到Warehouse中
                iGroupInstance.loadInto(Warehouse.routes);
                //移除group,group的意义便是用来获取route的
                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);   
        }
    } else {
        //第2次进入时到这儿
        //装备一些参数
        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 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();
                        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;
        }
    }
}
  • completion办法运行了两次:
    • 第一次从Warehouse.groupIndex中取出之前保存的数据,然后再通过loadinto把相关数据注入到Warehouse.routes
    • 第2次便是给Postcard中的属性赋值,type、Destination
  • 通过completion办法后Warehouse.route中便有了数据,而且参数postcard也有了typeDestinatio,就该履行第二个中心办法了
//_Arouter#_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类型便是创立了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);
            }
            // 切换到UI线程去发动Activity,Activity发动了
            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:
            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;
}
  • _navigation办法中首先依据postcard.type获取到详细类型,然后依据不同类型做相关处理,代码中我们运用的是Activity因而从源码中可知在Arouter中Activity的敞开也是根据Intent完结的