配置
Kotlin项目:
module App:
apply plugin: 'kotlin-kapt'
defaultConfig{
javaCompileOptions {
annotationProcessorOptions {
//AROUTER_MODULE_NAME必配项 用于拼接生成文件名 AROUTER_GENERATE_DOC
// AROUTER_GENERATE_DOC = enable 生成Json文档
// 生成的文档路径 : build/generated/source/apt/(debug or release)/com/alibaba/android/arouter/docs/arouter-map-of-${moduleName}.json
arguments = [AROUTER_MODULE_NAME:project_name,AROUTER_GENERATE_DOC:"enable"]
}
}
}
dependencies{
api 'com.alibaba:arouter-api:1.5.0'
kapt 'com.alibaba:arouter-compiler:1.2.2'
}
//项目根目录build.gradle
dependencies {
classpath "com.alibaba:arouter-register:1.0.2"
}
源码流程分析
三个关键阶段
自定义处理器工作流程:
自定义处理器源码分析:结构图
生成类的关系
调用类:
@Route(path = "/kotlin/test")
class KotlinTestActivity : Activity() {
@Autowired
@JvmField var name: String? = null
@Autowired
@JvmField var age: Int? = 0
override fun onCreate(savedInstanceState: Bundle?) {
ARouter.getInstance().inject(this) // Start auto inject.
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_kotlin_test)
content.text = "name = $name, age = $age"
}
}
ARouter生成类:
public class KotlinTestActivity$$ARouter$$Autowired implements ISyringe {
private SerializationService serializationService;
@Override
public void inject(Object target) {
serializationService = ARouter.getInstance().navigation(SerializationService.class);
KotlinTestActivity substitute = (KotlinTestActivity)target;
substitute.name = substitute.getIntent().getExtras() == null ? substitute.name : substitute.getIntent().getExtras().getString("name", substitute.name);
substitute.age = substitute.getIntent().getIntExtra("age", substitute.age);
}
}
ARouter.getInstance().inject(this)
这段代码最终会利用当前类名和规则,拼接成KotlinTestActivity$$ARouter$$Autowired
的全类名,然后利用反射传进对象。然后执行inject(this); 然后里面会初始化传输字段序列化服务,然后强转target,开始赋值数据
生成类文件的关系
由此可总结出下面整体工作流程
ARouter整体工作流程
运行时原理分析
初始化工作流程分析
//初始化
ARouter.init(getApplication());
_ARouter.init(application)
LogisticsCenter.init(mContext, executor)
LogisticsCenter.init(mContext, executor):
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context;
executor = tpe;
try {
long startInit = System.currentTimeMillis();
//load by plugin first
loadRouterMap();
if (registerByPlugin) {
logger.info(TAG, "Load router map by arouter-auto-register plugin.");
} else {
//1 生成文件所有文件的全类名字符串集合
Set<String> 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.
//2. 赋值集合
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>()));
}
logger.info(TAG, "Find router map finished, map size = " + routerMap.size() + ", cost " + (System.currentTimeMillis() - startInit) + " ms.");
startInit = System.currentTimeMillis();
//3. 遍历集合
for (String className : routerMap) {
// 这里装在了3中类别的文件: (1) 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);
//(2) Interceptor 文件
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
// Load interceptorMeta
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
// //(3) Provider 文件
} 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() + "]");
}
}
可以看出没加载AutoWired文件,也就是说@AutoWired注解字段 在inject()
时去创建对象赋值的。
反射找到对象并将Warehouse中的集合作为参数传递进入,把信息装载到内存。注意,这里只是加载了信息,但信息里面的具体内容并未创建。什么意思呢?以Provider为例:
public class ARouter$$Providers$$modulejava implements IProviderGroup {
@Override
public void loadInto(Map<String, RouteMeta> providers) {
providers.put("com.alibaba.android.arouter.demo.service.HelloService", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/yourservicegroupname/hello", "yourservicegroupname", null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.facade.service.SerializationService", RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, "/yourservicegroupname/json", "yourservicegroupname", null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.demo.module1.testservice.SingleService", RouteMeta.build(RouteType.PROVIDER, SingleService.class, "/yourservicegroupname/single", "yourservicegroupname", null, -1, -2147483648));
}
}
反射生成ARouter$$Providers$$modulejava
对象,但我们用的是com.alibaba.android.arouter.demo.service.HelloService
HelloService这个具体类。但这个类这是并未实例化,只有用到的时候才回去实例化创建。其他同理。
LogisticsCenter.init(mContext, executor):
还用到两个重要的类和方法: ClassUtils
/Warehouse
1. ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
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() {
//Dex文件
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();
//核心判断 packageName是我们上面传入的参数ROUTE_ROOT_PAKCAGE = "com.alibaba.android.arouter.routes"
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();
}
}
});
}
parserCtl.await();
Log.d(Consts.TAG, "Filter " + classNames.size() + " classes by packageName <" + packageName + ">");
return classNames;
}
如果初始化加载流程没有走完,路由操作将会阻塞,直到加载流程完成
Warehouse
相当于一个加载信息装载的容器类
class Warehouse {
// Cache route and metas
//groupsIndex 装载ARouter$$Root$$moduleName 中的Root文件 <groupName,Group.class>
static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
//routes 按需加载完成后 把加载的数据存到routes 集合中,等项目中用到的时候,找到集合中需要的元素 再去实例化对象
static Map<String, RouteMeta> routes = new HashMap<>();
// Cache provider
//项目中用到的时候,找到集合中需要的元素 再去实例化对象
static Map<Class, IProvider> providers = new HashMap<>();
// //每个服务的原始信息加载完成后存放到这里
static Map<String, RouteMeta> providersIndex = new HashMap<>();
// Cache 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();
}
}
初始化总结:
- 通过
ClassUtils.getFileNameByPackageName
方法,加载所有生成的dex文件,遍历每个dex中的java文件,并进行com.alibaba.android.arouter.routes
包名匹配,这必然是ARouter帮我们生成的 -
CountDownLatch parserCtl
开启线程池,进行同步操作,在没有遍历所有生成的java并装载进Set集合
,navigation路由操作将会阻塞 - 遍历Set集合 对
Warehouse
中的三个重要Map进行赋值,可以看到Warehouse
中都是static
修饰的,这样它就像个仓库,对外提供数据,同时通过反射调用方法也能看出,这里只是对信息进行加载,里面Group
组中的RouteMate并未创建
路由过程源码分析
ARouter.getInstance()
.build("/kotlin/test")
.withString("name", "老王")
.withInt("age", 23)
.navigation();
.build("/kotlin/test")
查找分组,构建Postcard对象。
protected Postcard build(String path) {
if (TextUtils.isEmpty(path)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
// 重定向路由路径Service 如果想自定义 则实现PathReplaceService
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
//通过forString(path) 返回修改后的Path
path = pService.forString(path);
}
//继续往下走
return build(path, extractGroup(path), true);
}
}
extractGroup(path):
private String extractGroup(String path) {
if (TextUtils.isEmpty(path) || !path.startsWith("/")) {
throw new HandlerException(Consts.TAG + "Extract the default group failed, the path must be start with '/' and contain more than 2 '/'!");
}
try {
//关键代码 defaultGroup 默认分组 以路由路径 第一个节点为分组名称
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;
}
} catch (Exception e) {
logger.warning(Consts.TAG, "Failed to extract default group! " + e.getMessage());
return null;
}
}
build(path, extractGroup(path), true);:
protected Postcard build(String path, String group, Boolean afterReplace) {
if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
if (!afterReplace) {
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
}
return new Postcard(path, group);
}
}
创建:Postcard
对象
.withString("name", "老王").withInt("age", 23)
构建参数mBundle对象。
.navigation();
运行在异步线程中
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.
return null;
}
// Set context to postcard.
postcard.setContext(null == context ? mContext : context);
try {
//给Postcard赋值其他数据
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.
//反射创建全局降级服务 回调 onLost方法
DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
if (null != degradeService) {
degradeService.onLost(context, postcard);
}
}
return null;
}
if (null != callback) {
//回调路由成功的方法
callback.onFound(postcard);
}
//如果绿色通道为false 则添加拦截器
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 {
//继续处理完成后继续向下执行
return _navigation(postcard, requestCode, callback);
}
return null;
}
LogisticsCenter.completion(postcard);
public synchronized static void completion(Postcard postcard) {
if (null == postcard) {
throw new NoRouteFoundException(TAG + "No postcard!");
}
//routes是加载信息 但为实例化对象 之前提过 所以这里第一次肯定为空
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) {
// Maybe its does't exist, or didn't load.
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.
try {
if (ARouter.debuggable()) {
logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] starts loading, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
}
//对Warehouse.routes初始化 实例化分组对象 获取数据
addRouteGroupDynamic(postcard.getGroup(), null);
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());
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());
}
//类型判断 PROVIDER类型实例化对象 PROVIDER/FRAGMENT默认开启通道,不经过拦截
switch (routeMeta.getType()) {
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();
IProvider instance = Warehouse.providers.get(providerMeta);
if (null == instance) { // There's no instance of this provider
IProvider provider;
try {
provider = providerMeta.getConstructor().newInstance();
provider.init(mContext);
Warehouse.providers.put(providerMeta, provider);
instance = provider;
} catch (Exception e) {
logger.error(TAG, "Init provider failed!", e);
throw new HandlerException("Init provider failed!");
}
}
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.
//对Warehouse.routes初始化 实例化分组对象 获取数据
Warehouse.groupsIndex.get(groupName).getConstructor().newInstance().loadInto(Warehouse.routes);
Warehouse.groupsIndex.remove(groupName);
}
// cover old group.
if (null != group) {
group.loadInto(Warehouse.routes);
}
}
_navigation(postcard, requestCode, callback);
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)) {
//context如果是Application 那么新建任务栈
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() {
//Activity则调用startActivity
startActivity(requestCode, currentContext, intent, postcard, callback);
}
});
break;
case PROVIDER:
//返回LogisticsCenter.completion(postcard)方法中创建的对象
return postcard.getProvider();
case BOARDCAST:
case CONTENT_PROVIDER:
case FRAGMENT:
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());
}
//反射返回fragment实例
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;
}
路由结束。
注意:通过ARouter源码分析,IProvider接口类型文件会生成两份,一份是ARouter{groupName},继承IRouteGroup,另一份是ARouter{groupName},继承IProviderGroup。类文件的内容是一摸一样的。为什么要这么设计呢?个人理解。答案是RoutProcesser里面不仅收集@Route注解的类,并声称信息。同时还会生成json说明文件。所以ARouter{groupName}类型是json文件生成用的。是运行时调用数据收集用的。