当前位置:网站首页>Analysis of arouter
Analysis of arouter
2022-07-07 14:22:00 【LLAiden】
ARouter
ARouter yes : Alibaba developed its own routing framework , It mainly solves the problems between components 、 Between modules Interface jump problem .
principle
- Use apt Generate code during compilation
- grouping + Load on demand The routing list
- For internal use Intent To jump
Principle analysis
// The first one created uses Router Annotated Activity
@Route(path = "/module_main/two")
public class TwoActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_two);
Toast.makeText(this, getLocalClassName(), Toast.LENGTH_SHORT).show();
}
}
// the second
@Route(path = "/module_comm/comm")
public class CommActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_comm);
Toast.makeText(this, getLocalClassName(), Toast.LENGTH_SHORT).show();
}
}
// Third
@Route(path = "/module_second/second")
public class SecondActivity extends AppCompatActivity {
@Autowired
String data;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ARouter.getInstance().inject(this);
TextView textView = new TextView(this);
textView.setText(data);
setContentView(textView);
}
}
These three classes are in different module Inside
Next, let's compile this project , See what code has been generated
// first module
public class ARouter$$Group$$module_main implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/module_main/two", RouteMeta.build(RouteType.ACTIVITY, TwoActivity.class, "/module_main/two", "module_main", null, -1, -2147483648));
}
}
public class ARouter$$Providers$$app implements IProviderGroup {
@Override
public void loadInto(Map<String, RouteMeta> providers) {
}
}
public class ARouter$$Root$$app implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("module_main", ARouter$$Group$$module_main.class);
}
}
// the second module
public class ARouter$$Group$$module_comm implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/module_comm/comm", RouteMeta.build(RouteType.ACTIVITY, CommActivity.class, "/module_comm/comm", "module_comm", null, -1, -2147483648));
}
}
public class ARouter$$Providers$$module_comm implements IProviderGroup {
@Override
public void loadInto(Map<String, RouteMeta> providers) {
}
}
public class ARouter$$Root$$module_comm implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("module_comm", ARouter$$Group$$module_comm.class);
}
}
// Third module
public class ARouter$$Group$$module_second implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/module_second/second", RouteMeta.build(RouteType.ACTIVITY, SecondActivity.class, "/module_second/second", "module_second", new java.util.HashMap<String, Integer>(){
{
put("data", 8); }}, -1, -2147483648));
}
}
public class ARouter$$Providers$$module_second implements IProviderGroup {
@Override
public void loadInto(Map<String, RouteMeta> providers) {
}
}
public class ARouter$$Root$$module_second implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("module_second", ARouter$$Group$$module_second.class);
}
}
// Here is for Autowired Annotation variable assignment
public class SecondActivity$$ARouter$$Autowired implements ISyringe {
private SerializationService serializationService;
@Override
public void inject(Object target) {
serializationService = ARouter.getInstance().navigation(SerializationService.class);
SecondActivity substitute = (SecondActivity)target;
substitute.data = substitute.getIntent().getStringExtra("data");
}
}
We can see that basically every module At least three classes will be generated
ARouter$$Providers$$***
It is an empty implementation that temporarily ignores it , Let's take a look atARouter$$Group$$Comm
public class ARouter$$Group$$module_comm implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/module_comm/comm", RouteMeta.build(RouteType.ACTIVITY, CommActivity.class, "/module_comm/comm", "module_comm", null, -1, -2147483648));
}
}
You can see it here loadInto Medium is to be used Router Annotated classes with path The value of is key, Class class by value There is Map in ,
ps all @Router annotation path The first class with the same value after the value slash will be managed in the same class
public class ARouter$$Root$$module_comm implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("module_comm", ARouter$$Group$$module_comm.class);
}
}
There will be Router Annotated path The first value after the slash of is key, take
ARouter$$Group$$module_comm.class
by value There is map in , In this case, can we think ARouter Will Router annotation path The first class with the same value after the slash is divided into a group , Use apt Generate the management class of this group , That's what we have nowARouter$$Group$$module_comm
Here we learn ARouter How to group , Let's take a look ARouter How to get our classes
ARouter.openLog(); // Print log
ARouter.openDebug(); // Turn on debugging mode ( If in InstantRun Run in mode , Debug mode must be turned on ! Online version needs to be closed , Otherwise, there is a safety risk )
ARouter.init(getApplication()); // As early as possible , Recommended in the Application In the initialization
public static void init(Application application) {
if (!hasInit) {
logger = _ARouter.logger;
_ARouter.logger.info(Consts.TAG, "ARouter init start.");
hasInit = _ARouter.init(application);
if (hasInit) {
_ARouter.afterInit();
}
_ARouter.logger.info(Consts.TAG, "ARouter init over.");
}
}
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;
}
// Code has been cut
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
tr {
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);
}
}
}
} catch (Exception e) {
throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
}
}
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
We see this line directly
public static Set<String> getFileNameByPackageName(Context context, final String packageName) throws PackageManager.NameNotFoundException, IOException, InterruptedException {
final Set<String> classNames = new HashSet<>();
// Got it apk Path after installation
List<String> paths = getSourcePaths(context);
for (final String path : paths) {
DefaultPoolExecutor.getInstance().execute(new Runnable() {
@Override
public void run() {
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();
// facilitate dex Next class
while (dexEntries.hasMoreElements()) {
String className = dexEntries.nextElement();
// Find the package name is com.alibaba.android.arouter.routes Initial class
if (className.startsWith(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) {
}
}
}
}
});
}
return classNames;
}
It's coming in here
packgeName = com.alibaba.android.arouter.routes
After this function is executed, we can get several classes we saw before . Because the package name of the class generated after compilation happens to becom.alibaba.android.arouter.routes
Next we continue to see LogisticsCenter Class init function
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
...
for (String className : routerMap) {
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
((IInterceptorGroup(Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
...
}
Here is to get apt Generated classes and call their
loadInto()
Of course, there is no specific group called here loadInto So actually path The mapping with the class has not been loaded into memory , This achieves the effect of saving memory , Here is the management class of each group class Added to theWarehouse.groupsIndex
in , It is also quite simple for us to take out the mapping relationship of specific groups later
Come here ARouter Of init()
It's done. Let's see where we can get the mapping of specific groups
ARouter.getInstance()
.build("/module_second/second")
.navigation();
We usually jump to the page like this. Let's take a look next
navigation()
What have you done
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
...
LogisticsCenter.completion(postcard);
...
return _navigation(context, postcard, requestCode, callback);
....
}
public synchronized static void completion(Postcard postcard) {
if (null == postcard) {
throw new NoRouteFoundException(TAG + "No postcard!");
}
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
// Current map There is no corresponding path Of class
if (null == routeMeta) {
// Take this out path The management class of the Group
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 {
if (ARouter.debuggable()) {
logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] starts loading, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
}
// Use reflection to create objects
IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
// Add all the data of this group to map
iGroupInstance.loadInto(Warehouse.routes);
Warehouse.groupsIndex.remove(postcard.getGroup());
if (ARouter.debuggable()) {
logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] has already been loaded, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
}
} catch (Exception e) {
throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
}
// Reset this function , Next time else molecular
completion(postcard); // Reload
}
} else {
// fill postcard Subsequent interface jumps require
postcard.setDestination(routeMeta.getDestination());
postcard.setType(routeMeta.getType());
postcard.setPriority(routeMeta.getPriority());
postcard.setExtra(routeMeta.getExtra());
Uri rawUri = postcard.getUri();
if (null != rawUri) {
// Try to set params into bundle.
Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);
Map<String, Integer> paramsType = routeMeta.getParamsType();
if (MapUtils.isNotEmpty(paramsType)) {
// Set value by its type, just for params which annotation by @Param
for (Map.Entry<String, Integer> params : paramsType.entrySet()) {
setValue(postcard,
params.getValue(),
params.getKey(),
resultMap.get(params.getKey()));
}
// Save params name which need auto inject.
postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{
}));
}
// Save raw uri
postcard.withString(ARouter.RAW_URI, rawUri.toString());
}
switch (routeMeta.getType()) {
case PROVIDER: // if the route is provider, should find its instance
// Its provider, so it must implement IProvider
Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
IProvider instance = Warehouse.providers.get(providerMeta);
if (null == instance) {
// There's no instance of this provider
IProvider provider;
try {
provider = providerMeta.getConstructor().newInstance();
provider.init(mContext);
Warehouse.providers.put(providerMeta, provider);
instance = provider;
} catch (Exception e) {
throw new HandlerException("Init provider failed! " + e.getMessage());
}
}
postcard.setProvider(instance);
postcard.greenChannel(); // Provider should skip all of interceptors
break;
case FRAGMENT:
postcard.greenChannel(); // Fragment needn't interceptors
default:
break;
}
}
}
From the code above, we can see ARouter On demand loading , We are going to look at ARouter How to jump
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:
// Build intent
final Intent intent = new Intent(currentContext, postcard.getDestination());
intent.putExtras(postcard.getExtras());
// Set flags.
int flags = postcard.getFlags();
if (-1 != flags) {
intent.setFlags(flags);
} else if (!(currentContext instanceof Activity)) {
// Non activity, need less one flag.
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
// Set Actions
String action = postcard.getAction();
if (!TextUtils.isEmpty(action)) {
intent.setAction(action);
}
// Navigation in main looper.
runInMainThread(new Runnable() {
@Override
public void run() {
startActivity(requestCode, currentContext, intent, postcard, callback);
}
});
break;
case PROVIDER:
return postcard.getProvider();
case BOARDCAST:
case CONTENT_PROVIDER:
case FRAGMENT:
Class fragmentMeta = postcard.getDestination();
try {
Object instance = fragmentMeta.getConstructor().newInstance();
if (instance instanceof Fragment) {
((Fragment) instance).setArguments(postcard.getExtras());
} else if (instance instanceof android.support.v4.app.Fragment) {
((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras());
}
return instance;
} catch (Exception ex) {
logger.error(Consts.TAG, "Fetch fragment instance error, " + TextUtils.formatStackTrace(ex.getStackTrace()));
}
case METHOD:
case SERVICE:
default:
return null;
}
return null;
}
From here we can see that ARouter Internal jump is still used Intent, Then why can't we use component development to reference anything else module But in ARouter But you can , That's because it's going on apk All classes will be packaged together , So we use ARouter It is not only convenient but also beautiful to achieve the effect of decoupling
边栏推荐
- requires php ~7.1 -&gt; your PHP version (7.0.18) does not satisfy that requirement
- 多商户商城系统功能拆解01讲-产品架构
- PERT图(工程网络图)
- Wired network IP address of VMware shared host
- Laravel5 call to undefined function openssl cipher iv length() 报错 PHP7开启OpenSSL扩展失败
- 接口自动化测试-接口间数据依赖问题解决
- Verilog implementation of a simple legv8 processor [4] [explanation of basic knowledge and module design of single cycle implementation]
- [network security] SQL injection syntax summary
- Environment configuration
- Laravel5 call to undefined function OpenSSL cipher IV length() error php7 failed to open OpenSSL extension
猜你喜欢
Assign a dynamic value to the background color of DataGrid through ivalueconverter
SSRF vulnerability file pseudo protocol [netding Cup 2018] fakebook1
AI talent cultivation new ideas, this live broadcast has what you care about
UML state diagram
Details of redis core data structure & new features of redis 6
The longest ascending subsequence model acwing 1014 Mountaineering
GAN发明者Ian Goodfellow正式加入DeepMind,任Research Scientist
CVPR2022 | 医学图像分析中基于频率注入的后门攻击
常用數字信號編碼之反向不歸零碼碼、曼徹斯特編碼、差分曼徹斯特編碼
Horizontal of libsgm_ path_ Interpretation of aggregation program
随机推荐
Hands on Teaching: XML modeling
The longest ascending subsequence model acwing 1012 Sister cities
Advanced Mathematics - Chapter 8 differential calculus of multivariate functions 1
Use day JS let time (displayed as minutes, hours, days, months, and so on)
搜索框效果的实现【每日一题】
Is the spare money in your hand better to fry stocks or buy financial products?
LeetCode每日一题(636. Exclusive Time of Functions)
Bashrc and profile
搜索引擎接口
gvim【三】【_vimrc配置】
UML state diagram
交换机和路由器的异同
3D detection: fast visualization of 3D box and point cloud
When FC connects to the database, do you have to use a custom domain name to access it outside?
FCOS3D label assignment
ARM Cortex-A9,MCIMX6U7CVM08AD 处理器应用
3D Detection: 3D Box和点云 快速可视化
Assign a dynamic value to the background color of DataGrid through ivalueconverter
请问,如图,pyhon云函数提示使用了 pymysql模块,这个是怎么回事?
[Reading stereo matching papers] [III] ints