ARouter使用&源码小结[版本1.5.2]
Posted guangdeshishe
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ARouter使用&源码小结[版本1.5.2]相关的知识,希望对你有一定的参考价值。
简介
官方介绍:一个用于帮助 android App 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦
Git项目地址:https://github.com/alibaba/ARouter
官方中文ARouter详细使用教程:https://github.com/alibaba/ARouter/blob/master/README_CN.md
kotlin使用注意点:
- 需要使用【kapt】引入注解处理器【com.alibaba:arouter-compiler:1.5.2】
kapt 'com.alibaba:arouter-compiler:1.5.2'
- 需要引入kotlin反射库,传递自定义类型对象序列化时需要用到反射
implementation 'org.jetbrains.kotlin:kotlin-reflect:1.5.20'
- 项目中同时包含kotlin和java类,需要同时配置两个版本的模块名
android {
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName()]
}
}
kapt {
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
}
}
}
其他注意点:
- 跳转时携带自定义类型对象需要实现【SerializationService】接口,否则会报错(
不知道官方为啥不提供默认实现?
)-
例如:ARouter.getInstance().build("/xxx/xxx").withObject(“user”,User()).navigation()
@Route(path = "/base/SerializationServiceImpl") class SerializationServiceImpl : SerializationService { override fun init(context: Context?) { } override fun <T : Any?> json2Object(input: String?, clazz: Class<T>?): T { return JSON.parseObject(input, clazz) } override fun object2Json(instance: Any?): String { return JSON.toJSONString(instance) } override fun <T : Any?> parseObject(input: String?, clazz: Type?): T { Log.d("ARouterTest", "SerializationServiceImpl parseObject") return JSON.parseObject(input, clazz) } }
-
上面示例中用到的json库是【fastjson】
implementation 'com.alibaba:fastjson:1.2.69'
- 开源库地址:https://github.com/alibaba/fastjson
-
如果实现多个【SerializationService】接口会怎么样呢?
经过试验,会以APT处理注解时后处理的类为准。
这点可以通过生成的中间类结合源码可以看出来:
中间类里是以【SerializationService】类名作为key保存它的实现类,根据Map的特点我们知道,相同的key后面put的值会覆盖掉前面的;而源码中获取【SerializationService】就是通过该类的类名来获取的;public class ARouter$$Providers$$base implements IProviderGroup { @Override public void loadInto(Map<String, RouteMeta> providers) { providers.put("com.alibaba.android.arouter.facade.service.SerializationService", RouteMeta.build(RouteType.PROVIDER, SerializationServiceImpl.class, "/base/SerializationServiceImpl", "base", null, -1, -2147483648)); providers.put("com.alibaba.android.arouter.facade.service.SerializationService", RouteMeta.build(RouteType.PROVIDER, SerializationServiceImpl2.class, "/base/SerializationServiceImpl2", "base", null, -1, -2147483648)); } }
public Postcard withObject(@Nullable String key, @Nullable Object value) { serializationService = ARouter.getInstance().navigation(SerializationService.class); mBundle.putString(key, serializationService.object2Json(value)); return this; }
-
源码分析小结
整体实现方式
- 通过【Autowired】、【Interceptor】、【Route】等注解声明类/字段
- 注解处理器【arouter-compiler】在编译时分析源码搜集并整理所有注解了的类/字段,然后生成中间类保存这些类/字段的集合
- Application初始化时,执行ARouter.init()方法通过调用中间类的方法将相关数据加载到内存中
- 如果有使用【arouter-register】Gradle插件,则会在编译时期就把生成的中间类加载到代码中,省去了从Dex文件中查找中间类这个过程,也避免了应用加固后无法访问Dex文件问题
class LogisticsCenter{ void init(Context context, ThreadPoolExecutor tpe){ //load by plugin first loadRouterMap(); if (registerByPlugin) { logger.info(TAG, "Load router map by arouter-auto-register plugin."); } else { ... load from Dex file } } } public class LogisticsCenter { /** * 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()); } }
页面跳转源码分析
示例代码:
@Route(path = "/settings/SettingsActivity", extras = 0)
这里的extras是Int类型,可以在拦截器中通过postcard.extra
获取,用于判断哪些页面需要拦截
@Route(path = "/settings/SettingsActivity", extras = 0)
class SettingsActivity : AppCompatActivity() {
@Autowired(name = "message")
lateinit var mMessage: String
@Autowired(name = "user")
lateinit var mUser: User
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.settings_activity)
ARouter.getInstance().inject(this)
Toast.makeText(this, mUser.toString(), Toast.LENGTH_LONG).show()
}
}
ARouter.getInstance().build("/settings/SettingsActivity")
.withString("message", "hello, I am ARouter")
.withObject("user", User("agile", 30))
.navigation(this, object : NavCallback() {
override fun onInterrupt(postcard: Postcard) {
val exception = postcard.tag as Exception
Log.d("ARouterTest", exception.message + "")
}
override fun onArrival(postcard: Postcard) {
Log.d("ARouterTest", postcard.path + " arrived!")
}
})
实现过程:
-
1.ARouter.build():根据路径(path)封装成Postcard对象并返回;
- Postcard.java:包含所有要跳转相关路由信息
- 可以通过实现PathReplaceService接口重写要跳转的路径,例如根据是否登录跳转不同页面
- 实现类【_ARouter.java】
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); } }
-
2.Postcard.withString/withObject:将要携带的参数保存到Postcard对象中
- withString/withInt等基本数据类型:保存到Postcard对象中的Bundle变量中
calss Postcard{ private Bundle mBundle; // Data to transform public Postcard withInt(@Nullable String key, int value) { mBundle.putInt(key, value); return this; } public Postcard withString(@Nullable String key, @Nullable String value) { mBundle.putString(key, value); return this; } }
- withObject自定义类型对象:必须要提供实现SerializationService接口的类,否则无法成功跳转并抛出异常,对象转换成JSON字符串后存到Bundle中:
java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.String com.alibaba.android.arouter.facade.service.SerializationService.object2Json(java.lang.Object)' on a null object reference
public Postcard withObject(@Nullable String key, @Nullable Object value) { serializationService = ARouter.getInstance().navigation(SerializationService.class); mBundle.putString(key, serializationService.object2Json(value)); return this; }
- withString/withInt等基本数据类型:保存到Postcard对象中的Bundle变量中
-
3.Postcard.navigation:执行跳转操作,实现类是【ARouter.java->_ARouter.java】
public Object navigation(Context context, NavigationCallback callback) { return ARouter.getInstance().navigation(context, this, -1, callback); }
-
4._ARouter.navigation:真正实现跳转逻辑
- 1.判断有没有定义PretreatmentService服务,如果有则跳转前执行预处理操作
- 2.LogisticsCenter.completion(postcard):根据要path查找要跳转的类,并将相关信息注入到Postcard对象中
- 例如:要跳转的类信息、目标对象类型(Activity/Provider/Fragment/等)、优先级(priority)、携带的数据(extra)等
- 如果跳转失败则优先回调NavigationCallback.onLost方法,其次回调全局DegradeService.onLost
- 跳转成则回调NavigationCallback.onFound方法
- 3.判断Postcard是否走的绿色通道,如果不是的话,则需要依次调用所有拦截器(IInterceptor)进行处理,如果被其中某个拦截器拦截了,则停止跳转并回调NavigationCallback.onInterrupt方法
/** * Green channel, it will skip all of interceptors. * * @return this */ public Postcard greenChannel() { this.greenChannel = true; return this; }
- 4.根据Postcard要跳转的目标类型做不同处理:
- Activity:将Postcard中要传递的参数交给Intent,并调用startActivity方法跳转
- Provider:返回对应的Provider对象,该对象是在LogisticsCenter.completion(postcard)方法中通过反射创建,并回调它的init方法
- Broadcast/ContentProvider/Fragment:通过反射创建对象并返回
- method/service/其他:返回null
-
关键源码:
class _ARouter{ protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) { PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class); pretreatmentService.onPretreatment(context, postcard) ... try { LogisticsCenter.completion(postcard); } catch (NoRouteFoundException ex) { callback.onLost(postcard); DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class); degradeService.onLost(context, postcard); return null; } callback.onFound(postcard); ... if (!postcard.isGreenChannel()) { interceptorService.doInterceptions(postcard, new InterceptorCallback() { public void onContinue(Postcard postcard) { _navigation(postcard, requestCode, callback); } public void onInterrupt(Throwable exception) { callback.onInterrupt(postcard); } }); } else { return _navigation(postcard, requestCode, callback); } return null; } 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()); ... startActivity(requestCode, currentContext, intent, postcard, callback); break; case PROVIDER: return postcard.getProvider(); case BOARDCAST: case CONTENT_PROVIDER: case FRAGMENT: Class<?> fragmentMeta = postcard.getDestination(); ... Object instance = fragmentMeta.getConstructor().newInstance(); return instance; case METHOD: case SERVICE: default: return null; } return null; } }
以上是关于ARouter使用&源码小结[版本1.5.2]的主要内容,如果未能解决你的问题,请参考以下文章