本文为过往笔记整理, 在此只作记录,不做严谨的技术分享。
阿里的ARouter,问几个你“不会”的问题! 利用APT实现Android编译时注解
“终于懂了” 系列:组件化框架 ARouter 完全解析-系列
基本使用
集成
gradle配置
android {
defaultConfig {
...
//纯Java项目
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName()]
}
}
//Kotlin
kapt {
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
}
}
}
//Java项目
annotationProcessor configDependencies.arout_compiler
//Kotlin项目
apply plugin: 'kotlin-kapt'
kapt configDependencies.arout_compiler//不能用annotationProcessor,否则无效
IProvider
解耦,暴露服务
interface HelloProvider:IProvider{
fun sayHello(name:String):String
}
// 实现接口
@Route(path = "/common/hello", name = "测试服务")
class HelloProviderImpl : HelloProvider {
override fun sayHello(name: String): String {
return "hello, $name"
}
override fun init(context: Context) {}
}
//获取服务:虽然方式一可以不用配置path路径,但还是要使用@Rout注解标注的,否则找不到
//方式一
ARouter.getInstance().navigation(HelloProvider::class.java)
?.initApp(this)
//方式二
(ARouter.getInstance().build("/common/hello").navigation() as HelloProvider)
?.initApp(this)
PathReplaceService
替换URL
// 实现PathReplaceService接口
@Route(path = "/yourservicegroupname/pathReplaceService")
class PathReplaceServiceImpl : PathReplaceService {
/**
* For normal path.
* @param path raw path
*/
override fun forString(path: String): String {
if (path == "/login/loginActivity") {
return "/share/shareActivity"
}
return path // 按照一定的规则处理之后返回处理后的结果
}
/**
* For uri type.
* @param uri raw uri
*/
override fun forUri(uri: Uri?): Uri? {
return null // 按照一定的规则处理之后返回处理后的结果
}
override fun init(context: Context?) {
}
}
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) {
//navigation会在providersIndex中寻找自定义路径替换服务
PathReplaceService pService = ARouter.getInstance()
.navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
}
return new Postcard(path, group);
}
}
PretreatmentService
预处理服务,早于拦截器
@Route(path = "/yourservicegroupname/pretreatmentService")
class PretreatmentServiceImpl : PretreatmentService {
override fun onPretreatment(context: Context, postcard: Postcard): Boolean {
if (postcard.path == "/share/ShareActivity") {
val userInfo = DataSource.getInstance(ArouterApplication.application).getUserInfo()
if (TextUtils.isEmpty(userInfo)) {
Toast.makeText(application, "还没有登录",LENGTH_SHORT).show()
return false// 跳转前预处理,如果需要自行处理跳转,该方法返回 false 即可
}
}
return true
}
override fun init(context: Context) {}
}
在路由navigation之前进行干扰路由,比如干扰在分享之前判断有没有登录
protected Object navigation(Context ct,Postcard pd, int re, NavigationCallback ck) {
PretreatmentService pretreatmentService = ARouter.getInstance()
.navigation(PretreatmentService.class);
if (null != pretreatmentService
&& !pretreatmentService.onPretreatment(context, postcard)) {
// Pretreatment failed, navigation canceled.
return null;
}
...
}
DegradeService
全局降级策略,没有找到路由地址时调用
@Route(path = "/yourservicegroupname/DegradeServiceImpl")
class DegradeServiceImpl : DegradeService {
override fun onLost(context: Context, postcard: Postcard) {
Log.d("DegradeServiceImpl", "没有找到该路由地址:${postcard.path}")
}
override fun init(context: Context?) {}
}
navigation中未找到路径,exception catch中:
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);
}
}
@Interceptor
在跳转之间执行,多个拦截器会按优先级顺序依次执行
@Interceptor(priority = 8, name = "登录拦截")
class LoginInterceptor : IInterceptor {
override fun process(postcard: Postcard, callback: InterceptorCallback) {
val path = postcard.path
if (path == "/share/shareActivity") {
val userInfo = DataSource.getInstance(ArouterApplication.application).getUserInfo()
if (TextUtils.isEmpty(userInfo)) {
callback.onInterrupt(Throwable("还没有登录,去登陆"))
} else {
callback.onContinue(postcard)
}
}else{
callback.onContinue(postcard)
}
}
override fun init(context: Context?) {
// 拦截器的初始化,会在sdk初始化的时候调用该方法,仅会调用一次
Log.d("LoginInterceptor", "LoginInterceptor初始化了")
}
}
@Autowired
@Autowired注解使用,得在类的初始化中使用ARouter.getInstance().inject(this)
- 基本数据类型:正常存入Bundle
- 自定义数据类型:自定义SerializationService解析为Json,以String方式存入Bundle。取值到json后再解析为自定义数据类型
//携带参数跳转
ARouter.getInstance().build(ARoutTable.BASE_ACT_TARGET)
.withString("info", "跳转携带参数")
.withString("arg1", "arg1")
.withInt("arg2", 2)
.withBoolean("arg3", true)
.withObject("testBean", TestBean("withObject"))
.withObject("listBean", listBean)
.withObject("mapStr", mapStr)
.navigation()
//目标类接收
@Autowired(name = "arg1")
String arg;
@Autowired
int arg2;
@Autowired
boolean arg3;
@Autowired
TestBean testBean;
@Autowired
List<TestBean> listBean;
@Autowired
Map<String, String> mapStr;
@Autowired
ModuleLoginProvider moduleLoginProvider;
存储参数:
//#Postcard
//基本数据类型
public Postcard withString(@Nullable String key, @Nullable String value) {
mBundle.putString(key, value);
return this;
}
//自定义类
public Postcard withObject(@Nullable String key, @Nullable Object value) {
serializationService = ARouter.getInstance().navigation(SerializationService.class);
mBundle.putString(key, serializationService.object2Json(value));//解析为json,作为String存储
return this;
}
//#SerializationService
//传递自定义对象时,需新建SerializationService类,并自定义解析方式
@Route(path = ARoutTable.BASE_SERVICE_SERIALIZATION)
class JsonServiceImpl: SerializationService {
private lateinit var mGson: Gson
override fun init(context: Context?) {
KLog.d("", "全局序列化服务")
mGson = Gson()
}
//跳转传参时:将自定义类参数,转为json,以String类型存入Bundle
override fun object2Json(instance: Any?): String {
return mGson.toJson(instance).apply {
KLog.d("", "instance: $instance, result: $this")
}
}
override fun <T : Any?> json2Object(input: String?, clazz: Class<T>?): T {
return mGson.fromJson(input, clazz).apply {
KLog.d("", "input: $input, clazz: $clazz, result: $this")
}
}
//inject注解时:将json转为自定义类型
override fun <T : Any?> parseObject(input: String?, clazz: Type?): T {
val result: T = mGson.fromJson(input, clazz)
KLog.d("", "input: $input, clazz: $clazz, result: $result")
return result
}
}
//将对象转换为Json
[object2Json ] instance: TestBean{name='withObject'}, result: {"age":18,"name":"withObject"}
[object2Json ] instance: [TestBean{name='list_0'}, TestBean{name='list_1'}, TestBean{name='list_2'}, TestBean{name='list_3'}, TestBean{name='list_4'}, TestBean{name='list_5'}], result: [{"age":18,"name":"list_0"},{"age":18,"name":"list_1"},{"age":18,"name":"list_2"},{"age":18,"name":"list_3"},{"age":18,"name":"list_4"},{"age":18,"name":"list_5"}]
[object2Json ] instance: {str_key=str_value}, result: {"str_key":"str_value"}
//解析Json转为自定义类
[parseObject ] input: {"age":18,"name":"withObject"}, clazz: class com.varmin.modulebase.arout.TestBean, result: TestBean{name='withObject'}
[parseObject ] input: [{"age":18,"name":"list_0"},{"age":18,"name":"list_1"},{"age":18,"name":"list_2"},{"age":18,"name":"list_3"},{"age":18,"name":"list_4"},{"age":18,"name":"list_5"}], clazz: java.util.List<com.varmin.modulebase.arout.TestBean>, result: [TestBean{name='list_0'}, TestBean{name='list_1'}, TestBean{name='list_2'}, TestBean{name='list_3'}, TestBean{name='list_4'}, TestBean{name='list_5'}]
[parseObject ] input: {"str_key":"str_value"}, clazz: java.util.Map<java.lang.String, java.lang.String>, result: {str_key=str_value}
参数:解析、赋值
- 把json数据通过SerializationService转换为自定义类对象
public class ARoutTargetActivity$$ARouter$$Autowired implements ISyringe {
private SerializationService serializationService;
@Override
public void inject(Object target) {
serializationService = ARouter.getInstance().navigation(SerializationService.class);
ARoutTargetActivity substitute = (ARoutTargetActivity)target;
//基本数据类型赋值
substitute.arg = substitute.getIntent().getExtras() == null ? substitute.arg : substitute.getIntent().getExtras().getString("arg1", substitute.arg);
substitute.arg2 = substitute.getIntent().getIntExtra("arg2", substitute.arg2);
substitute.arg3 = substitute.getIntent().getBooleanExtra("arg3", substitute.arg3);
//自定义数据类型解析
if (null != serializationService) {
substitute.testBean = serializationService.parseObject(substitute.getIntent().getStringExtra("testBean"), new com.alibaba.android.arouter.facade.model.TypeWrapper<TestBean>(){}.getType());
}
if (null != serializationService) {
substitute.listBean = serializationService.parseObject(substitute.getIntent().getStringExtra("listBean"), new com.alibaba.android.arouter.facade.model.TypeWrapper<List<TestBean>>(){}.getType());
}
if (null != serializationService) {
substitute.mapStr = serializationService.parseObject(substitute.getIntent().getStringExtra("mapStr"), new com.alibaba.android.arouter.facade.model.TypeWrapper<Map<String, String>>(){}.getType());
}
substitute.moduleLoginProvider = ARouter.getInstance().navigation(ModuleLoginProvider.class);
}
}
源码分析
编译后源码
Java项目生成的代理类路径:
Kotlin项目生成的代理类路径:
编译后的文件如下,在运行中ARout框架即可找到这些类,运行、赋值、跳转...
//以分组名为key,存入该分组的管理类Class
public class ARouter$$Root$$ModuleBase implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("base", ARouter$$Group$$base.class);
//...
routes.put("app", ARouter$$Group$$app.class);
routes.put("auto", ARouter$$Group$$auto.class);
routes.put("award", ARouter$$Group$$award.class);
}
}
//当前base下:Activity、IProvider的class
public class ARouter$$Group$$base implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/base/act/target",
RouteMeta.build(RouteType.ACTIVITY,
ARoutTargetActivity.class,
"/base/act/target", "base", //分组
new java.util.HashMap<String, Integer>(){//Autowired注解的变量
{put("arg3", 0);
put("arg2", 3);
put("listBean", 11);
put("arg1", 8);
put("mapStr", 11);
put("testBean", 11); }},
-1,
-2147483648));
atlas.put("/base/service/degrade",
RouteMeta.build(RouteType.PROVIDER,
DegradeServiceImpl.class, //降级策略服务
"/base/service/degrade", "base",
null, -1, -2147483648));
atlas.put("/base/service/pretreatment",
RouteMeta.build(RouteType.PROVIDER,
PretreatmentServiceImpl.class, //预处理服务
"/base/service/pretreatment", "base",
null, -1, -2147483648));
atlas.put("/base/service/serialization",
RouteMeta.build(RouteType.PROVIDER,
JsonServiceImpl.class, //自定义类解析
"/base/service/serialization", "base",
null, -1, -2147483648));
}
}
//当前Module下的拦截器
public class ARouter$$Interceptors$$ModuleBase implements IInterceptorGroup {
@Override
public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) {
interceptors.put(5, LoginInterceptor.class);
}
}
//当前Module下的IProvider服务
public class ARouter$$Providers$$ModuleBase implements IProviderGroup {
@Override
public void loadInto(Map<String, RouteMeta> providers) {
providers.put("com.alibaba.android.arouter.facade.service.DegradeService",
RouteMeta.build(RouteType.PROVIDER,
DegradeServiceImpl.class,
"/base/service/degrade", "base",
null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.facade.service.PretreatmentService",
RouteMeta.build(RouteType.PROVIDER,
PretreatmentServiceImpl.class,
"/base/service/pretreatment", "base",
null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.facade.service.SerializationService",
RouteMeta.build(RouteType.PROVIDER,
JsonServiceImpl.class,
"/base/service/serialization", "base",
null, -1, -2147483648));
}
}
init()
- 如果是arouter-register自动加载的路由表,设置registerByPlugin=true,不执行以下代码
- 如果不是自动加载的路由表,则通过
反射找到编译生成的文件,将文件内保存的信息存入Warehouse的各个map中
//ARout.java
public static void init(Application application) {
if (!hasInit) {
hasInit = _ARouter.init(application);
if (hasInit) {
_ARouter.afterInit();
}
}
}
//InterceptorServiceImpl是ARout内部实现类,会在navigation方法最后,遍历自定义拦截器进行处理
static void afterInit() {
interceptorService = (InterceptorService) ARouter.getInstance()
.build("/arouter/service/interceptor")
.navigation();
}
//LogisticsCenter.java
public synchronized static void init(Context context, ThreadPoolExecutor tpe) {
mContext = context;
executor = tpe;
try {
long startInit = System.currentTimeMillis();
//Gradle加载路由表,会把registerByPlugin设置为true;效率比扫描dex文件获取class快得多
loadRouterMap();
if (registerByPlugin) {
logger.info(TAG, "Load router map by arouter-auto-register plugin.");
} else {
Set<String> routerMap;
// debug 或 新的版本code/name
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.
//将编译产生的class文件中的类放到SP中,方便下次直接取
//扫描dex文件过滤出ARout需要的class文件,过程慢
/*ROUTE_ROOT_PAKCAGE = com.alibaba.android.arouter.routes*/
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);
} else {
logger.info(TAG, "Load router map from cache.");
//从sp中直接取class类
routerMap = new HashSet<>(getSharedPreferences(AROUTER_SP_CACHE_KEY, ...)
.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)) {
//将ARouter$$Root$$ModuleName对应的配置存入groupsIndex
((IRouteRoot) (Class.forName(className).getConstructor()
.newInstance())).loadInto(Warehouse.groupsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT +
SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
// 将ARouter$$Interceptors$$ModuleName存入interceptorsIndex
((IInterceptorGroup) (Class.forName(className).getConstructor()
.newInstance())).loadInto(Warehouse.interceptorsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT +
SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
// 将ARouter$$Providers$$ModuleName存入providersIndex
((IProviderGroup) (Class.forName(className).getConstructor()
.newInstance())).loadInto(Warehouse.providersIndex);
}
}
}
logger.info(TAG, "Load root element finished, cost "
+ (System.currentTimeMillis() - startInit) + " ms.");
}
build()
- 替换path或URI
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) {
//navigation会在providersIndex中寻找自定义路径替换服务
PathReplaceService pService = ARouter.getInstance()
.navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
}
return new Postcard(path, group);
}
}
navigation()
LogisticsCenter.navigation():
- 预处理
- completion处理goupsIndex、routes、greenChannel()等信息
- activity跳转或返回fragment/provider实例
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;
}
try {
//goupsIndex、routes、greenChannel
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
if (debuggable()) {
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.
//降级策略:没有callback时调用
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);
}
}
});
} else {
return _navigation(context, postcard, requestCode, callback);
}
return null;
}
LogisticsCenter.completion()
- Warehouse.groupsIndex中的信息保存到Warehouse.routes中
- IProvider、Fragment:greenChannel()跳过拦截器
public synchronized static void completion(Postcard postcard) {
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
//第一次为null,此时还没有将groupsIndex中以组名为key所对应的ARouter$$Group$$goupName中的act/iprovider保存到routes中
if (null == routeMeta) {
Class<? extends IRouteGroup> groupMeta = Warehouse
.groupsIndex.get(postcard.getGroup());
if (null == groupMeta) {
} else {
try {
//ARouter$$Group$$goupName信息保存到routes中
IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
iGroupInstance.loadInto(Warehouse.routes);
Warehouse.groupsIndex.remove(postcard.getGroup());
}
completion(postcard); // Reload从新调用
}
} else {
postcard.setDestination(routeMeta.getDestination());
postcard.setType(routeMeta.getType());
postcard.setPriority(routeMeta.getPriority());
postcard.setExtra(routeMeta.getExtra());
Uri rawUri = postcard.getUri();
if (null != rawUri) {
...
}
//Provider、Fragment设置greenChannel()跳过拦截器
switch (routeMeta.getType()) {
case PROVIDER:
Class<? extends IProvider> providerMeta = (Class<? extends IProvider>)
routeMeta.getDestination();
IProvider instance = Warehouse.providers.get(providerMeta);
if (null == instance) {
IProvider provider;
try {
provider = providerMeta.getConstructor().newInstance();
provider.init(mContext);
Warehouse.providers.put(providerMeta, provider);
instance = provider;
} catch (Exception e) {
throw new HandlerException("Init provider failed! " + e.getMessage());
}
}
postcard.setProvider(instance);
postcard.greenChannel(); // Provider should skip all of interceptors
break;
case FRAGMENT:
postcard.greenChannel(); // Fragment needn't interceptors
default:
break;
}
}
}
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:
final Intent intent = new Intent(currentContext, postcard.getDestination());
intent.putExtras(postcard.getExtras());
...
// 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:
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;
}
case METHOD:
case SERVICE:
default:
return null;
}
return null;
}
jar中加载路由表
自动加载路由表是通过arouter-register插件来实现的,主要通过在编译期给LogisticsCenter的loadRouterMap方法插入register方法调用的代码;反编译出来的代码确实在loadRouterMap方法处插入了register方法调用的代码,并且把registerByPlugin置为true,所以最终不会通过扫描dex文件来加载路由表类装载到map中。
通过这种方式,在init时便不会通过扫描dex文件的方式获取class类信息了,减小耗时会有明显提升
原理
Tips
- 插件跳转目标类 :ARout Helper插件
- 问题排查:
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
//ARout
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName()]
}
}
kapt androidDepend.arouter_compiler
//纯Java项目,如果.gradle已经配置了Kotlin相关的,需使用上面的kapt
annotationProcessor androidDepend.arouter_compiler
路径以"/"开头
- 其它模块Application初始化
class App:BaseApplication() {
override fun initMain(application: Application?) {
//第一次调用是,provide初始化
ARouter.getInstance().navigation(IModuleWalletService::class.java)
}
}
@Route(path = RoutTable.WALLET_SERVICE_MAIN)
class ModuleWalletService: IModuleWalletService {
//第一次自动调用
override fun init(context: Context?) {
KLog.d("", "$context")
(context as? Application).run { AppWallet().initMain(this) }
}
}
class AppWallet: BaseApplication() {
override fun initMain(application: Application?) {
KLog.d("", "$application")
}
}