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
- Initialization operation, internal call
LogisiticsCenter init
Help us manage our logic - The first time I load it,
ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
Through the firstcom.alibaba.android.arouter.routes
Package name, scan for all classnames contained below, and store Sp for them. The second load is fetched directly from Sp. - Then traverse, match, meet the conditions to add to the specific set, according to the file prefix, add them to the mapping table Warehouse
groupsIndex
,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).
- 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 only
Warehouse
thegroupsIndex
,interceptorsIndex
,providersIndex
There is data), so this time routeMeta=null. So, the class name prefix is first fetched from Warehouse. GroupsIndexcom.alibaba.android.arouter.routes.ARouter$$Group$$group
The file - Next, map our @route annotated class to warehouse.routes;
- The loaded group is then removed from the
Warehouse.groupsIndex
This also avoids adding Warehouse. Routes; Notice, at this pointWarehouse.routes
It 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