当前位置:网站首页>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.classby 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.routesAfter 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.groupsIndexin , 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
边栏推荐
- Parameter keywords final, flags, internal, mapping keywords internal
- SAKT方法部分介绍
- 请问指南针股票软件可靠吗?交易股票安全吗?
- call undefined function openssl_ cipher_ iv_ length
- c#通过frame 和 page 切换页面
- Excuse me, as shown in the figure, the python cloud function prompt uses the pymysql module. What's the matter?
- 内部排序——插入排序
- Laravel Form-builder使用
- call undefined function openssl_cipher_iv_length
- Leetcode——236. 二叉树的最近公共祖先
猜你喜欢

数据流图,数据字典

CVPR2022 | 医学图像分析中基于频率注入的后门攻击

LeetCode每日一题(636. Exclusive Time of Functions)

Selenium Library

Parsing of XML files

The longest ascending subsequence model acwing 1012 Sister cities

js 获取当前时间 年月日,uniapp定位 小程序打开地图选择地点

Introduction to sakt method

Did login metamask

Horizontal of libsgm_ path_ Interpretation of aggregation program
随机推荐
AutoCAD - how to input angle dimensions and CAD diameter symbols greater than 180 degrees?
Vscode configuration uses pylint syntax checker
gvim【三】【_vimrc配置】
最长上升子序列模型 AcWing 482. 合唱队形
Environment configuration of lavarel env
Introduction to sakt method
call undefined function openssl_ cipher_ iv_ length
Excellent open source system recommendation of ThinkPHP framework
requires php ~7.1 -&gt; your PHP version (7.0.18) does not satisfy that requirement
C # use TCP protocol to establish connection
请问,PTS对数据库压测有好方案么?
Cascading update with Oracle trigger
FCOS3D label assignment
請問,在使用flink sql sink數據到kafka的時候出現執行成功,但是kafka裏面沒有數
Cargo placement problem
Docker deploy Oracle
Codes de non - retour à zéro inversés, codes Manchester et codes Manchester différentiels couramment utilisés pour le codage des signaux numériques
内部排序——插入排序
Mrs offline data analysis: process OBS data through Flink job
js 获取当前时间 年月日,uniapp定位 小程序打开地图选择地点