ARouter是一个路由框架,来实现组件化过程中不同module之间的相互跳转。本文简单分析ARouter初始化以及跳转Activity的最基本过程,不包含额外的参数设置等。
一、ARouter涉及的知识:
1.门面模式
2.双重检验锁单例
3.注解以及APT注解处理器
4.DexFile
- 以后再补
二、可能遇到的坑:
1.必须为每一个支持跳转的module单独添加依赖,并指定
AROUTER_MODULE_NAME
2.不同的Activity相同的layout会混乱???
3.kotlin使用kapt而不是annotation processor添加依赖
4.添加kotlin-kapt依赖时
apply plugin: 'kotlin-android'
//kotlin-kapt要放在kotlin-android 下面,否则编译出错???
apply plugin: 'kotlin-kapt'
三、准备工作
1.在Application下执行Arouter的初始化
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
初始化//
ARouter.init(this);
}
2.在支持跳转的Activity中添加注解,并设置path
@Route(path = "/a/b")
class EmptyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_empty)
btn_2.setOnClickListener {
val postCard = ARouter.getInstance().build( "/a/b")
postCard.navigation()
}
}
3.make project,然后在/Users/king/Documents/workspace/asworkspace/MyDemo/app/build/generated/source/kapt/debug
目录下自动生成相关类文件(Android Studio 3.6, kotlin)。本文只关注com.alibaba.android.arouter.routes.ARouter$$Group$$a
以及 com.alibaba.android.arouter.routes.ARouter$$Root$$app
两个文件。(名字与注解中的path
参数有关)
ARouter$$Group$$a.java
public class ARouter$$Group$$a implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/a/b", RouteMeta.build(RouteType.ACTIVITY, EmptyActivity.class, "/a/b", "a", null, -1, -2147483648));
}
}
ARouter$$Root$$app.java
public class ARouter$$Root$$app implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("a", ARouter$$Group$$a.class);
}
}
四、正式开始
1.初始化
//ARouter.java 省略部分代码...
//采用门面模式,初始化实际在 _Arouter.java
public static void init(Application application) {
if (!hasInit) {
hasInit = _ARouter.init(application);
}
}
直接进入_ARouter.java
//_Arouter.java 省略部分代码...
protected static synchronized boolean init(Application application) {
mContext = application;
//核心代码
LogisticsCenter.init(mContext, executor);
mHandler = new Handler(Looper.getMainLooper());
return true;
}
接着看LogisticsCenter.init
方法
//LogisticsCenter.java 省略部分代码...
public synchronized static void init(Context context, ThreadPoolExecutor tpe){
try {
//TODO 通过ARouter插件加载??
loadRouterMap();
if (registerByPlugin) {
logger.info(TAG, "Load router map by arouter-auto-register plugin.");
} else {
//普通方式
Set<String> routerMap;
//如果ARouter在debug模式下或者首次初始化时
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
//ROUTE_ROOT_PAKCAGE=“com.alibaba.android.arouter.routes”
//这个方法返回该包名下所有的类集合
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
if (!routerMap.isEmpty()) {
//保存到SP中
context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
}
} else {
//非debug模式下或者非首次初始化时,从SP中取
routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
}
//遍历routerMap
for (String className : routerMap) {
//以SUFFIX_ROOT=“ARouter$$Root”开头的类,并调用 loadInto 方法
//IRouteRoot类型,对应生成的ARouter$$Root$$app.java
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
}
//TODO 通过反射初始化“ARouter$$Interceptors”开头的类
else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
}
//TODO 以“ARouter$$Providers”开头的类
else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
}
}
先分析ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE)
这一行
//ClassUtils.java 省略部分代码...
/**
* 通过指定包名,扫描包下面包含的所有的ClassName
*
* @param context U know
* @param packageName 包名
* @return 所有class的集合
*/
//packageName=com.alibaba.android.arouter.routes
public static Set<String> getFileNameByPackageName(Context context, final String packageName) {
final Set<String> classNames = new HashSet<>();
//获取apk内所有dex所在路径,可能有多个
List<String> paths = getSourcePaths(context);
//使用CountDownLatch
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 {
//EXTRACTED_SUFFIX=".zip"
if (path.endsWith(EXTRACTED_SUFFIX)) {
dexfile = DexFile.loadDex(path, path + ".tmp", 0);
} else {
//创建DexFile
dexfile = new DexFile(path);
}
//遍历dexFile内的Class文件,找到目标包名开头的文件,保存到集合中
Enumeration<String> dexEntries = dexfile.entries();
while (dexEntries.hasMoreElements()) {
String className = dexEntries.nextElement();
if (className.startsWith(packageName)) {
classNames.add(className);
}
}
} catch (Throwable ignore) {
} finally {
dexfile.close();
}
//完成一个dexFiel解析CountDownLatch减一,减到0时取消阻塞
parserCtl.countDown();
}
}
});
}
//阻塞
parserCtl.await();
//返回对应包名下class的集合,包含apt为我们生成的类以及ARouter自己的部分类
return classNames;
}
再分析((IRouteRoot(Class.forName(className).getConstructor().newInstance())) .loadInto(Warehouse.groupsIndex);
这一行。以ARouter$$Rout$$开头的类,这里以Arouter帮我们生成的类ARouter$$Root$$app.java
为例
/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER.
*/
public class ARouter$$Root$$app implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
//routes即Warehouse.groupsIndex(HashMap)
//将ARouter$$Group$$a.class保存到Warehouse.groupsIndex中
routes.put("a", ARouter$$Group$$a.class);
}
}
至此,初始化完成。
总结
ARouter初始化时,将我们apk中所有以com.alibaba.android.arouter.routes
为包名的类名保存到 routerMap
中,并将routerMap
保存在SP中。
然后遍历 routerMap
,通过反射初始化ARouter$$Root
开头的类,并调用其loadInto
方法,传入一个Map类型的参数Warehouse.groupsIndex
。
以 ARouter$$Root$$app
为例,在loadInto
方法中,将ARouter$$Group$$a.class
保存到Warehouse.groupsIndex
中。
2.使用
val postCard = ARouter.getInstance().build("/a/b")
//门面模式
public Postcard build(String path) {
return _ARouter.getInstance().build(path);
}
进入_Arouter.java
类 省略部分代码...
// _ARouter.getInstance() 使用了双重检验锁单例
//直接看build方法
protected Postcard build(String path) {
//TODO PathReplaceService
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
//重载,extractGroup(path)内部为字符串操作,拿到path中第一个/后的String
//path="/a/b" ,extractGroup(path)="a"
return build(path, extractGroup(path));
}
查看重载的build
方法
protected Postcard build(String path, String group) {
//...省略部分代码
//TODO PathReplaceService
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
//返回一个PostCard对象,只添加了path和group属性
return new Postcard(path, group);
}
接着看navigation
跳转
//跳转 postCard.navigation(),经过几次重载,最终到达
public Object navigation(Context context, NavigationCallback callback) {
return ARouter.getInstance().navigation(context, this, -1, callback);
}
因为Arouter
为门面,_Arouter
为核心,所以直接进入_Arouter
中
//参数 requestCode=-1,callback=null
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
//TODO PretreatmentService 这几个内部的跳转未分析
//省略部分代码...
try {
//为postcard 设置更多属性信息
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
//这部分为异常的回调,我们传入的callback=null,暂时不看🙈
//TODO PretreatmentService 这几个内部的跳转未分析
//省略部分代码...
return null;
}
//省略部分代码...
//TODO 是否为GreenChannel,当为Fragment和PROVIDER时为true。
if (!postcard.isGreenChannel()) {
} else {
//重载
return _navigation(context, postcard, requestCode, callback);
}
return null;
}
先分析LogisticsCenter.completion(postcard)
这一行,这里对postcard添加更多的属性值
//分析 LogisticsCenter.completion(postcard);
//首次进入时,postcard包含path,group属性
public synchronized static void completion(Postcard postcard) {
//省略部分代码...
//Warehouse.routes首次是一个空的HashMap
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) { // Maybe its does't exist, or didn't load.
//从Warehouse.groupsIndex中获取,上面我们分析到 Warehouse.groupsIndex是一个HashMap
//保存了ARouter$$Group$$a.class
Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup());
if (null == groupMeta) {
//抛出异常
} else {
// Load route and cache it into memory, then delete from metas.
try {
//以groupMeta=ARouter$$Group$$a.class 为例
IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
//调用loadInto方法,以key=path,value=RouteMeta保存到 Warehouse.routes中,具体见下面分析
iGroupInstance.loadInto(Warehouse.routes);
Warehouse.groupsIndex.remove(postcard.getGroup());
} catch (Exception e) {
}
//再次执行completion方法,此时Warehouse.routes中包含内容,进入下面的else分支
completion(postcard); // Reload
}
} else {
//为postcard赋值
postcard.setDestination(routeMeta.getDestination());
postcard.setType(routeMeta.getType());
postcard.setPriority(routeMeta.getPriority());
postcard.setExtra(routeMeta.getExtra());
if (null != rawUri) { // Try to set params into bundle.
//TODO 省略uri跳转相关
}
switch (routeMeta.getType()) {
case PROVIDER:
//TODO 省略PROVIDER 跳转相关,这里设置了greenChannel
postcard.greenChannel(); // Provider should skip all of interceptors
break;
case FRAGMENT:
//这里设置了greenChannel
postcard.greenChannel(); // Fragment needn't interceptors
default:
break;
}
}
}
分析iGroupInstance.loadInto(Warehouse.routes);
这一行,以iGroupInstance
为ARouter$$Group$$a
类为例
public class ARouter$$Group$$a implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
//以key为Activity上注解内的path,value为RouteMeta
atlas.put("/a/b", RouteMeta
.build(RouteType.ACTIVITY, EmptyActivity.class,
"/a/b", "a", null, -1,
-2147483648));
}
}
查看RouteMeta
的build
方法,最终调用了其构造方法
//对应我们的类中
public RouteMeta(RouteType type, Element rawType, Class<?> destination, String name, String path, String group, Map<String, Integer> paramsType, int priority, int extra) {
this.type = type; //RouteType.ACTIVITY
this.name = name; //
this.destination = destination; // EmptyActivity.class
this.rawType = rawType; //null
this.path = path; // “/a/b”
this.group = group;// ”a“
this.paramsType = paramsType; //null
this.priority = priority; //-1
this.extra = extra; //-2147483648??
}
回来继续继续看重载的_navigation
方法,这里的postcard
已经是添加过额外属性的postcard
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:
// postcard.getDestination() 拿到目标Activity
final Intent intent = new Intent(currentContext, postcard.getDestination());
//省略部分设置FLAG,ACTION等代码...
runInMainThread(new Runnable() {
@Override
public void run() {
//实现Activity跳转
startActivity(requestCode, currentContext, intent, postcard, callback);
}
});
break;
//TODO 其余组件处理
case PROVIDER:
case BOARDCAST:
case CONTENT_PROVIDER:
case FRAGMENT:
case METHOD:
case SERVICE:
default:
return null;
}
return null;
}
总结
通过ARouter.getInstance().build("/a/b")
创建了一个只添加了path
和 group
属性的PostCard
对象(PostCard继承自RouteMeta)。
当调用 PostCard.navigation()
实现跳转时,最终仍然调用的_Arouter
的navigation
,在这个方法从Warehouse.groupsIndex
中取出IRouteGroup
类型的对象,并调用IRouteGroup
内部的loadInto
方法,创建RouteMeta
并保存到Warehouse.routes ``中,方便以后使用。 然后为
PostCard添加更多的属性(待启动的组件、待启动的组件类型、启动模式等等)。 最后根据
routeMeta的
type```属性分别调用对应的启动方法,完成组件之间的跳转


参考:
通过ApplicationInfo下的sourceDir获取APK所有的class
简书的短信验证码机制真垃圾哦,一分钟只能发送一条。登陆的验证码和重置密码这种不同类型的短信验证码默认不区分
网友评论