前言
在前面的文章组件化中用到了ARouter框架,它是专门用来做组件化改造的,官方定义如下:
一个用于帮助Android App进行组件化改造的框架 ------ 支持模块间的路由、通信、解耦
什么是路由?可能你首先联想到的是路由器,路由器根据路由表来转发数据包,路由表决定了数据传输的路径。ARouter就相当于一个路由器,让无依赖的双方可以通信。
下面我们就通过源码来分析ARouter的实现原理,本文ARouter源码基于:com.alibaba:arouter-api:1.5.2。
源码解析
ARouter的简单使用在前面的文章中已有介绍,使用前需要在Application中用ARouter.init(this)
来初始化ARouter,下面我们就从这里开始分析源码:
java
public final class ARouter {
private volatile static boolean hasInit = false;
public static void init(Application application) {
//判断是否已经初始化
if (!hasInit) {
...
//调用_ARouter.init()初始化
hasInit = _ARouter.init(application);
if (hasInit) {
_ARouter.afterInit();
}
...
}
}
}
首先判断有没有初始化过,已经初始化过后把hasInit置为true,ARouter的init()方法调用了_ARouter的init()方法:
java
final class _ARouter {
private volatile static boolean hasInit = false;
private volatile static ThreadPoolExecutor executor = DefaultPoolExecutor.getInstance();
protected static synchronized boolean init(Application application) {
mContext = application;
LogisticsCenter.init(mContext, executor);
...
hasInit = true;
return true;
}
}
_ARouter的init()方法中出现了LogisticsCenter这个类,这个类是做什么的呢?看类名是 "物流中心"的意思 ,继续看下它的init()方法做了些什么:
java
public class LogisticsCenter {
//加载内存中的路由信息
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
...
//从插件中加载路由表
loadRouterMap();
if (registerByPlugin) {
logger.info(TAG, "Load router map by arouter-auto-register plugin.");
} else {
Set<String> routerMap;
// It will rebuild router map every times when debuggable.
// 调用ARouter.openDebug()、第一次运行、版本更新都会更新routerMap。
// PackageUtils.isNewVersion()中通过SharedPreference存储的App的
// versionName和versionCode来判断版本是否有更新
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
// These class was generated by arouter-compiler.
// 这里就是从dex中获取com.alibaba.android.arouter.routes包下的.class
// ROUTE_ROOT_PAKCAGE = "com.alibaba.android.arouter.routes";
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
if (!routerMap.isEmpty()) {
//把routerMap存入SharedPreference
context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
}
//更新SharedPreference中存储的App的版本信息
PackageUtils.updateVersion(context); // Save new version name when router map update finishes.
} else {
//直接从SP中拿routerMap,就是前面保存在SP中的routerMap
routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
}
...
// 根据className,来实例化不同的对象并调用loadInto()方法。
for (String className : routerMap) {
//以com.alibaba.android.arouter.routes.ARouter$$Root开头
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);
}
//以com.alibaba.android.arouter.routes.ARouter$$Interceptors开头
else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
// Load interceptorMeta
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
}
//以com.alibaba.android.arouter.routes.ARouter$$Providers开头
else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
// Load providerIndex
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
}
}
/**
* arouter-auto-register plugin will generate code inside this method
* call this method to register all Routers, Interceptors and Providers
*/
private static void loadRouterMap() {
registerByPlugin = false;
// auto generate register code by gradle plugin: arouter-auto-register
// looks like below:
// registerRouteRoot(new ARouter..Root..modulejava());
// registerRouteRoot(new ARouter..Root..modulekotlin());
}
}
从上面的代码可以看出,这段代码就是加载路由表的核心代码,上面有注释标出了一些代码的业务逻辑,这里再挑出几个比较难理解的地方重点讲解一下,首先是这句代码loadRouterMap(),注释上说的是从插件中加载路由表,什么意思呢?就是如果我们想缩短ARouter初始化的时间,可以用ARouter的Gradle插件,这个插件能自动加载路由表,这样ARouter初始化的时候就不需要读取类的信息,从而缩短初始化时间。
第22行从dex中获取所有com.alibaba.android.arouter.routes包下的.class文件放入routerMap,从第36行开始遍历routerMap,分成了3种情况:
- 如果.class文件以com.alibaba.android.arouter.routes.ARouter$$Root开头则反射创建该实例,并调用该实例的loadInto()方法,loadInto()方法传入的参数为Warehouse.groupsIndex;
- 如果.class文件以com.alibaba.android.arouter.routes.ARouter$$Interceptors开头则反射创建该实例,并调用该实例的loadInto()方法,loadInto()方法传入的参数为Warehouse.interceptorsIndex;
- 如果.class文件以com.alibaba.android.arouter.routes.ARouter$$Providers开头则反射创建该实例,并调用该实例的loadInto()方法,loadInto()方法传入的参数为Warehouse.providersIndex;
在前面的文章中,选择集成调试模式,Sync Project后,在download模块下的build\intermediates\javac\debug\classes\com\alibaba\android\arouter\routes目录下共生成了3个.class文件,这些文件是由注解处理器生成的:
ruby
ARouter$$Group$$download.class
ARouter$$Providers$$download.class
ARouter$$Root$$download.class
其中ARouter$$Root$$download.class以com.alibaba.android.arouter.routes.ARouter$$Root开头,点开ARouter$$Root$$download.class,其代码如下:
java
public class ARouter$$Root$$download implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("download", ARouter$$Group$$download.class);
}
}
其loadInto()方法将ARouter$$Group$$download.class存入routes中,routes为Map<String, Class<? extends IRouteGroup>>类型,key为download,routes就是上面的Warehouse.groupsIndex。Warehouse是存储路由信息的仓库,其代码如下:
java
class Warehouse {
//用于缓存实现了IRouteGroup接口的类,key为path的第一级
static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
//用于缓存路由元信息,key为path
static Map<String, RouteMeta> routes = new HashMap<>();
//缓存实现了IProvider接口的类的实例,key为IProvider实现类的class
static Map<Class, IProvider> providers = new HashMap<>();
//缓存实现了IProvider类的路由元信息,key为继承了IProvider的接口的路径
static Map<String, RouteMeta> providersIndex = new HashMap<>();
//缓存interceptor
static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");
static List<IInterceptor> interceptors = new ArrayList<>();
static void clear() {
routes.clear();
groupsIndex.clear();
providers.clear();
providersIndex.clear();
interceptors.clear();
interceptorsIndex.clear();
}
}
这样就把ARouter$$Group$$download.class存入了Warehouse.groupsIndex这个HashMap中,key为download。
接下来分析跳转的代码,跳转代码如下:
java
ARouter.getInstance().build(path).navigation();
跟进去:
java
public final class ARouter {
private volatile static ARouter instance = null;
private ARouter() {
}
public static ARouter getInstance() {
if (!hasInit) {
throw new InitException("ARouter::Init::Invoke init(context) first!");
} else {
if (instance == null) {
synchronized (ARouter.class) {
if (instance == null) {
instance = new ARouter();
}
}
}
return instance;
}
}
public Postcard build(String path) {
return _ARouter.getInstance().build(path);
}
}
在getInstance()方法中先判断hasInit是否为true,如果为false抛出异常,接下来用单例模式创建ARouter实例,build()方法调用了_ARouter.getInstance().build(path):
java
final class _ARouter {
private _ARouter() {
}
protected static _ARouter getInstance() {
if (!hasInit) {
throw new InitException("ARouterCore::Init::Invoke init(context) first!");
} else {
if (instance == null) {
synchronized (_ARouter.class) {
if (instance == null) {
instance = new _ARouter();
}
}
}
return instance;
}
}
/**
* Build postcard by path and default group
*/
protected Postcard build(String path) {
if (TextUtils.isEmpty(path)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
//替换path
path = pService.forString(path);
}
return build(path, extractGroup(path), true);
}
}
//从传入的path中解析出group
private String extractGroup(String path) {
...
String defaultGroup = path.substring(1, path.indexOf("/", 1));
if (TextUtils.isEmpty(defaultGroup)) {
throw new HandlerException(Consts.TAG + "Extract the default group failed! There's nothing between 2 '/'!");
} else {
return defaultGroup;
}
...
}
/**
* Build postcard by path and group
* 通过path和group拿到Postcard
*/
protected Postcard build(String path, String group, Boolean afterReplace){
...
return new Postcard(path, group);
}
}
第25行先判断,如果path为空抛出异常,如果想替换path,可以写一个类实现PathReplaceService接口。接着调用第33行的build()方法,这里从path中解析出group,最后新建Postcard实例,传入path和group。Postcard从名字翻译过来是明信片的意思,其承载了一次路由需要的所有信息,Postcard代码如下:
java
/**
* A container that contains the roadmap.
*/
public final class Postcard extends RouteMeta {
private Uri uri;
private Bundle mBundle; // Data to transform
public Postcard() {
this(null, null);
}
public Postcard(String path, String group) {
this(path, group, null, null);
}
public Postcard(String path, String group, Uri uri, Bundle bundle) {
setPath(path);
setGroup(group);
setUri(uri);
this.mBundle = (null == bundle ? new Bundle() : bundle);
}
/**
* Navigation to the route with path in postcard.
* No param, will be use application context.
*/
public Object navigation() {
return navigation(null);
}
/**
* Navigation to the route with path in postcard.
*
* @param context Activity and so on.
*/
public Object navigation(Context context) {
return navigation(context, null);
}
/**
* Navigation to the route with path in postcard.
*
* @param context Activity and so on.
*/
public Object navigation(Context context, NavigationCallback callback) {
return ARouter.getInstance().navigation(context, this, -1, callback);
}
}
Postcard继承自路由元信息RouteMeta,传入path和group实际调用的是RouteMeta的setPath()方法和setGroup()方法,对RouteMeta中的path和group成员变量赋值:
java
/**
* It contains basic route information.
*/
public class RouteMeta {
private RouteType type; // Type of route
private Element rawType; // Raw type of route
private Class<?> destination; // Destination
private String path; // Path of route
private String group; // Group of route
public String getPath() {
return path;
}
public RouteMeta setPath(String path) {
this.path = path;
return this;
}
public String getGroup() {
return group;
}
public RouteMeta setGroup(String group) {
this.group = group;
return this;
}
}
接下来看看navigation()方法做了什么?
java
public final class Postcard extends RouteMeta {
public Object navigation() {
return navigation(null);
}
/**
* Navigation to the route with path in postcard.
*
* @param context Activity and so on.
*/
public Object navigation(Context context) {
return navigation(context, null);
}
/**
* Navigation to the route with path in postcard.
*
* @param context Activity and so on.
*/
public Object navigation(Context context, NavigationCallback callback) {
return ARouter.getInstance().navigation(context, this, -1, callback);
}
}
调用了ARouter的navigation()方法并传入Postcard实例:
java
public final class ARouter {
/**
* Launch the navigation.
*/
public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) {
return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);
}
}
这里又调用了_ARouter的navigation()方法:
java
final class _ARouter {
/**
* Use router navigation.
*/
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
//若有PretreatmentService的实现,就进行预处理,可以在真正路由前进行一些判断然后中断路由。
PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
// Pretreatment failed, navigation canceled.
return null;
}
// Set context to postcard.
postcard.setContext(null == context ? mContext : context);
try {
//标记1,完善postcard信息
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
logger.warning(Consts.TAG, ex.getMessage());
//没有找到路由的回调
if (null != callback) {
callback.onLost(postcard);
} else {
// No callback for this invoke, then we use the global degrade service.
DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
if (null != degradeService) {
degradeService.onLost(context, postcard);
}
}
return null;
}
//找到路由的回调
if (null != callback) {
callback.onFound(postcard);
}
//不是绿色通道,要走拦截器
if (!postcard.isGreenChannel()) { // It must be run in async thread, maybe interceptor cost too mush time made ANR.
interceptorService.doInterceptions(postcard, new InterceptorCallback() {
/**
* Continue process
*
* @param postcard route meta
*/
//拦截器处理结果:继续路由
@Override
public void onContinue(Postcard postcard) {
_navigation(postcard, requestCode, callback);
}
/**
* Interrupt process, pipeline will be destory when this method called.
*
* @param exception Reson of interrupt.
*/
//拦截器处理结果:中断路由,回调中断
@Override
public void onInterrupt(Throwable exception) {
if (null != callback) {
callback.onInterrupt(postcard);
}
logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());
}
});
} else {
//标记2,绿色通道,不走拦截器,获取路由结果
return _navigation(postcard, requestCode, callback);
}
return null;
}
}
我们先看看标记1处代码做了什么:
java
public class LogisticsCenter {
/**
* Completion the postcard by route metas
*
* @param postcard Incomplete postcard, should complete by this method.
*/
public synchronized static void completion(Postcard postcard) {
//通过path去Warehouse.routes中拿RouteMeta
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
//如果RouteMeta为空
if (null == routeMeta) {
// Maybe its does't exist, or didn't load.
//如果Warehouse.groupsIndex中没有这个group,抛出异常
if (!Warehouse.groupsIndex.containsKey(postcard.getGroup())) {
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.
//添加RouteMeta到Warehouse.routes
addRouteGroupDynamic(postcard.getGroup(), null);
//重新执行completion()方法
completion(postcard); // Reload
}
} else {
//把routeMeta里面的配置传递给postcard
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()) {
//如果类型是PROVIDER
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();
//去Warehouse.providers中获取实例
IProvider instance = Warehouse.providers.get(providerMeta);
//如果instance为null
if (null == instance) { // There's no instance of this provider
IProvider provider;
try {
//反射创建实例
provider = providerMeta.getConstructor().newInstance();
provider.init(mContext);
//存入Warehouse.providers
Warehouse.providers.put(providerMeta, provider);
instance = provider;
} catch (Exception e) {
logger.error(TAG, "Init provider failed!", e);
throw new HandlerException("Init provider failed!");
}
}
//传递给postcard
postcard.setProvider(instance);
postcard.greenChannel(); // Provider should skip all of interceptors
break;
case FRAGMENT:
postcard.greenChannel(); // Fragment needn't interceptors
default:
break;
}
}
}
public synchronized static void addRouteGroupDynamic(String groupName, IRouteGroup group) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
if (Warehouse.groupsIndex.containsKey(groupName)){
// If this group is included, but it has not been loaded
// load this group first, because dynamic route has high priority.
//调用实例的loadInto()方法添加到Warehouse.routes
Warehouse.groupsIndex.get(groupName).getConstructor().newInstance().loadInto(Warehouse.routes);
//从Warehouse.groupsIndex中移除key为groupName的数据
Warehouse.groupsIndex.remove(groupName);
}
// cover old group.
if (null != group) {
group.loadInto(Warehouse.routes);
}
}
}
上面的completion()方法先去Warehouse.routes中拿RouteMeta,Warehouse就是前面存储路由信息的仓库,Warehouse.routes是其中缓存路由元信息的HashMap。判断如果RouteMeta为null,调用addRouteGroupDynamic()方法,去Warehouse.groupsIndex中取出缓存的类,使用反射创建该类的实例,然后调用其loadInto()方法,这样就调用了ARouter$$Group$$download.class的loadInto()方法,其代码如下:
java
public class ARouter$$Group$$download implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/download/DownloadActivity", RouteMeta.build(RouteType.ACTIVITY, DownloadActivity.class, "/download/downloadactivity", "download", null, -1, -2147483648));
atlas.put("/download/service", RouteMeta.build(RouteType.PROVIDER, DownloadServiceImpl.class, "/download/service", "download", null, -1, -2147483648));
}
}
其loadInto()方法把RouteMeta通过build()方法创建的实例添加到Warehouse.routes中。
回到前面的代码,接着又调用了一次completion()方法,此时routeMeta不再为null,将routeMeta的配置信息(destination、type等信息)传递给postcard,如果routeMeta类型是PROVIDER,会反射创建实例并存入Warehouse.providers。最后执行标记2处的代码开始准备跳转,标记2处代码如下:
java
final class _ARouter {
private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {
final Context currentContext = postcard.getContext();
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 (0 != flags) {
intent.setFlags(flags);
}
// Non activity, need FLAG_ACTIVITY_NEW_TASK
if (!(currentContext instanceof Activity)) {
intent.addFlags(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:
//Broadcast、ContentProvider、Fragment,都是使用postcard.getDestination()反射创建实例
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;
}
}
上面的代码没什么好说的,先判断postcard的类型,如果是Activity类型,就把postcard里面的参数传递给Intent,最终还是调用startActivity()方法进行跳转;如果是Provider类型,直接通过postcard.getProvider()拿到IProvier的实现类的单例;Broadcast、ContentProvider、Fragment都是使用postcard.getDestination()反射创建实例并返回。
参考资料: juejin.cn/post/720004...