ARouter主要是用于组件化开发中的组件之间的通信。
从ARouter的用法透析组件通信原理
// 需要接收消息的组件,要注册Router注解
@Route(group = "app", path = "/app/Component")
public class ComponentActivity extends AppCompatActivity {
// 通过Router直接发送事件
// 构造参数
Bundle bundle = new Bundle();
bundle.putString("data", "data from other module");
// 发送事件
ARouter.getInstance()
.build("/app/Component") // 跳转页面路由地址
.with(bundle) // 传参
.navigation();
ARouter的实现原理
在代码里加入的@Route注解,会在编译时期通过apt生成一些存储path和activity.class映射关系的类文件,然后app进程启动的时候会加载这些类文件,把保存这些映射关系的数据读到内存里(保存在map里),然后在进行路由跳转的时候,通过build()方法传入要到达页面的路由地址,ARouter会通过它自己存储的路由表找到路由地址对应的Activity.class(activity.class = map.get(path)),然后new Intent(context, activity.Class),当调用ARouter的withString()方法它的内部会调用intent.putExtra(String name, String value),调用navigation()方法,它的内部会调用startActivity(intent)进行跳转,这样便可以实现两个相互没有依赖的module顺利的启动对方的Activity了。
1.ARouter.init(this);的执行过程
init()方法源码如下:
# ARouter.java
/**
* Init, it must be call before used router.
*/
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.");
}
}
其中调用了_ARouter的init方法,最终调用到LogisticsCenter的init()
# _ARouter
protected static synchronized boolean init(Application application) {
mContext = application;
LogisticsCenter.init(mContext, executor);
logger.info(Consts.TAG, "ARouter init success!");
hasInit = true;
// It's not a good idea.
// if (Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
// application.registerActivityLifecycleCallbacks(new AutowiredLifecycleCallback());
// }
return true;
}
#LogisticsCenter
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context;
executor = tpe;
try {
long startInit = System.currentTimeMillis();
//billy.qi modified at 2017-12-06
//load by plugin first
loadRouterMap();
if (registerByPlugin) {
logger.info(TAG, "Load router map by arouter-auto-register plugin.");
} else {
Set routerMap;
// It will rebuild router map every times when debuggable.
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()));
}
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);
}
}
}
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) {
throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
}
}
在该方法中只做了一件事,导入生成的路由表。
ARouter.getInstance().build(path)
在build的时候,传入要跳转的路由地址,build()方法会返回一个Postcard对象,我们称之为跳卡。Postcard里面保存着跳转的信息。
public final class Postcard extends RouteMeta {
// Base
private Uri uri;
private Object tag; // A tag prepare for some thing wrong.
private Bundle mBundle; // Data to transform
private int flags = -1; // 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;
}
navigation
Postcard 的 navigation() 方法又会调用到 _ARouter 的以下方法来完成 Activity 的跳转。
final class _ARouter {
/**
* Use router navigation.
*
* @param context Activity or null.
* @param postcard Route metas
* @param requestCode RequestCode
* @param callback cb
*/
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
// Pretreatment failed, navigation canceled.
//用于执行跳转前的预处理操作,可以通过 onPretreatment 方法的返回值决定是否取消跳转
return null;
}
try {
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
//没有找到匹配的目标类
//下面就执行一些提示操作和事件回调通知
logger.warning(Consts.TAG, ex.getMessage());
if (debuggable()) {
// Show friendly tips for user.
runInMainThread(new Runnable() {
@Override
public void run() {
Toast.makeText(mContext, "There's no route matched!\n" +
" Path = [" + postcard.getPath() + "]\n" +
" Group = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show();
}
});
}
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(context, 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 {
//开启了绿色通道,直接跳转,不需要遍历拦截器
return _navigation(context, postcard, requestCode, callback);
}
return null;
}
//由于本例子的目标页面是 Activity,所以只看 ACTIVITY 即可
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
//Destination 就是指向目标 Activity 的 class 对象
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;
··· //省略其它类型判断
}
return null;
}
}
复制代码
navigation 方法的重点在于 LogisticsCenter.completion(postcard) 这一句代码。它反射调用 ARouteraccount 的 loadInto 方法,获取详细的路由对应信息。
completion 方法就是用来获取详细的路由对应信息的。该方法会通过 postcard 携带的 path 和 group 信息从 Warehouse 取值,如果值不为 null 的话就将信息保存到 postcard 中,如果值为 null 的话就抛出 NoRouteFoundException。
/**
* Completion the postcard by route metas
*
* @param postcard Incomplete postcard, should complete by this method.
*/
public synchronized static void completion(Postcard postcard) {
if (null == postcard) {
throw new NoRouteFoundException(TAG + "No postcard!");
}
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) { //为 null 说明目标类不存在或者是该 group 还未加载过
Class extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup()); // Load route meta.
if (null == groupMeta) {
//groupMeta 为 null,说明 postcard 的 path 对应的 group 不存在,抛出异常
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()));
}
//会执行到这里,说明此 group 还未加载过,那么就来反射加载 group 对应的所有 path 信息
//获取后就保存到 Warehouse.routes
IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
iGroupInstance.loadInto(Warehouse.routes);
//移除此 group
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() + "]");
}
//重新执行一遍
completion(postcard); // Reload
}
} else {
//拿到详细的路由信息了,将这些信息存到 postcard 中
postcard.setDestination(routeMeta.getDestination());
postcard.setType(routeMeta.getType());
postcard.setPriority(routeMeta.getPriority());
postcard.setExtra(routeMeta.getExtra());
//省略一些和本例子无关的代码
···
}
}
复制代码
回到_ARouter的navigation()方法来,可以清楚的看到最终人仍然是通过Intent(context,MainActivity.class)
的方式来实现跳转的。