Arouter简单使用
@Route(path = "/image/imageActivity")
class MainActivity : AppCompatActivity() {
@Autowired(name = "tip")
@JvmField
var mTip: String? = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ARouter.getInstance().inject(this)
setContentView(R.layout.layout_activity_main)
findViewById<TextView>(R.id.mTvImageModule).setOnClickListener {
ARouter.getInstance().build("/login/loginActivity").navigation(this)
}
}
}
使用@Route标识路由(至少2级 /image/imageActivity),第一级代表的分组。编译后生成文件
在通过注解处理器在build目录生成了相关文件
Group
public class ARouter$$Group$$image implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/image/imageActivity", RouteMeta.build(RouteType.ACTIVITY, MainActivity.class, "/image/imageactivity", "image", new java.util.HashMap<String, Integer>(){{put("tip", 8); }}, -1, -2147483648));
}
}
我们之前@Route注解的/image/imageActivity,调用loadInto,将该映射信息载入内存
Providers
public class ARouter$$Providers$$imagemodule implements IProviderGroup {
@Override
public void loadInto(Map<String, RouteMeta> providers) {
}
}
代表定义的IProvider,我没有定义IProvider接口,所以这几loadInto是空方法
Root
public class ARouter$$Root$$imagemodule implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("image", ARouter$$Group$$image.class);
}
}
该类将image组下的类信息保存起来,也就是上图中的ARouter$Group$Image类,后续可以有用到具体跳转时候再找到ARouter$Group$image类,然后调用loadInto,避免初始化时候一股脑将所有映射类加载到内存。
问题1:app如何找到不同模块Arouter生成的类的?
答案:通过扫描dex文件,找到目标包名的所有类 看一下初始化过程,最终走到LogisticsCenter的init
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
//省略代码
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>()));
}
//省略代码
}
ClassUtils.getFileNamePackageName(),这个就是从dex文件查找目标类的过程,匹配条件是
ROUTE_ROOT_PAKCAGE 包名开头的类
ROUTE_ROOT_PAKCAGE = com.alibaba.android.arouter.routes
可以看到这个包名就是所有ARouter通过注解处理器生成的类的包名,可以看到上图build下的文件目录,生成类的包名就是这个,再看ClassUtil的查找过程
public static Set<String> getFileNameByPackageName(Context context, final String packageName) throws PackageManager.NameNotFoundException, IOException, InterruptedException {
final Set<String> classNames = new HashSet<>();
List<String> paths = getSourcePaths(context);
final CountDownLatch parserCtl = new CountDownLatch(paths.size());
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();
while (dexEntries.hasMoreElements()) {
String className = dexEntries.nextElement();
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) {
}
}
parserCtl.countDown();
}
}
});
}
//省略代码
return classNames;
}
可以清晰看到,这个过程就是在遍历Dex文件中的Class文件,找过所有目标类后返回目标类名的列表
回到LogisticsCenter的init方法,看下后续步骤
问题2:ARouter是直接将所有注解路由加载进内存吗?
答案:不是,ARouter一开始只是把分组信息,拦截器和Provider加载进来,并没有调用ARouter$Group$XX类的loadInto方法,使用到具体跳转导航,再把分组下的注册跳转信息通过loadInto加载到内存。
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);
}
}
routerMap就是之前查找dex文件找到的目标类类名的集合,遍历集合,然后通过反射调用目标类loadInto方法,
- 调用ARouter$Group$xx的loadInto,将组信息加载进内存
- 调用ARouter$Interceptors$xx,将拦截器信息加载进内存
- 调用ARouter$Providers$xx,将 IProvider信息加载进内存 调用的都是ARouter注解处理器生成的文件