preface
I divide arouter into development phase, compilation phase and running phase, which correspond to the three dependent packages of arouter respectively
1, Development phase (arouter annotation)
Arouter annotation mainly defines annotations and related parameters for us to use
2, Compilation phase (arouter compiler)
Arouter compiler scans code that uses annotations and converts it into java code through javapoet
The core code is in the Process folder, and the corresponding class handles the processing of several types of annotations respectively
What is javapoet? Javapool is an open source java code generation framework launched by square, which provides Java Api to generate. Java source files. ARouter uses javapoet to generate various routing files. In this way, you can scan the path where the routing file is located, obtain the class name of the routing file, and call relevant methods to initialize the routing table.
3, Runtime phase (arouter API)
-
ARouter.build(path) to build Postcard
-
LogisticsCenter
LogisticsCenter.completion(postcard) improves the information of postcard and completes the conversion relationship from path to activity (mapping of name and class files).
-
Postcard.navigation() implements jump
-
Warehouse data warehouse, storage mapping relationship to map
/** * Storage of route meta and other data. * * @author zhilong <a href="mailto:zhilong.lzl@alibaba-inc.com">Contact me.</a> * @version 1.0 * @since 2017/2/23 1:39 PM */ 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(); } }
The map of the interceptor is somewhat special, especially the UniqueKeyTreeMap. In fact, it is very simple. It only throws the specified exception when the key (priority) is the same. Therefore, remember not to have interceptors with the same priority.
4, Run accelerator (arouter register)
There are defects in the running phase of ARouter. The defect lies in the process of getting this map. We need to initialize when using ARouter. What ARouter does is to use reflection scanning to specify all classnames under the package name during initialization, and then add them to the map.We analyze the source code of LogisticsCenter.init
1. When opening for the first time, all classnames under the corresponding package will be scanned through reflection by using ClassUtils.getFileNameByPackageName
2. After the initial scan, it will be stored in SharedPreferences so that subsequent scans are not required, which is also an optimization
The above two processes are time-consuming operations, that is, they may be slow when ARouter is opened for the first time (the second time will not be slow because there is sp cache)
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException { //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. //Reflection scanning corresponding package 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>())); } .... } }
Therefore, arouter provides arouter register plug-in to solve this problem.
Its basic principle is bytecode insertion pile. java files generated by gradle are inserted into the source code, which has solved the problem of reflection speed
//Source code, before inserting pile private static void loadRouterMap() { //registerByPlugin is always set to false registerByPlugin = false; } //Decompile code after pile insertion private static void loadRouterMap() { registerByPlugin = false; register("com.alibaba.android.arouter.routes.ARouter$$Root$$modulejava"); register("com.alibaba.android.arouter.routes.ARouter$$Root$$modulekotlin"); register("com.alibaba.android.arouter.routes.ARouter$$Root$$arouterapi"); register("com.alibaba.android.arouter.routes.ARouter$$Interceptors$$modulejava"); register("com.alibaba.android.arouter.routes.ARouter$$Providers$$modulejava"); register("com.alibaba.android.arouter.routes.ARouter$$Providers$$modulekotlin"); register("com.alibaba.android.arouter.routes.ARouter$$Providers$$arouterapi"); }