- 最近对项目进行组件化重构,要进行组件化就少不了组件间通信的路由,于是就再次研究了一下阿里的ARouter,关于组件化可以查看我之前写的组件化架构系列文章,这里就不过多提及了
使用详解
1. 添加依赖
- 在baseRouter module 的 build.gradle的dependencies中加入中添加依赖 (最好把路由相关放到单独的module中,并进行二次封装,方便日后的管理和替换)
// arouter-api要与arouter-compiler匹配使用,均使用最新版可以保证兼容
// 这里用api而不是implementation,方便其他组件依赖baseRouter后使用ARouter的功能
api 'com.alibaba:arouter-api:1.5.2'
// 我用的是kotlin所以下面用了kapt,如果是java替换成annotationProcessor即可
kapt 'com.alibaba:arouter-compiler:1.5.2'
- 在其他需要进行组件化通信的各模块的build.gradle的dependencies中加入
implementation project(path: ':baseRouter')
kapt 'com.alibaba:arouter-compiler:1.5.2'
- 设置AROUTER_MODULE_NAME
//1. kotlin
plugins {
id 'com.android.library'
id 'kotlin-android'
id 'kotlin-kapt'
}
kapt {
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
}
android {
...
}
//2. java
android {
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName()]
}
}
}
}
2. 初始化ARouter
1. 直接初始化
class AppModuleApplication : Application() {
override fun onCreate() {
super.onCreate()
if (BuildConfig.isDebug){
ARouter.openLog();
ARouter.openDebug();
//需要在init之前配置才有效
}
ARouter.init(this)
}
}
2. 提供BaseRouterApplication被继承
- 在baseRouter module中创建一个Application的子类初始化ARouter,然后在app module的Application中继承(不推荐);
class BaseRouterApplication : Application() {
override fun onCreate() {
super.onCreate()
if (BuildConfig.isDebug){
ARouter.openLog();
ARouter.openDebug();
//需要在init之前配置才有效
}
ARouter.init(this)
}
}
class AppModuleApplication : BaseRouterApplication() {
override fun onCreate() {
super.onCreate()
}
}
3. 提供静态方法initRouter
- 在二次封装的Router工具类中创建一个初始化Router的init方法再在app module的Application中调用
fun initRouter(application: Application,isDebug: Boolean){
if (isDebug){
ARouter.openLog();
ARouter.openDebug();
//需要在init之前配置才有效
}
ARouter.init(application);
}
class AppModuleApplication : Application() {
override fun onCreate() {
super.onCreate()
initRouter(this,BuildConfig.DEBUG)
}
}
- 通过ContentProvider初始化
- 在baseRouter module中自定义BaseRouterProvider并初始化ARouter,然后在baseRouter module的AndroidManifest.xml中配置
class BaseRouterProvider : ContentProvider() {
override fun onCreate(): Boolean {
//ContentProvider中也可以取得Context
val application = context?.applicationContext as Application?
application?.let {
if (BuildConfig.DEBUG) {
ARouter.openLog()
ARouter.openDebug()
}
ARouter.init(application)
}
return true
}
//其他方法用不到,直接return null 或 return -1 即可
...
}
<application>
<provider
android:name=".BaseRouterProvider"
android:authorities="${applicationId}.BaseRouterProvider"
android:exported="false" />
...
</application>
5. 通过Jetpack组件AppStartup来初始化
- 添加依赖
implementation "androidx.startup:startup-runtime:1.0.0"
- 创建Initializer的实现类,并初始化ARouter
class BaseRouterInitializer : Initializer<Unit> {
//在create方法中执行要初始化的代码
override fun create(context: Context) {
val application = context.applicationContext as Application?
application?.let {
if (BuildConfig.DEBUG) {
ARouter.openLog()
ARouter.openDebug()
}
ARouter.init(application)
}
}
//dependencies方法用于配置当前LjyToastInitializer是否还依赖于其他Initializer
//有的话在此配置,没有就return emptyList()即可
override fun dependencies(): List<Class<out Initializer<*>>> {
return emptyList()
}
}
- AndroidMainfest.xml中配置
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name=".BaseRouterInitializer"
android:value="androidx.startup" />
</provider>
3. 简单使用
1. 定义路由路径管理类
- 在baseRouter module中创建ARouterPath.kt用于存放各模块的路由路径,这里的路径需要注意的是至少需要有两级(/xx/xx)
/**
* app主模块入口
*/
const val PATH_ACTIVITY_MAIN = "/app/MainActivity"
/**
* 登录模块入口
*/
const val PATH_ACTIVITY_LOGIN = "/login/LoginActivity"
/**
* 网页加载模块入口
*/
const val PATH_ACTIVITY_WEB = "/web/WebActivity"
2. 添加@Route注解
- 创建上面对应的module及Activity,添加注解@Route,并设置对应的路由路径,例如LoginActivity设置如下
@Route(path = PATH_ACTIVITY_LOGIN)
class LoginActivity : AppCompatActivity() {
}
3. 发起路由跳转
ARouter.getInstance().build(PATH_ACTIVITY_LOGIN).navigation()
- 携带参数,有很多withXXX方法基本都是见明知意
ARouter.getInstance().build(PATH_ACTIVITY_LOGIN)
.withLong("key1", 666L)
.withString("key2", "888")
.navigation()
4. 接收参数
- 使用@Autowired注解标注字段即可支持自动注入,并在onCreate中调用inject方法(原理类似ButterKnife)
- kotlin中需要额外使用lateinit修饰符或者添加@JvmField注解,否则会编译失败;
- 但是需要注意的是lateinit修饰的字段是延迟初始化,跳转时一定要传递,否则会报错UninitializedPropertyAccessException: lateinit property name has not been initialized,所以还是用@JvmField注解吧;
@Route(path = PATH_ACTIVITY_LOGIN)
class LoginActivity : AppCompatActivity() {
@Autowired
@JvmField
var key1: Int? = -1
@Autowired(name = "key2", required = true, desc = "userName field")
lateinit var name: String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ARouter.getInstance().inject(this)
log("key1=$key1")
log("name=$name")
}
}
5. 添加混淆规则(如果使用了Proguard)
-keep public class com.alibaba.android.arouter.routes.**{*;}
-keep public class com.alibaba.android.arouter.facade.**{*;}
-keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;}
# 如果使用了 byType 的方式获取 Service,需添加下面规则,保护接口
-keep interface * implements com.alibaba.android.arouter.facade.template.IProvider
# 如果使用了 单类注入,即不定义接口实现 IProvider,需添加下面规则,保护实现
# -keep class * implements com.alibaba.android.arouter.facade.template.IProvider
6. 使用 Gradle 插件实现路由表的自动加载 (可选)
- 实现路由表自动加载,这个功能虽然是选项配置,但是对于arouter启动优化(sdk初始化速度)有很大的作用,在编译期通过gradle插装把需要依赖arouter注解的类自动扫描到arouter的map管理器里面,
//根gradle中
buildscript {
...
dependencies {
...
classpath "com.alibaba:arouter-register:1.0.2"
}
}
//app module 的gradle中
plugins {
...
id 'com.alibaba.arouter'
}
4. 进阶用法
进行二次封装
- 将进阶用法之前我们先对ARouter进行一下封装,调用起来更简洁一定,也方便做替换,新建RouterUtil.kt,代码如下
/**
* 标准使用
* @path 路由路径
* @params 所传参数
*/
fun routerNavigation(path: String, params: Map<String, Any?>? = null) {
buildParams(ARouter.getInstance().build(path), params).navigation()
}
/**
* 解析params放到Postcard中
*/
private fun buildParams(postcard: Postcard, params: Map<String, Any?>?): Postcard {
return postcard.apply {
if (params != null && params.isNotEmpty())
for ((k, v) in params)
when (v) {
is String -> withString(k, v)
is Int -> withInt(k, v)
is Boolean -> withBoolean(k, v)
is Long -> withLong(k, v)
is Float -> withFloat(k, v)
is Double -> withDouble(k, v)
is ByteArray -> withByteArray(k, v)
is Bundle -> with(v)
is List<*>, is Map<*, *>, is Set<*> -> withObject(k, v)
is Parcelable -> withParcelable(k, v)
is Serializable -> withSerializable(k, v)
else -> withObject(k, v)
}
}
}
/**
* 进阶使用方法
* @path 路由路径
* @params 所传参数
* @activity 当前activity,配合requestCode或callback使用
* @requestCode 当需要startActivityForResult时
* @callback 使用NavigationCallback处理跳转结果(局部降级策略)
* @group 一般不传,默认就是 path 的第一段相同
* @uri 通过uri跳转时
* @flag 当需要指定flag时Intent.FLAG_ACTIVITY_CLEAR_TOP
* @enterAnim 页面进入动画
* @exitAnim 页面退出动画
* @compat ActivityOptionsCompat动画
* @greenChannel 是否使用绿色通道(跳过所有的拦截器)
*/
fun routerNavigation(
path: String,
params: Map<String, Any?>? = null,
activity: Activity? = null,
requestCode: Int = -1,
callback: NavigationCallback? = null,
group: String? = null,
uri: Uri? = null,
flag: Int? = null,
enterAnim: Int? = null,
exitAnim: Int? = null,
compat: ActivityOptionsCompat? = null,
greenChannel: Boolean = false,
) {
if (path.isNullOrEmpty()) {
buildParams(ARouter.getInstance().build(uri), params)
} else {
val postcard =
if (group.isNullOrEmpty()) ARouter.getInstance().build(path)
else ARouter.getInstance().build(path, group)
buildParams(
postcard.apply {
if (flag != null)
postcard.withFlags(flag)
if (enterAnim != null && exitAnim != null)//转场动画(常规方式)
postcard.withTransition(enterAnim, exitAnim)
if (compat != null)// 转场动画(API16+)
postcard.withOptionsCompat(compat)
if (greenChannel)//使用绿色通道(跳过所有的拦截器)
postcard.greenChannel()
}, params
)
}.navigation(activity, requestCode, callback)
}
1. 更多类型的参数
data class TestObj(val name:String?,val age:Int?,val email:String?)
val testObj = TestObj("ljy", 18, "xxx@qq.com")
val testObj2 = TestObj("yang", 20, "www@qq.com")
val list = listOf(testObj, testObj2)
val testSerializable = TestSerializable("serial", 12)
val testParcelable = TestParcelable("parcel", 11)
routerNavigation(
PATH_ACTIVITY_LOGIN,
mapOf(
"key1" to 123,
"key2" to "aaa",
"key3" to "bbb",
"testObj" to testObj,
"list" to list,
"testSerializable" to testSerializable,
"testParcelable" to testParcelable
)
)
@Autowired
lateinit var testObj: TestObj
@Autowired
lateinit var list: List<TestObj?>
@Autowired
lateinit var testSerializable: TestSerializable
@Autowired
lateinit var testParcelable: TestParcelable
2. 解析自定义对象
- 上面TestObj是自定义对象,要想传递自定义对象还需要实现一个SerializationService
- 通过Gson或Fastjson解析
@Route(path = "/serialization/gson")//这里和之前的路由定义规则一样,自己控制好group不要重复就行
class GsonSerializationServiceImpl : SerializationService {
override fun init(context: Context?) {
log("GsonSerializationServiceImpl.init")
}
override fun <T : Any?> json2Object(input: String?, clazz: Class<T>?): T? {
log("GsonSerializationServiceImpl.json2Object")
return GsonUtils.fromJson(input, clazz)
}
override fun object2Json(instance: Any?): String {
log("GsonSerializationServiceImpl.object2Json")
return GsonUtils.toJson(instance)
}
override fun <T : Any?> parseObject(input: String?, clazz: Type?): T? {
log("GsonSerializationServiceImpl.parseObject")
return GsonUtils.fromJson(input, clazz)
}
}
3. 通过URL跳转
- baseRouter module中新建一个Activity用于监听Scheme事件,之后直接把url传递给ARouter即可
class SchemeFilterActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val uri = intent.data
ARouter.getInstance().build(uri).navigation()
}
}
- 在baseRouter module的AndroidManifest.xml中注册,分别定义Scheme和App Links协议
<activity android:name=".ui.SchemeFilterActivity">
<!-- Scheme -->
<intent-filter>
<data
android:host="test.ljy.com"
android:scheme="arouter" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
<!-- App Links -->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="test.ljy.com"
android:scheme="http" />
<data
android:host="test.ljy.com"
android:scheme="https" />
</intent-filter>
</activity>
- 跳转
routerNavigation( uri = Uri.parse("arouter://test.ljy.com$PATH_ACTIVITY_LOGIN"))
4. 使用拦截器
- 要想定义拦截器需要实现IInterceptor,添加@Interceptor,如登录校验,运行时权限等
- 关于注解的参数priority: 拦截器可以定义优先级,如果有多个拦截器,会依次执行拦截器
- 以运行时权限校验为例,代码如下
@Interceptor(priority = 0)
class PermissionInterceptor : IInterceptor {
private var context: Context? = null
private var postcard: Postcard? = null
private var callback: InterceptorCallback? = null
override fun init(context: Context) {
this.context = context
log("PermissionInterceptor.init")
}
override fun process(postcard: Postcard, callback: InterceptorCallback) {
log("PermissionInterceptor.process")
this.postcard = postcard
this.callback = callback
if (postcard.path == PATH_ACTIVITY_WEB) {
log("PermissionInterceptor.process: path匹配,开始校验运行时权限")
requestMyPermissions(
arrayOf(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE,
)
) {
if (it) {
log("允许了权限申请")
callback.onContinue(postcard)
} else {
log("拒绝了权限申请")
}
}
}else{
log("PermissionInterceptor.process: path不匹配,无需拦截")
callback.onContinue(postcard)
}
}
}
5. 降级策略
全局降级策略
- 实现DegradeService,重写onLost方法
@Route(path = "/degrade/Test")
class TestDegradeServiceImpl : DegradeService {
override fun onLost(context: Context?, postcard: Postcard?) {
log("TestDegradeServiceImpl.onLost:没有找到该路由地址:${postcard?.path}")
// do something:可以提供一个错误页进行跳转
routerNavigation(PATH_ACTIVITY_ERROR)
}
override fun init(context: Context?) {
log("TestDegradeServiceImpl.init")
}
}
局部降级策略
- 就是通过NavigationCallback监听单次路由跳转
routerNavigation("/aaa/bbb", callback = object : NavigationCallback {
override fun onFound(postcard: Postcard?) {
log("NavigationCallback.onFound-找到了:$postcard")
}
override fun onLost(postcard: Postcard?) {
log("NavigationCallback.onLost-找不到了:$postcard")
routerNavigation(PATH_ACTIVITY_ERROR)
}
override fun onArrival(postcard: Postcard?) {
log("NavigationCallback.onArrival-跳转完了:$postcard")
}
override fun onInterrupt(postcard: Postcard?) {
log("NavigationCallback.onInterrupt-被拦截了:$postcard")
}
})
6. 通过依赖注入解耦:服务管理
1. 提供服务
interface HelloService:IProvider {
fun sayHello(msg:String)
}
@Route(path = PATH_SERVICE_HELLO, name = "hello service")
class HelloServiceImpl:HelloService {
override fun sayHello(msg: String) {
log("Hello $msg")
}
override fun init(context: Context?) {
log("HelloServiceImpl.init")
}
}
@Route(path = PATH_SERVICE_NIHAO, name = "nihao service")
class NiHaoServiceImpl : HelloService {
override fun sayHello(msg: String) {
log("你好 $msg")
}
override fun init(context: Context?) {
log("NiHaoServiceImpl.init")
}
}
2. 发现服务
- 使用依赖注入的方式发现服务(推荐)
- 通过注解标注字段, 即可使用,无需主动获取Autowired注解中标注name之后,将会使用byName的方式注入对应的字段,
- 不设置name属性,会默认使用byType的方式发现服务(当同一接口有多个实现的时候,必须使用byName的方式发现服务)
//注解字段
//byType
@Autowired
lateinit var helloService1: HelloService
//byName
@Autowired(name = PATH_SERVICE_HELLO)
lateinit var helloService2: HelloService
//直接使用
helloService1.sayHello("msg1")
helloService2.sayHello("msg2")
- 使用依赖查找的方式发现服务,主动去发现服务并使用,下面两种方式分别是byName和byType
//声明变量时未使用注解
var helloService3: HelloService? = null
var helloService4: HelloService? = null
//byType
helloService3 = ARouter.getInstance().navigation(HelloService::class.java)
helloService3?.sayHello("msg3")
//byName
helloService4 = ARouter.getInstance().build(PATH_SERVICE_HELLO).navigation() as HelloService?
helloService4?.sayHello("msg4")
7. 获取Fragment
- 先定义一个Fragment,并用@Route注解添加path
@Route(path = PATH_FRAGMENT_DIALOG_TEST)
class TestDialogFragment : AppCompatDialogFragment() {
...
}
- 获取Fragment实例
val fragment: AppCompatDialogFragment =ARouter.getInstance()
.build(PATH_FRAGMENT_DIALOG_TEST).navigation() as AppCompatDialogFragment
(fragment as TestDialogFragment).setConfirmCallback { log("MainActivity confirmCallback ") }
fragment.show(supportFragmentManager, "TestDialogFragment")
8. 预处理服务
- 在路由navigation之前进行干扰路由,需要实现PretreatmentService接口,
- 重写onPretreatment方法,跳转前预处理,如果需要自行处理跳转,该方法返回 false 即可;
- 拦截器功能和预处理服务功能是有点像的,只不过预处理服务是早于拦截器;
@Route(path = "/pretreatment/test")
class TestPretreatmentServiceImpl : PretreatmentService {
override fun init(context: Context?) {
log("TestPretreatmentServiceImpl.init")
}
override fun onPretreatment(context: Context?, postcard: Postcard?): Boolean {
log("TestPretreatmentServiceImpl.onPretreatment")
if (postcard?.path == PATH_ACTIVITY_WEB) {
if (!ApplicationUtil.instance.isLogin) {
Toast.makeText(
ApplicationUtil.instance.getAppContext(),
"还没有登录哦",
Toast.LENGTH_SHORT
).show()
routerNavigation(PATH_ACTIVITY_LOGIN)
return false
}
}
return true
}
}
9. 重定义URL跳转
- 对要跳转的path或uri进行处理,如重定向等,需要实现PathReplaceService接口
@Route(path = "/pathReplace/test")
class TestPathReplaceServiceImpl : PathReplaceService {
override fun init(context: Context?) {
log("TestPathReplaceServiceImpl.init")
}
override fun forString(path: String): String {
log("TestPathReplaceServiceImpl.replacePath")
// 按照一定的规则处理之后返回处理后的结果
return if (path == PATH_ACTIVITY_MAIN) PATH_ACTIVITY_LOGIN else path
}
override fun forUri(uri: Uri?): Uri? {
log("TestPathReplaceServiceImpl.replaceUri")
return uri // 按照一定的规则处理之后返回处理后的结果
}
}
10. 动态注册路由信息
- 适用于部分插件化架构的App以及需要动态注册路由信息的场景,可以通过 ARouter 提供的接口实现动态注册 路由信息,目标页面和服务可以不标注 @Route 注解,注意:同一批次仅允许相同 group 的路由信息注册
//1. 一个没有被@Route注解的Activity
class RegisterActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_register)
}
}
//2. addRouteGroup 动态注册路由
ARouter.getInstance().addRouteGroup {
it[PATH_ACTIVITY_REGISTER] = RouteMeta.build(
RouteType.ACTIVITY,// 路由信息
Class.forName("com.jinyang.login.RegisterActivity"),// 目标的 Class
PATH_ACTIVITY_REGISTER, // Path
PATH_ACTIVITY_REGISTER.split("/")[1],// Group, 尽量保持和 path 的第一段相同
0, // 优先级,暂未使用
0// Extra,用于给页面打标
)
}
//3. 进行路由跳转
routerNavigation(PATH_ACTIVITY_REGISTER)
11. 更多的初始化配置
private fun initRouter() {
if (BuildConfig.DEBUG) {
// 开启日志
ARouter.openLog()
// 使用InstantRun的时候,需要打开该开关,上线之后关闭,否则有安全风险
ARouter.openDebug()
// 打印日志的时候打印线程堆栈
ARouter.printStackTrace();
//使用自己的日志工具打印日志
ARouter.setLogger(myLogger)
// 使用自己提供的线程池
ARouter.setExecutor(DispatcherExecutor.getCPUExecutor())
}
//需要在init之前配置才有效
ARouter.init(this)
}
val myLogger = object : ILogger {
private var mIsMonitorMode = false
private var mIsShowStackTrace: Boolean = false
override fun showLog(isShowLog: Boolean) {
LjyLogUtil.setIsLog(isShowLog)
}
override fun showStackTrace(isShowStackTrace: Boolean) {
mIsShowStackTrace = isShowStackTrace
}
override fun debug(tag: String?, message: String?) {
LjyLogUtil.d("$tag --> $message")
}
override fun info(tag: String?, message: String?) {
LjyLogUtil.i("$tag --> $message")
}
override fun warning(tag: String?, message: String?) {
LjyLogUtil.w("$tag --> $message")
}
override fun error(tag: String?, message: String?) {
LjyLogUtil.e("$tag --> $message")
}
override fun error(tag: String?, message: String?, e: Throwable?) {
LjyLogUtil.e("$tag --> message=$message,e=${e?.message}")
}
override fun monitor(message: String?) {
if (LjyLogUtil.isLog() && isMonitorMode) {
val stackTraceElement = Thread.currentThread().stackTrace[3]
LjyLogUtil.d("$defaultTag::monitor", message + DefaultLogger.getExtInfo(stackTraceElement))
}
}
override fun isMonitorMode(): Boolean {
return mIsMonitorMode
}
override fun getDefaultTag(): String {
return "LJY_LOG"
}
}
12. 生成路由文档
// 更新 build.gradle, 添加参数 AROUTER_GENERATE_DOC = enable
// 生成的文档路径 : build/generated/source/apt/(debug or release)/com/alibaba/android/arouter/docs/arouter-map-of-${moduleName}.json
android {
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName(), AROUTER_GENERATE_DOC: "enable"]
}
}
}
}
13.拦截器和服务的异同
- 拦截器和服务所需要实现的接口不同,但是结构类似,都存在 init(Context context) 方法,但是两者的调用时机不同;
- 拦截器因为其特殊性,会被任何一次路由所触发,拦截器会在ARouter初始化的时候异步初始化,如果第一次路由的时候拦截器还没有初始化结束,路由会等待,直到初始化完成;
- 服务没有该限制,某一服务可能在App整个生命周期中都不会用到,所以服务只有被调用的时候才会触发初始化操作;
- 到此已经跟着官方文档走了一边,各种使用方法基本都列全了,下面我们来掀开源码的面纱,看看他到底是如何实现的吧
源码解析
路由原理
- 主要是通过编译的时候通过APT扫描注解,并进行相应处理,通过javapoet库生成Java代码;
- 关于APT和javapoet我在之前的探索Android开源框架 - 6. ButterKnife使用及源码解析中有介绍过,APT技术被广泛的运用在Java框架中,ButterKnife,EventBus,Dagger2以及ARouter等都运用到APT;
- 主要步骤如下:
- 调用ARouter.init方法,在LogisticsCenter中生成三个文件,Group(IRouteGroup),Providers(IProviderGroup),Root(IRouteRoot),使用Warehouse将文件保存到三个不同的HashMap中, Warehouse就相当于路由表, 保存着全部模块的跳转关系;
- 通过ARouter.navigation封装postcard对象;
- 通过ARouter索引传递到LogisticsCenter(路由中转站),询问是否存在跳转对象;
- 判断是否绿色通行和是否能通过拦截服务;
- 全部通过就会调用ActivityCompat.startActivity方法来跳转到目的Activity;
- 所以,ARouter实际还是使用原生的Framework机制startActivity,只是通过apt注解的形式制造出跳转规则,并人为的拦截跳转和设置跳转条件;
ARouter.init
public static void init(Application application) {
if (!hasInit) {//防止重复初始化
logger = _ARouter.logger;
_ARouter.logger.info(Consts.TAG, "ARouter init start.");
//真正的初始化方法:_ARouter.init
hasInit = _ARouter.init(application);
if (hasInit) {
//初始化成功后调用
_ARouter.afterInit();
}
_ARouter.logger.info(Consts.TAG, "ARouter init over.");
}
}
- _ARouter.init代码如下,主要是调用LogisticsCenter.init和创建mHandler
protected static synchronized boolean init(Application application) {
mContext = application;
LogisticsCenter.init(mContext, executor);
logger.info(Consts.TAG, "ARouter init success!");
hasInit = true;
mHandler = new Handler(Looper.getMainLooper());
return true;
}
LogisticsCenter.init
- LogisticsCenter.init代码如下,主要是负责加载路由表
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context;
executor = tpe;
try {
long startInit = System.currentTimeMillis();
//判断是不是通过arouter-register自动加载路由表
loadRouterMap();
if (registerByPlugin) {
logger.info(TAG, "Load router map by arouter-auto-register plugin.");
} else {
//不是通过插件加载路由表
//声明路由表routerMap
Set<String> routerMap;
// 如果调用了openDebug,或有新的版本
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
//遍历dex中所有的className,过滤出前缀为com.alibaba.android.arouter.routes,放到set集合里面
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
//将routerMap放到sp中,方便下次直接取
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 {
//直接在sp中取缓存
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();
for (String className : routerMap) {
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
//将前缀ARouter$$Root的class放到Warehouse.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的class放到Warehouse.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的class放到Warehouse.providersIndex
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
}
} catch (Exception e) {
throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
}
}
_ARouter.afterInit
static void afterInit() {
// 这里是获取拦截器服务实例
interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
}
ARouter.build
- 再来看看跳转时调用的ARouter.build方法,代码如下
public Postcard build(String path) {
return _ARouter.getInstance().build(path);
}
protected Postcard build(String path) {
if (TextUtils.isEmpty(path)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
//调用navigation生成PathReplaceService实例,还记得我们之前实现的TestPathReplaceServiceImpl么,
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
//如果用户实现了PathReplaceService,则调用其对跳转的path或uri进行处理
if (null != pService) {
//通过PathReplaceService处理path
path = pService.forString(path);
}
//extractGroup就是截取出group
return build(path, extractGroup(path), true);
}
}
- 下面看看三个参数的build,可以看到和上面的build一样也是先看看有没有实现PathReplaceService,是否需要对path或uri进行处理,然后new了一个Postcard
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保存了路由跳转需要的所有信息,并且有一系列withXXX方法供我们设置
public final class Postcard extends RouteMeta {
// Base
private Uri uri;
private Object tag; // A tag prepare for some thing wrong. inner params, DO NOT USE!
private Bundle mBundle; // Data to transform
private int flags = 0; // 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;
private Context context; // May application or activity, check instance type before use it.
private String action;
// Animation
private Bundle optionsCompat; // The transition animation of activity
private int enterAnim = -1;
private int exitAnim = -1;
...
}
ARouter.navigation
- build完之后就要调用navigation来执行跳转了,有一系列的重载方法,最终都是调用到_ARouter.getInstance().navigation
public Object navigation() {
return navigation(null);
}
public Object navigation(Context context) {
return navigation(context, null);
}
public Object navigation(Context context, NavigationCallback callback) {
return ARouter.getInstance().navigation(context, this, -1, callback);
}
public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) {
return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);
}
- _ARouter.getInstance().navigation代码如下
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
//获取PretreatmentService实例
PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
//如果PretreatmentService实例存在,即用户实现了预处理服务,并且onPretreatment返回了false,则拦截本次跳转
if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
// Pretreatment failed, navigation canceled.
return null;
}
// Set context to postcard.
postcard.setContext(null == context ? mContext : context);
try {
//最主要是调用了这一行
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
logger.warning(Consts.TAG, ex.getMessage());
//openDebug则toast一些提示
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();
}
});
}
//如果NavigationCallback存在则调用其onLost,就是我们之前讲的局部降级
if (null != callback) {
callback.onLost(postcard);
} else {
// NavigationCallback不存在则看看有没有实现DegradeService,就是之前讲的全局降级
// 所以局部降级存在,则全局降级就在本次路由就不会生效了
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.
//如果没有开启绿色通道,则调用拦截器服务,
//拦截器服务是在上面的_ARouter.afterInit中初始化的
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
public synchronized static void completion(Postcard postcard) {
if (null == postcard) {
throw new NoRouteFoundException(TAG + "No postcard!");
}
//根据path获取RouteMeta信息
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())) {
//如果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()));
}
//在groupIndex中找对应的groupMeta
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.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());
}
//如果type是Fragment或者IProvider则开启greenChannel,也就是不用拦截器
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;
}
}
}
真正的跳转方法
- 之前的_navigation最后一行调用了_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)) {
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:
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;
}
InterceptorServiceImpl
- 上面的拦截器服务interceptorService实现类为InterceptorServiceImpl,在其init方法中,通过线程池遍历了 Warehouse.interceptorsIndex : Map<Integer, Class<? extends IInterceptor>>创建拦截器实例放到集合List<IInterceptor>中
public void init(final Context context) {
LogisticsCenter.executor.execute(new Runnable() {
@Override
public void run() {
if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
for (Map.Entry<Integer, Class<? extends IInterceptor>> entry : Warehouse.interceptorsIndex.entrySet()) {
Class<? extends IInterceptor> interceptorClass = entry.getValue();
try {
IInterceptor iInterceptor = interceptorClass.getConstructor().newInstance();
iInterceptor.init(context);
Warehouse.interceptors.add(iInterceptor);
} catch (Exception ex) {
throw new HandlerException(TAG + "ARouter init interceptor error! name = [" + interceptorClass.getName() + "], reason = [" + ex.getMessage() + "]");
}
}
interceptorHasInit = true;
logger.info(TAG, "ARouter interceptors init over.");
synchronized (interceptorInitLock) {
interceptorInitLock.notifyAll();
}
}
}
});
}
- 在doInterceptions中通过CountDownLatch(可以理解为一个递减锁存器的计数,如果计数到达零,则释放所有等待的线程)调用来_execute,
@Override
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
checkInterceptorsInitStatus();
//如果拦截器list没有初始化
if (!interceptorHasInit) {
callback.onInterrupt(new HandlerException("Interceptors initialization takes too much time."));
return;
}
LogisticsCenter.executor.execute(new Runnable() {
@Override
public void run() {
CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
try {
_execute(0, interceptorCounter, postcard);
//拦截器如果超时会回调callback.onInterrupt
interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
if (interceptorCounter.getCount() > 0) { // Cancel the navigation this time, if it hasn't return anythings.
callback.onInterrupt(new HandlerException("The interceptor processing timed out."));
} else if (null != postcard.getTag()) { // Maybe some exception in the tag.
callback.onInterrupt((Throwable) postcard.getTag());
} else {
callback.onContinue(postcard);
}
} catch (Exception e) {
callback.onInterrupt(e);
}
}
});
} else {
callback.onContinue(postcard);
}
}
private static void _execute(final int index, final CancelableCountDownLatch counter, final Postcard postcard) {
if (index < Warehouse.interceptors.size()) {
IInterceptor iInterceptor = Warehouse.interceptors.get(index);
iInterceptor.process(postcard, new InterceptorCallback() {
@Override
public void onContinue(Postcard postcard) {
// Last interceptor excute over with no exception.
counter.countDown();
_execute(index + 1, counter, postcard); // When counter is down, it will be execute continue ,but index bigger than interceptors size, then U know.
}
@Override
public void onInterrupt(Throwable exception) {
// Last interceptor execute over with fatal exception.
postcard.setTag(null == exception ? new HandlerException("No message.") : exception); // save the exception message for backup.
counter.cancel();
// Be attention, maybe the thread in callback has been changed,
// then the catch block(L207) will be invalid.
// The worst is the thread changed to main thread, then the app will be crash, if you throw this exception!
// if (!Looper.getMainLooper().equals(Looper.myLooper())) { // You shouldn't throw the exception if the thread is main thread.
// throw new HandlerException(exception.getMessage());
// }
}
});
}
}
APT编译时注解
- 再来看看我们之前用的几个注解是如何生成类文件的,
- ARouter总共有三个自定义注解:Route,Autowired,Interceptor,对应有三个注解处理器RouteProcessor,AutowiredProcessor,InterceptorProcessor
- 以其中RouteProcessor为例,代码如下,其实APT的使用我在之前的探索Android开源框架 - 6. ButterKnife使用及源码解析中有过详细介绍了,这里就不过多说明了,有兴趣的话可以看看下面代码的实现
@AutoService(Processor.class)
@SupportedAnnotationTypes({ANNOTATION_TYPE_AUTOWIRED})
public class AutowiredProcessor extends BaseProcessor {
private Map<TypeElement, List<Element>> parentAndChild = new HashMap<>(); // Contain field need autowired and his super class.
private static final ClassName ARouterClass = ClassName.get("com.alibaba.android.arouter.launcher", "ARouter");
private static final ClassName AndroidLog = ClassName.get("android.util", "Log");
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
logger.info(">>> AutowiredProcessor init. <<<");
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
if (CollectionUtils.isNotEmpty(set)) {
try {
logger.info(">>> Found autowired field, start... <<<");
categories(roundEnvironment.getElementsAnnotatedWith(Autowired.class));
generateHelper();
} catch (Exception e) {
logger.error(e);
}
return true;
}
return false;
}
private void generateHelper() throws IOException, IllegalAccessException {
TypeElement type_ISyringe = elementUtils.getTypeElement(ISYRINGE);
TypeElement type_JsonService = elementUtils.getTypeElement(JSON_SERVICE);
TypeMirror iProvider = elementUtils.getTypeElement(Consts.IPROVIDER).asType();
TypeMirror activityTm = elementUtils.getTypeElement(Consts.ACTIVITY).asType();
TypeMirror fragmentTm = elementUtils.getTypeElement(Consts.FRAGMENT).asType();
TypeMirror fragmentTmV4 = elementUtils.getTypeElement(Consts.FRAGMENT_V4).asType();
// Build input param name.
ParameterSpec objectParamSpec = ParameterSpec.builder(TypeName.OBJECT, "target").build();
if (MapUtils.isNotEmpty(parentAndChild)) {
for (Map.Entry<TypeElement, List<Element>> entry : parentAndChild.entrySet()) {
// Build method : 'inject'
MethodSpec.Builder injectMethodBuilder = MethodSpec.methodBuilder(METHOD_INJECT)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(objectParamSpec);
TypeElement parent = entry.getKey();
List<Element> childs = entry.getValue();
String qualifiedName = parent.getQualifiedName().toString();
String packageName = qualifiedName.substring(0, qualifiedName.lastIndexOf("."));
String fileName = parent.getSimpleName() + NAME_OF_AUTOWIRED;
logger.info(">>> Start process " + childs.size() + " field in " + parent.getSimpleName() + " ... <<<");
TypeSpec.Builder helper = TypeSpec.classBuilder(fileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(type_ISyringe))
.addModifiers(PUBLIC);
FieldSpec jsonServiceField = FieldSpec.builder(TypeName.get(type_JsonService.asType()), "serializationService", Modifier.PRIVATE).build();
helper.addField(jsonServiceField);
injectMethodBuilder.addStatement("serializationService = $T.getInstance().navigation($T.class)", ARouterClass, ClassName.get(type_JsonService));
injectMethodBuilder.addStatement("$T substitute = ($T)target", ClassName.get(parent), ClassName.get(parent));
// Generate method body, start inject.
for (Element element : childs) {
Autowired fieldConfig = element.getAnnotation(Autowired.class);
String fieldName = element.getSimpleName().toString();
if (types.isSubtype(element.asType(), iProvider)) { // It's provider
if ("".equals(fieldConfig.name())) { // User has not set service path, then use byType.
// Getter
injectMethodBuilder.addStatement(
"substitute." + fieldName + " = $T.getInstance().navigation($T.class)",
ARouterClass,
ClassName.get(element.asType())
);
} else { // use byName
// Getter
injectMethodBuilder.addStatement(
"substitute." + fieldName + " = ($T)$T.getInstance().build($S).navigation()",
ClassName.get(element.asType()),
ARouterClass,
fieldConfig.name()
);
}
// Validater
if (fieldConfig.required()) {
injectMethodBuilder.beginControlFlow("if (substitute." + fieldName + " == null)");
injectMethodBuilder.addStatement(
"throw new RuntimeException(\"The field '" + fieldName + "' is null, in class '\" + $T.class.getName() + \"!\")", ClassName.get(parent));
injectMethodBuilder.endControlFlow();
}
} else { // It's normal intent value
String originalValue = "substitute." + fieldName;
String statement = "substitute." + fieldName + " = " + buildCastCode(element) + "substitute.";
boolean isActivity = false;
if (types.isSubtype(parent.asType(), activityTm)) { // Activity, then use getIntent()
isActivity = true;
statement += "getIntent().";
} else if (types.isSubtype(parent.asType(), fragmentTm) || types.isSubtype(parent.asType(), fragmentTmV4)) { // Fragment, then use getArguments()
statement += "getArguments().";
} else {
throw new IllegalAccessException("The field [" + fieldName + "] need autowired from intent, its parent must be activity or fragment!");
}
statement = buildStatement(originalValue, statement, typeUtils.typeExchange(element), isActivity);
if (statement.startsWith("serializationService.")) { // Not mortals
injectMethodBuilder.beginControlFlow("if (null != serializationService)");
injectMethodBuilder.addStatement(
"substitute." + fieldName + " = " + statement,
(StringUtils.isEmpty(fieldConfig.name()) ? fieldName : fieldConfig.name()),
ClassName.get(element.asType())
);
injectMethodBuilder.nextControlFlow("else");
injectMethodBuilder.addStatement(
"$T.e(\"" + Consts.TAG + "\", \"You want automatic inject the field '" + fieldName + "' in class '$T' , then you should implement 'SerializationService' to support object auto inject!\")", AndroidLog, ClassName.get(parent));
injectMethodBuilder.endControlFlow();
} else {
injectMethodBuilder.addStatement(statement, StringUtils.isEmpty(fieldConfig.name()) ? fieldName : fieldConfig.name());
}
// Validator
if (fieldConfig.required() && !element.asType().getKind().isPrimitive()) { // Primitive wont be check.
injectMethodBuilder.beginControlFlow("if (null == substitute." + fieldName + ")");
injectMethodBuilder.addStatement(
"$T.e(\"" + Consts.TAG + "\", \"The field '" + fieldName + "' is null, in class '\" + $T.class.getName() + \"!\")", AndroidLog, ClassName.get(parent));
injectMethodBuilder.endControlFlow();
}
}
}
helper.addMethod(injectMethodBuilder.build());
// Generate autowire helper
JavaFile.builder(packageName, helper.build()).build().writeTo(mFiler);
logger.info(">>> " + parent.getSimpleName() + " has been processed, " + fileName + " has been generated. <<<");
}
logger.info(">>> Autowired processor stop. <<<");
}
}
private String buildCastCode(Element element) {
if (typeUtils.typeExchange(element) == TypeKind.SERIALIZABLE.ordinal()) {
return CodeBlock.builder().add("($T) ", ClassName.get(element.asType())).build().toString();
}
return "";
}
private String buildStatement(String originalValue, String statement, int type, boolean isActivity) {
switch (TypeKind.values()[type]) {
case BOOLEAN:
statement += (isActivity ? ("getBooleanExtra($S, " + originalValue + ")") : ("getBoolean($S)"));
break;
case BYTE:
statement += (isActivity ? ("getByteExtra($S, " + originalValue + ")") : ("getByte($S)"));
break;
case SHORT:
statement += (isActivity ? ("getShortExtra($S, " + originalValue + ")") : ("getShort($S)"));
break;
case INT:
statement += (isActivity ? ("getIntExtra($S, " + originalValue + ")") : ("getInt($S)"));
break;
case LONG:
statement += (isActivity ? ("getLongExtra($S, " + originalValue + ")") : ("getLong($S)"));
break;
case CHAR:
statement += (isActivity ? ("getCharExtra($S, " + originalValue + ")") : ("getChar($S)"));
break;
case FLOAT:
statement += (isActivity ? ("getFloatExtra($S, " + originalValue + ")") : ("getFloat($S)"));
break;
case DOUBLE:
statement += (isActivity ? ("getDoubleExtra($S, " + originalValue + ")") : ("getDouble($S)"));
break;
case STRING:
statement += (isActivity ? ("getExtras() == null ? " + originalValue + " : substitute.getIntent().getExtras().getString($S, " + originalValue + ")") : ("getString($S)"));
break;
case SERIALIZABLE:
statement += (isActivity ? ("getSerializableExtra($S)") : ("getSerializable($S)"));
break;
case PARCELABLE:
statement += (isActivity ? ("getParcelableExtra($S)") : ("getParcelable($S)"));
break;
case OBJECT:
statement = "serializationService.parseObject(substitute." + (isActivity ? "getIntent()." : "getArguments().") + (isActivity ? "getStringExtra($S)" : "getString($S)") + ", new " + TYPE_WRAPPER + "<$T>(){}.getType())";
break;
}
return statement;
}
/**
* Categories field, find his papa.
*
* @param elements Field need autowired
*/
private void categories(Set<? extends Element> elements) throws IllegalAccessException {
if (CollectionUtils.isNotEmpty(elements)) {
for (Element element : elements) {
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
if (element.getModifiers().contains(Modifier.PRIVATE)) {
throw new IllegalAccessException("The inject fields CAN NOT BE 'private'!!! please check field ["
+ element.getSimpleName() + "] in class [" + enclosingElement.getQualifiedName() + "]");
}
if (parentAndChild.containsKey(enclosingElement)) { // Has categries
parentAndChild.get(enclosingElement).add(element);
} else {
List<Element> childs = new ArrayList<>();
childs.add(element);
parentAndChild.put(enclosingElement, childs);
}
}
logger.info("categories finished.");
}
}
}
参考
我是今阳,如果想要进阶和了解更多的干货,欢迎关注微信公众号 “今阳说” 接收我的最新文章
网友评论