本篇文章主要讲解了 ARouter 框架的源码分析,包括其初始化过程、核心方法等。
初始化
在使用ARouter的时候我们都会先进行初始化:
java
ARouter.init(this);
我们看下 init() 源码:
java
public static void init(Application application) {
// 检查 ARouter 是否已经初始化,避免重复初始化
if (!hasInit) {
// 获取 logger,并记录初始化开始的日志
logger = _ARouter.logger;
_ARouter.logger.info(Consts.TAG, "ARouter init start.");
// 执行 ARouter 的初始化方法,并将初始化结果赋值给 hasInit
hasInit = _ARouter.init(application);
// 如果初始化成功,执行后续的初始化步骤
if (hasInit) {
_ARouter.afterInit();
}
// 记录初始化完成的日志
_ARouter.logger.info(Consts.TAG, "ARouter init over.");
}
}
真正调用的还是里面的 _ARouter.init():
java
protected static synchronized boolean init(Application application) {
// 将传入的 Application 实例赋值给 mContext
mContext = application;
// 初始化 LogisticsCenter,通常是用来管理路由路径和目标组件的调度
LogisticsCenter.init(mContext, executor);
// 记录初始化成功的日志
logger.info(Consts.TAG, "ARouter init success!");
// 设置初始化标志,标记 ARouter 已初始化
hasInit = true;
// 创建一个 Handler 用于在主线程中处理消息
mHandler = new Handler(Looper.getMainLooper());
// 返回初始化成功
return true;
}
最终调用了LogisticsCenter的 init() 方法:
java
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
// 保存上下文和线程池实例
mContext = context;
executor = tpe;
try {
// 记录初始化开始时间
long startInit = System.currentTimeMillis();
// 加载路由映射
loadRouterMap();
// 判断是否使用插件进行自动注册
if (registerByPlugin) {
logger.info(TAG, "Load router map by arouter-auto-register plugin.");
} else {
Set<String> routerMap;
// 如果是调试模式或者是新版本安装,则重新构建路由映射
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
// 获取路由类文件名(这些类是由 arouter-compiler 插件生成的)
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
// 如果有新的路由映射,保存到 SharedPreferences 中
if (!routerMap.isEmpty()) {
context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE)
.edit()
.putStringSet(AROUTER_SP_KEY_MAP, routerMap)
.apply();
}
// 更新应用版本信息
PackageUtils.updateVersion(context);
} 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) {
// 判断是否是根元素类,若是则加载到 groupsIndex
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
}
// 判断是否是拦截器类,若是则加载到 interceptorsIndex
else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
}
// 判断是否是提供者类,若是则加载到 providersIndex
else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
}
// 打印组件加载完成的日志
logger.info(TAG, "Load root element finished, cost " + (System.currentTimeMillis() - startInit) + " ms.");
// 检查是否加载到路由映射,如果没有,打印错误日志
if (Warehouse.groupsIndex.size() == 0) {
logger.error(TAG, "No mapping files were found, check your configuration please!");
}
// 如果是调试模式,打印路由、拦截器和提供者的数量
if (ARouter.debuggable()) {
logger.debug(TAG, String.format(Locale.getDefault(), "LogisticsCenter has already been loaded, GroupIndex[%d], InterceptorIndex[%d], ProviderIndex[%d]",
Warehouse.groupsIndex.size(), Warehouse.interceptorsIndex.size(), Warehouse.providersIndex.size()));
}
} catch (Exception e) {
// 捕获异常并抛出自定义的 HandlerException
throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
}
}
上面方法主要实现了:
- 通过loadRouterMap方法判断是不是通过arouter-register自动加载路由表,如果是通过自动加载的则registerByPlugin=true,这里我们先不管通过arouter-register自动加载的方式
- 紧接着通过ClassUtils.getFileNameByPackageName(此处用到了线程池、CountDownLatch面试高频考点)获取到apk中前缀为com.alibaba.android.arouter.routes的类,这里面主要是通过判断是不是支持MultiDex,如果不支持MultiDex,扫描所有的dex文件,然后压缩成zip文件,然后通过DexFile.loadDex转化成DexFile对象,如果支持MultiDex,直接new DexFile,然后循环DexFile拿里面的class文件,然后过滤出com.alibaba.android.arouter.routes前缀的class并返回。
- 拿到了需要的class类后,放到sp里面,方便下次不去扫描apk拿class,更新版本号
- 将com.alibaba.android.arouter.routes.ARouter$$Root前缀的class类放到Warehouse.groupsIndex中
- 将com.alibaba.android.arouter.routes.ARouter$$Interceptors前缀的class类放到Warehouse.interceptorsIndex中
- 将com.alibaba.android.arouter.routes.ARouter$$Providers前缀的class类放到Warehouse.providersIndex中
_ARouter.afterInit:
java
static void afterInit() {
// Trigger interceptor init, use byName.
interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
}
ARouter.build
下面我们来看ARouter的build方法:
java
public Postcard build(String path) {
return _ARouter.getInstance().build(path);
}
返回的是Postcard类型的对象,我们看一下里面的 _ARouter.build() :
java
protected Postcard build(String path, String group, Boolean afterReplace) {
// 检查路径(path)和分组(group)是否为空,如果为空则抛出异常
if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
// 如果 afterReplace 为 false,则进行路径替换
if (!afterReplace) {
// 获取 PathReplaceService 服务实例,该服务用于路径的替换操作
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
// 如果 PathReplaceService 服务实例不为空,则执行路径替换操作
if (null != pService) {
path = pService.forString(path); // 调用替换方法替换路径
}
}
// 构建并返回一个 Postcard 对象,使用处理后的 path 和 group
return new Postcard(path, group);
}
}
build
方法 :根据传入的路径path
和分组group
创建一个Postcard
对象。- 路径替换 :如果
afterReplace
为false
,则调用PathReplaceService
服务对路径进行替换。 - 异常处理 :如果
path
或group
为空,抛出自定义的HandlerException
异常。
- 当
afterReplace
为false
时 :如果afterReplace
参数为false
,则会进行路径替换。这意味着在构建Postcard
对象之前,会通过PathReplaceService
服务来替换路径。这个服务允许开发者自定义路径替换逻辑,例如,根据配置或环境变量动态调整路径。这样做的好处是可以灵活地适应不同的部署环境或业务需求,而无需硬编码路径。 - 当
afterReplace
为true
时 :如果afterReplace
参数为true
,则不会进行路径替换。这意味着直接使用原始的路径来构建Postcard
对象。这通常用于那些不需要动态路径替换的场景,或者路径已经在其他地方被处理过,不需要再次替换。
示例代码
假设你实现了一个 PathReplaceService
,它的作用是根据不同的环境替换路径中的某些部分:
java
public class PathReplaceServiceImpl implements PathReplaceService {
@Override
public String forString(String path) {
// 假设在生产环境中替换 "/test/" 为 "/prod/"
if (BuildConfig.DEBUG) {
return path; // 开发环境不做替换
} else {
return path.replace("/test/", "/prod/"); // 生产环境进行路径替换
}
}
}
然后在调用 ARouter
时使用这个服务:
java
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path); // 使用服务进行路径替换
}
Postcard类
Postcard
类是 ARouter 框架中的一个核心组件,它表示一个路由的实体,包含了路由的所有信息,比如目标路径、分组、传递的数据、动画效果、标志位等。
java
public final class Postcard extends RouteMeta {
// Base
private Uri uri;
private Object tag; // A tag prepare for some thing wrong. inner params, DO NOT USE!
private Bundle mBundle; // Data to transform
private int flags = 0; // Flags of route
private int timeout = 300; // Navigation timeout, TimeUnit.Second
private IProvider provider; // It will be set value, if this postcard was provider.
private boolean greenChannel;
private SerializationService serializationService;
private Context context; // May application or activity, check instance type before use it.
private String action;
Postcard
类继承自 RouteMeta
,是路由的实际载体,包含导航需要的额外信息。
RouteMeta:
java
public class RouteMeta {
// 路由类型,指示目标是 Activity、Fragment 还是 Provider 等
private RouteType type;
// 原始类型元素,表示目标路由的原始类型(如 Activity 或 Fragment)
private Element rawType;
// 目标页面的 Class 类型,表示路由请求的具体目标类
private Class<?> destination;
// 路由路径,唯一标识一个路由,如 "/app/MainActivity"
private String path;
// 路由分组,常用于模块化开发,避免路径冲突
private String group;
// 路由的优先级,用于在多个路由匹配时,确定优先使用哪个路由
private int priority;
// 额外的信息,通常用于存放一些配置信息
private int extra;
// 路由参数类型的映射,存储参数名与其类型的对应关系
private Map<String, Integer> paramsType;
// 路由的名称,通常用于调试或日志中,便于标识路由
private String name;
// 注入的配置,存储需要自动注入的字段信息
private Map<String, Autowired> injectConfig;
}
PostCard.navigation
navigation有很多重载的方法,最终都会走_Arouter.navigation
java
/**
* 导航到 postcard 中路径指定的路由。
*
* @param context 上下文,通常是 Activity 等。
*/
public Object navigation(Context context) {
return navigation(context, null);
}
/**
* 导航到 postcard 中路径指定的路由。
*
* @param context 上下文,通常是 Activity 等。
* @param callback 导航回调接口。
*/
public Object navigation(Context context, NavigationCallback callback) {
return ARouter.getInstance().navigation(context, this, -1, callback);
}
/**
* 导航到 postcard 中路径指定的路由。
*
* @param mContext 上下文,通常是 Activity 等。
* @param requestCode `startActivityForResult` 的请求码。
*/
public void navigation(Activity mContext, int requestCode) {
navigation(mContext, requestCode, null);
}
/**
* 导航到 postcard 中路径指定的路由。
*
* @param mContext 上下文,通常是 Activity 等。
* @param requestCode `startActivityForResult` 的请求码。
* @param callback 导航回调接口。
*/
public void navigation(Activity mContext, int requestCode, NavigationCallback callback) {
ARouter.getInstance().navigation(mContext, this, requestCode, callback);
}
这几个方法会调用ARouter.navigation:
java
public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) {
return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);
}
接着调用_Arouter.navigation:
java
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
// 获取预处理服务实例
PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
// 如果预处理服务存在并且返回的结果为 false,则导航失败,取消导航
if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
// 预处理失败,导航被取消
return null;
}
// 设置 postcard 的上下文,如果传入的 context 为 null,则使用 mContext
postcard.setContext(null == context ? mContext : context);
try {
// 完成路由的物流处理
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
logger.warning(Consts.TAG, ex.getMessage());
// 如果启用了调试模式,则显示友好的提示信息给用户
if (debuggable()) {
runInMainThread(new Runnable() {
@Override
public void run() {
// 弹出提示框,告知没有找到匹配的路由
Toast.makeText(mContext, "没有找到匹配的路由!\n" +
" 路径 = [" + postcard.getPath() + "]\n" +
" 分组 = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show();
}
});
}
// 如果有回调函数,调用 onLost 方法
if (null != callback) {
callback.onLost(postcard);
} else {
// 如果没有回调,则使用全局的降级服务
DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
if (null != degradeService) {
degradeService.onLost(context, postcard);
}
}
return null;
}
// 如果有回调函数,调用 onFound 方法
if (null != callback) {
callback.onFound(postcard);
}
// 如果不是绿色通道,则执行拦截器,可能会导致 ANR 问题
if (!postcard.isGreenChannel()) {
// 执行拦截器
interceptorService.doInterceptions(postcard, new InterceptorCallback() {
/**
* 继续处理
*
* @param postcard 路由元数据
*/
@Override
public void onContinue(Postcard postcard) {
// 调用实际的导航方法
_navigation(postcard, requestCode, callback);
}
/**
* 中断处理,管道将在此方法被调用时销毁。
*
* @param exception 中断原因
*/
@Override
public void onInterrupt(Throwable exception) {
// 如果有回调,调用 onInterrupt 方法
if (null != callback) {
callback.onInterrupt(postcard);
}
// 记录导航失败信息
logger.info(Consts.TAG, "导航失败,因拦截器中止 : " + exception.getMessage());
}
});
} else {
// 如果是绿色通道,直接执行导航
return _navigation(postcard, requestCode, callback);
}
return null;
}
最后会执行_navigation:
java
private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {
// 获取当前的上下文
final Context currentContext = postcard.getContext();
// 根据 Postcard 的类型执行不同的导航操作
switch (postcard.getType()) {
case ACTIVITY:
// 如果是 Activity 类型,构建 Intent 对象
final Intent intent = new Intent(currentContext, postcard.getDestination());
intent.putExtras(postcard.getExtras()); // 设置 extras 数据
// 设置 flags
int flags = postcard.getFlags();
if (0 != flags) {
intent.setFlags(flags); // 如果有 flags,设置到 intent 中
}
// 如果当前上下文不是 Activity,需要设置 FLAG_ACTIVITY_NEW_TASK
if (!(currentContext instanceof Activity)) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
// 设置 Action
String action = postcard.getAction();
if (!TextUtils.isEmpty(action)) {
intent.setAction(action); // 如果有 action,设置到 intent 中
}
// 在主线程中启动 Activity
runInMainThread(new Runnable() {
@Override
public void run() {
startActivity(requestCode, currentContext, intent, postcard, callback);
}
});
break;
case PROVIDER:
// 如果是 Provider 类型,直接返回对应的 Provider
return postcard.getProvider();
case BOARDCAST:
case CONTENT_PROVIDER:
case FRAGMENT:
// 如果是 Broadcast、ContentProvider 或 Fragment 类型
Class<?> fragmentMeta = postcard.getDestination();
try {
// 通过反射创建 Fragment 实例
Object instance = fragmentMeta.getConstructor().newInstance();
if (instance instanceof Fragment) {
((Fragment) instance).setArguments(postcard.getExtras()); // 设置 extras 数据
} else if (instance instanceof android.support.v4.app.Fragment) {
((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras());
}
// 返回创建的 Fragment 实例
return instance;
} catch (Exception ex) {
logger.error(Consts.TAG, "获取 Fragment 实例出错, " + TextUtils.formatStackTrace(ex.getStackTrace()));
}
case METHOD:
case SERVICE:
default:
// 如果是 METHOD 或 SERVICE 类型,或者未知类型,返回 null
return null;
}
return null;
}
已经到底啦!!