The principle of

The @route annotation in our code will generate some class files storing the path and activityClass mappings through APT at compile time. Then when the app process starts, it will get these class files and read the data storing these mappings into memory (stored in map). Class (activity.class = map.get(path))); And then new Intent(), when you call ARouter’s withString() method, it calls intent.putextra (String name, String Value) internally and calls navigation(). Its internal call startActivity(Intent) to jump, so that two modules that do not depend on each other can successfully start each other’s activities.

The source code

Explore the Android routing framework -ARouter source code (2)

# # #_ARouter init(a)
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {...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;

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

                logger.info(TAG, "Find router map finished, map size = " + routerMap.size() + ", cost " + (System.currentTimeMillis() - startInit) + " ms.");
                startInit = System.currentTimeMillis();

                for (String className : routerMap) {
                    if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
                        // This one of root elements, load root.
                        ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
                    } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
                        // Load interceptorMeta
                        ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
                    } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
                        // Load providerIndex((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex); }}}}Copy the code
  1. Initialization operation, internal callLogisiticsCenter initHelp us manage our logic
  2. The first time I load it,ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);Through the firstcom.alibaba.android.arouter.routesPackage name, scan for all classnames contained below, and store Sp for them. The second load is fetched directly from Sp.
  3. Then traverse, match, meet the conditions to add to the specific set, according to the file prefix, add them to the mapping table WarehousegroupsIndex,interceptorsIndex,providersIndex In the
### _ARouter.getInstance().build(path)
  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); }returnbuild(path, extractGroup(path)); }}Copy the code

Get the preprocessing path according to PathReplaceService, which is a subclass of Iprovider

ExtractGroup (path) This method, which mainly gets the group name. Cut path A string. By default, the first part of path is the group name. This proves that if we do not customize the grouping, the default is the content of the first semicolon.

The build method, which returns a Postcard object.

_ARoter navigation(Class
service) will be called LogisticsCenter.com pletion (it).

  1. First, look for the corresponding RouteMeta in the Warehouse. Routes mapping table based on path. However, the first time it is loaded, there is no data. The first load is in logisticScenter.init (), as mentioned above. So onlyWarehousethegroupsIndex,interceptorsIndex,providersIndexThere is data), so this time routeMeta=null. So, the class name prefix is first fetched from Warehouse. GroupsIndexcom.alibaba.android.arouter.routes.ARouter$$Group$$groupThe file
  2. Next, map our @route annotated class to warehouse.routes;
  3. The loaded group is then removed from theWarehouse.groupsIndexThis also avoids adding Warehouse. Routes; Notice, at this pointWarehouse.routesIt already has a value, so call this method again(the completion (it))Else code block is executed.

Providers, Warehouse. Routes.

Where is the Warehouse. Interceptors assigned?

### LogisticsCenre.java

provider = providerMeta.getConstructor().newInstance();
                            provider.init(mContext);
                            Warehouse.providers.put(providerMeta, provider);
                            instance = provider;

### InterceptorServiceImpl.java

 public void init(final Context context) {... IInterceptor iInterceptor = interceptorClass.getConstructor().newInstance(); iInterceptor.init(context); Warehouse.interceptors.add(iInterceptor); }Copy the code

ARouter defects

The problem with ARouter is that we need to initialize it whenever we use ARouter. All ARouter does is use reflection to scan all classnames in the package name and then add them to the Map

// source code, before plugging
private static void loadRouterMap(a) {
	//registerByPlugin is always set to false
    registerByPlugin = false;
}
// Decompile the code after staking
private static void loadRouterMap(a) {
    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");
}

Copy the code

By default, the file is loaded by scanning dex. Automatic registration using gradle can shorten the initialization time and solve the problem that you cannot directly access the dex file due to application hardening

ARouter’s principle and defect resolution