阿里路由框架--ARouter 源码解析之初始化ARouter

来源:互联网 发布:d3.js 动态折线图 编辑:程序博客网 时间:2024/06/05 17:49

转载请注明出处:http://blog.csdn.net/crazy1235/article/details/77337691


    • 初始化ARouter
      • openLog
      • openDebug
      • init
      • LogisticsCenterinit
      • getFileNameByPackageName
      • getSourcePaths
      • tryLoadInstantRunDexFile
      • afterInit
    • 参考

上一篇介绍了ARouter的Compiler SDK : 阿里路由框架–ARouter 源码解析之Compiler

在编译阶段会根据注解生成相应的 java 类文件。

接下来分析一下 arouter-api SDK


先来看看目录结构:

这里写图片描述

这里写图片描述 这里写图片描述 这里写图片描述


初始化ARouter

ARouter.openLog();ARouter.openDebug();ARouter.init(getApplication());

openLog()

ARouter.java

public static synchronized void openLog() {        _ARouter.openLog();    }

_ARouter.java

static ILogger logger = new DefaultLogger(Consts.TAG);

ILogger是一个接口,定义了一些日志输出函数

public class DefaultLogger implements ILogger
static synchronized void openLog() {        logger.showLog(true);        logger.info(Consts.TAG, "ARouter openLog");    }

ARouter中的函数基本都是通过调用_ARouter类中的函数来实现的,这里应用了一种 外观模式


openDebug()

public static synchronized void openDebug() {        _ARouter.openDebug();    }
// //设置debug模式static synchronized void openDebug() {        debuggable = true;        logger.info(Consts.TAG, "ARouter openDebug");    }

init()

初始化ARouter

private volatile static boolean hasInit = false;
public static void init(Application application) {        if (!hasInit) {            logger = _ARouter.logger;            // log            _ARouter.logger.info(Consts.TAG, "ARouter init start.");            // 调用_ARouter的init函数            hasInit = _ARouter.init(application);            // 调用afterInit()            if (hasInit) {                _ARouter.afterInit();            }            _ARouter.logger.info(Consts.TAG, "ARouter init over.");        }    }

_ARouter.java

protected static synchronized boolean init(Application application) {        mContext = application;        // 初始化 “处理中心”        LogisticsCenter.init(mContext, executor);        logger.info(Consts.TAG, "ARouter init success!");        hasInit = true;        return true;    }

LogisticsCenter.init()

// 线程池执行对象private volatile static ThreadPoolExecutor executor = DefaultPoolExecutor.getInstance();
// compile编译阶段生成的类文件都在此目录下public static final String ROUTE_ROOT_PAKCAGE = "com.alibaba.android.arouter.routes";
    /**     *      */    public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {        mContext = context;        executor = tpe;        try {            // 根据报名加载下面所有的类文件(也就是ARouter编译生成的文件)            List<String> classFileNames = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);            // 遍历文件            for (String className : classFileNames) {                // 如果是 文件名是 com.alibaba.android.arouter.routes.ARouter$$Root 开头的文件,表示是一个Root类型的文件。                if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {                    // 创建Root类对象,并放入到WareHouse这个仓库中                    ((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);                }            }            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() + "]");        }    }

Warehouse 类是一个仓库,用来存储路由信息以及 provider , interceptor 等信息。


接下来重点看怎么获取ARouter compile生成的类文件

getFileNameByPackageName()

ARouter有如下特性:

  • 支持InstantRun

  • 支持MultiDex(Google方案)

所以下面这个函数就显得很重要!!!

    /**     * 通过指定包名,扫描包下面包含的所有的ClassName     *     * @param context     U know     * @param packageName 包名     * @return 所有class的集合     */    public static List<String> getFileNameByPackageName(Context context, String packageName) throws PackageManager.NameNotFoundException, IOException {        List<String> classNames = new ArrayList<>();        // 先去得到所有的dex文件,然后进行遍历        for (String path : getSourcePaths(context)) {            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);                }                Enumeration<String> dexEntries = dexfile.entries();                while (dexEntries.hasMoreElements()) {                    String className = dexEntries.nextElement();                    if (className.contains(packageName)) {                        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) {                    }                }            }        }        Log.d("ARouter", "Filter " + classNames.size() + " classes by packageName <" + packageName + ">");        return classNames;    }

getSourcePaths()

    /**     * get all the dex path     *     * @param context the application context     * @return all the dex path     * @throws PackageManager.NameNotFoundException     * @throws IOException     */    public static List<String> getSourcePaths(Context context) throws PackageManager.NameNotFoundException, IOException {        ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);        //    /data/app/com.alibaba.android.arouter.demo-2/base.apk        File sourceApk = new File(applicationInfo.sourceDir);        List<String> sourcePaths = new ArrayList<>();        sourcePaths.add(applicationInfo.sourceDir); //add the default apk path        //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配置的        if (!isVMMultidexCapable()) {            //the total dex numbers            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;    }

tryLoadInstantRunDexFile()

加载InstantRun 模式下的dex文件

/**     * Get instant run dex path, used to catch the branch usingApkSplits=false.     */    private static List<String> tryLoadInstantRunDexFile(ApplicationInfo applicationInfo) {        List<String> instantRunSourcePaths = new ArrayList<>();        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && null != applicationInfo.splitSourceDirs) {            // add the splite apk, normally for InstantRun, and newest version.            instantRunSourcePaths.addAll(Arrays.asList(applicationInfo.splitSourceDirs));            Log.d("ARouter", "Found InstantRun support");        } else {            try {                // This man is reflection from Google instant run sdk, he will tell me where the dex files go.                Class pathsByInstantRun = Class.forName("com.android.tools.fd.runtime.Paths");                Method getDexFileDirectory = pathsByInstantRun.getMethod("getDexFileDirectory", String.class);                String instantRunDexPath = (String) getDexFileDirectory.invoke(null, applicationInfo.packageName);                File instantRunFilePath = new File(instantRunDexPath);                if (instantRunFilePath.exists() && instantRunFilePath.isDirectory()) {                    File[] dexFile = instantRunFilePath.listFiles();                    for (File file : dexFile) {                        if (null != file && file.exists() && file.isFile() && file.getName().endsWith(".dex")) {                            instantRunSourcePaths.add(file.getAbsolutePath());                        }                    }                    Log.d("ARouter", "Found InstantRun support");                }            } catch (Exception e) {                Log.e("ARouter", "InstantRun support error, " + e.getMessage());            }        }        return instantRunSourcePaths;    }

afterInit()

static void afterInit() {        // Trigger interceptor init, use byName.        interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();    }

参考

http://blog.csdn.net/fei20121106/article/details/73743235