当前位置:网站首页>Learning record of arouter principle
Learning record of arouter principle
2022-07-03 04:46:00 【Aosendi】
Preface
The first share after the Spring Festival , The working state is also slowly getting back . The sharing that this article will bring to you is ARouter Introduction to the principle of , By understanding its principle , We can know how it supports calling or page jumping between componentized and non interdependent modules .
Text
ARouter Introduce
ARouter Alibaba open source is a routing framework for componentization , It can help page Jump and service call between components that do not depend on each other .
ARouter Use
Add dependency :
android {
//...
defaultConfig {
kapt {
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
}
}
//...
}
dependencies {
api 'com.alibaba:arouter-api:1.5.0'
kapt 'com.alibaba:arouter-compiler:1.2.2'
}
Define jump Activity Of path:
@Route(path = "/test/router_activity")
class RouterActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_router)
}
}
initialization Router frame :
class RouterDemoApp : Application() {
override fun onCreate() {
super.onCreate()
// initialization 、 Inject
ARouter.init(this)
}
}
Call jump :
ARouter.getInstance().build("/test/router_activity").navigation()
Generated code ( Generated routing table )
When we give Activity Or services, etc., plus Route After the note ,build once ,ARouter The framework will help us generate according to the template java file , And it can be accessed at runtime . The technology used is apt technology . Let's take a look at the code generated by the above example :
public class ARouter$$Root$$app implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("test", ARouter$$Group$$test.class);
}
}
public class ARouter$$Group$$test implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/test/router_activity", RouteMeta.build(RouteType.ACTIVITY, RouterActivity.class, "/test/router_activity", "test", null, -1, -2147483648));
}
}
According to the code generated above, we can see , The generated code is a routing table , First, match the group with the group's class correspond , Inside each group is the routing table under the Group .
initialization init()( Load the group of the routing table )
Next, let's look at initialization , What is done in the routing framework :
//#ARouter
public static void init(Application application) {
if (!hasInit) {
//... Omitted code
hasInit = _ARouter.init(application);
//... Omitted code
}
}
//#_ARouter
protected static synchronized boolean init(Application application) {
mContext = application;
LogisticsCenter.init(mContext, executor);
logger.info(Consts.TAG, "ARouter init success!");
hasInit = true;
mHandler = new Handler(Looper.getMainLooper());
return true;
}
The core code of initialization looks like LogisticsCenter in :
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context;
executor = tpe;
try {
//... Omit code
if (registerByPlugin) {
//... Omit code
} else {
Set<String> routerMap;
// If it is debug Package or newer version
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
// To get in com.alibaba.android.arouter.routes So class Class name
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
// Update to sp in
if (!routerMap.isEmpty()) {
context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
}
// An updated version
PackageUtils.updateVersion(context);
} else {
// Take out the stored data directly from the cache class
routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
}
// Traverse routerMap, take group Class loaded into the cache
for (String className : routerMap) {
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
// Generated Root、 For example, our example above ARouter$$Root$$app, call loadInto It's equivalent to loading routes.put("test", ARouter$$Group$$test.class)
((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
// Load interceptors , For example, generated ARouter$$Interceptors$$app
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
// load Provider, For example, generated ARouter$$Providers$$app
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
}
//... Omit code
} catch (Exception e) {
throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
}
}
The core logic above is if debug Package or newer version , Then go get com.alibaba.android.arouter.routes So class Class name , And then update to sp in , And update the version number . Then load through reflection IRouteRoot, To load the group and its corresponding class object , In addition, interceptors will be loaded ,Provider.
Here we focus on obtaining class File path method getFileNameByPackageName:
public static Set<String> getFileNameByPackageName(Context context, final String packageName) throws NameNotFoundException, IOException, InterruptedException {
final Set<String> classNames = new HashSet();
// Get dex File path
List<String> paths = getSourcePaths(context);
final CountDownLatch parserCtl = new CountDownLatch(paths.size());
Iterator var5 = paths.iterator();
while(var5.hasNext()) {
final String path = (String)var5.next();
DefaultPoolExecutor.getInstance().execute(new Runnable() {
public void run() {
DexFile dexfile = null;
try {
// Load out dexfile file
if (path.endsWith(".zip")) {
dexfile = DexFile.loadDex(path, path + ".tmp", 0);
} else {
dexfile = new DexFile(path);
}
Enumeration dexEntries = dexfile.entries();
// Traverse dexFile The elements inside , Load out .class file
while(dexEntries.hasMoreElements()) {
String className = (String)dexEntries.nextElement();
// start "com.alibaba.android.arouter.routes"
if (className.startsWith(packageName)) {
classNames.add(className);
}
}
} catch (Throwable var12) {
Log.e("ARouter", "Scan map file in dex files made error.", var12);
} finally {
//... Omit code
parserCtl.countDown();
}
}
});
}
parserCtl.await();
//... Omit code
return classNames;
}
The core logic in this method is to load dex Path to file , Then build DexFile, After construction, traverse the elements inside , If it is com.alibaba.android.arouter.routes At the beginning class file , Then save it to the list and wait for it to return .
getSourcePaths:
public static List<String> getSourcePaths(Context context) throws NameNotFoundException, IOException {
ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);
File sourceApk = new File(applicationInfo.sourceDir);
List<String> sourcePaths = new ArrayList();
sourcePaths.add(applicationInfo.sourceDir);
String extractedFilePrefix = sourceApk.getName() + ".classes";
// Whether it is turned on multidex, If it's on , You need to get each dex route
if (!isVMMultidexCapable()) {
int totalDexNumber = getMultiDexPreferences(context).getInt("dex.number", 1);
File dexDir = new File(applicationInfo.dataDir, SECONDARY_FOLDER_NAME);
// Go through each one dex file
for(int secondaryNumber = 2; secondaryNumber <= totalDexNumber; ++secondaryNumber) {
//app.classes2.zip、app.classes3.zip ...
String fileName = extractedFilePrefix + secondaryNumber + ".zip";
File extractedFile = new File(dexDir, fileName);
if (!extractedFile.isFile()) {
throw new IOException("Missing extracted secondary dex file '" + extractedFile.getPath() + "'");
}
sourcePaths.add(extractedFile.getAbsolutePath());
}
}
if (ARouter.debuggable()) {
sourcePaths.addAll(tryLoadInstantRunDexFile(applicationInfo));
}
return sourcePaths;
}
getSourcePaths The function of is to get app All of the dex Path to file , Turn into class File to get class The file path provides data .
Summary :
- ARouter.init(this) The call is handed over to the internal _ARouter.init(application), Then the real thing is LogisticsCenter.init(mContext, executor)
- If it is debug Package or upgraded version , Then go to load com.alibaba.android.arouter.routes Under bag dex Path to file , And update it to the cache
- Through these dex To get all the corresponding class Path to file
- Finally, according to the prefix of the class name, it is loaded into Warehouse Corresponding map in , Among them group、interceptor and provider
Call and process
ARouter.getInstance().build("/test/router_activity").navigation()
build Will build a Postcard The object comes out :
//#Router
public Postcard build(String path) {
return _ARouter.getInstance().build(path);
}
//#_ARouter
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 The way is from path To extract from group, such as "/test/router_activity",test Is extracted group
return build(path, extractGroup(path));
}
}
build(path, group) Method will eventually build a Postcard The object comes out .
Build up PostCard after , Call it the navigation Method can realize our jump or get the corresponding entity .navigation Method will finally call _ARouter Of navigation Method :
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
//... Omit code
try {
//1. according to postCard Of group Load routing table , And complete postCard Information about
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
//... exception handling
return null;
}
if (null != callback) {
callback.onFound(postcard);
}
// If it's not the green channel , The logic of interceptors is needed , Otherwise, the interceptor will be skipped
if (!postcard.isGreenChannel()) {
interceptorService.doInterceptions(postcard, new InterceptorCallback() {
@Override
public void onContinue(Postcard postcard) {
//2. Truly realize action processing
_navigation(context, postcard, requestCode, callback);
}
@Override
public void onInterrupt(Throwable exception) {
if (null != callback) {
callback.onInterrupt(postcard);
}
//... Omit code
}
});
} else {
//2. Truly realize action processing
return _navigation(context, postcard, requestCode, callback);
}
return null;
}
navigation The core logic of the method is : Load routing table , And complete postCard Information about , Then really deal with jump or request logic .
LogisticsCenter.completion(postcard) The core source code is as follows :
public synchronized static void completion(Postcard postcard) {
if (null == postcard) {
throw new NoRouteFoundException(TAG + "No postcard!");
}
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) {
//groupsIndex stay init It has been loaded by , You can go through here group Get the corresponding group Of class object
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 {
// Load route and cache it into memory, then delete from metas.
try {
//... Omit code
IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
// take group The routing table inside is loaded into memory , Our first example is to implement :atlas.put("/test/router_activity", RouteMeta.build(RouteType.ACTIVITY, RouterActivity.class, "/test/router_activity", "test", null, -1, -2147483648));
iGroupInstance.loadInto(Warehouse.routes);
// Because the routing table is loaded , So you can group Remove... From memory , Save memory
Warehouse.groupsIndex.remove(postcard.getGroup());
//... Omit code
} catch (Exception e) {
throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
}
// Have already put group The routing table in is loaded , Execute the function again .
completion(postcard); // Reload
}
} else {
// The second time , to postCard Fill in the information
postcard.setDestination(routeMeta.getDestination());
postcard.setType(routeMeta.getType());
postcard.setPriority(routeMeta.getPriority());
postcard.setExtra(routeMeta.getExtra());
//... Omit code , Mainly parsing uri Then the assignment of parameters
// According to different types of route acquisition , Continue to add some information to postCard
switch (routeMeta.getType()) {
case PROVIDER:
//... Omit code , It is mainly to add some other parameters
postcard.greenChannel(); // Provider should skip all of interceptors
break;
case FRAGMENT:
postcard.greenChannel(); // Fragment needn't interceptors
default:
break;
}
}
}
End of supplement postCard After the message , Now let's see _navigation Method :
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:
// structure Intent, Then switch to the main thread , And jump to the specified Activity
break;
case PROVIDER:
return postcard.getProvider();
case BOARDCAST:
case CONTENT_PROVIDER:
case FRAGMENT:
// Reflection constructs an instance and returns
case METHOD:
default:
return null;
}
return null;
}
You can see , Eventually, according to different type, To respond differently , for example ACTIVITY Words , Will be carried out in activity The jump , Other operations such as instance return will be constructed through reflection .
Summary :
- The beginning of the call , Will build a PostCard object , initialization path and group
- navigation Method will eventually call _ARouter Of navigation Method , And then through LogisticsCenter.completion(postcard) To load the group The routing table inside , And complete postcard Information .
- If there is a green channel , The interceptor is not executed , Just skip , Otherwise, you need to execute the interceptor .
- Finally, perform corresponding operations through different types .
Conclusion
The sharing of this article ends here , Believe that after reading , Can be right ARouter Have a certain understanding of the principle of , So that if we use it later , Can better use , Or provide a good idea reference for customizing the routing framework for the project . meanwhile , Such an excellent framework is also worth learning some of its design ideas .
边栏推荐
- 论文阅读_中文NLP_ELECTRA
- What functions need to be set after the mall system is built
- Sdl2 + OpenGL glsl practice (Continued)
- Market status and development prospect prediction of the global forward fluorescent microscope industry in 2022
- Market status and development prospect prediction of the near infrared sensor industry of the global Internet of things in 2022
- MPM model and ab pressure test
- stm32逆向入门
- [USACO 2009 Dec S]Music Notes
- Market status and development prospect prediction of the global fire hose industry in 2022
- Writing skills of multi plate rotation strategy -- strategy writing learning materials
猜你喜欢
Number of 1 in binary (simple difficulty)
Auman Galaxy new year of the tiger appreciation meeting was held in Beijing - won the double certification of "intelligent safety" and "efficient performance" of China Automotive Research Institute
2022 t elevator repair simulation examination question bank and t elevator repair simulation examination question bank
2022 registration of G2 utility boiler stoker examination and G2 utility boiler stoker reexamination examination
SSM based campus part-time platform for College Students
Integration of Android high-frequency interview questions (including reference answers)
Valentine's day limited withdrawal guide: for one in 200 million of you
移动端——uniapp开发记录(公共请求request封装)
Smart contract security audit company selection analysis and audit report resources download - domestic article
带有注意力RPN和多关系检测器的小样本目标检测网络(提供源码和数据及下载)...
随机推荐
Thesis reading_ Chinese medical model_ eHealth
7. Integrated learning
Introduction to message queuing (MQ)
MC Layer Target
Library management system based on SSM
[USACO 2009 Dec S]Music Notes
Market status and development prospect prediction of the near infrared sensor industry of the global Internet of things in 2022
Why does I start with =1? How does this code work?
The programmer went to bed at 12 o'clock in the middle of the night, and the leader angrily scolded: go to bed so early, you are very good at keeping fit
[SQL injection point] location and judgment of the injection point
2022 Shandong Province safety officer C certificate examination content and Shandong Province safety officer C certificate examination questions and analysis
[set theory] binary relation (example of binary relation on a | binary relation on a)
GFS distributed file system (it's nice to meet it alone)
2022 a special equipment related management (elevator) analysis and a special equipment related management (elevator) simulation test
MediaTek 2023 IC written examination approved in advance (topic)
Use the benchmarksql tool to perform a data prompt on kingbases. The jdbc driver cannot be found
4 years of experience to interview test development, 10 minutes to end, ask too
Uipath practice (08) - selector
Day 51 - tree problem
[PCL self study: filtering] introduction and use of various filters in PCL (continuously updated)