EventBus
EventBus使用很简单,在build.gradle中添加依赖
implementation 'org.greenrobot:eventbus:3.2.0'
然后按照下面官方推荐的方式,就可以上手了:
在不配置annotationProcessor的情况下,EventBus是通过反射来实现消息传递,配置annotationProcessor后是主要通过apt生成文件来辅助实现(官方也更加推荐使用这种方式,当然了apt这种方式还是有少量的反射在里面)。下面是官方的相关描述:
反射
今天讲讲EventBus如何通过反射实现。首先要解决的第一问题是找到订阅的方法。
怎么找接收事件的方法?
答:在接收事件的方法上添加注释标识,有了注解标识后,就能通过类信息过滤获取到。
先定义一个@Subscribe
Subscribe.kt
@Target(
AnnotationTarget.FUNCTION
)
@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
// threadMode 定义接收事件的线程
annotation class Subscribe(val threadMode: ThreadMode = ThreadMode.POSTING)
Subscribe注解,接收一个ThreadMode参数,用于指定接收事件的线程。ThreadMode是一个枚举类。
定义如下
ThreadMode.kt
/**
* 线程状态
*/
enum class ThreadMode {
POSTING, // 事件的处理在和事件的发送在相同的进程,所以事件处理时间不应太长,不然影响事件的发送线程,而这个线程可能是UI线程
MAIN, // 事件的处理会在UI线程中执行,事件处理不应太长时间
BACKGROUND, // 后台进程,处理如保存到数据库等操作
ASYNC // 异步执行,另起线程操作。事件处理会在单独的线程中执行,主要用于在后台线程中执行耗时操作
}
注解标识有了,下一步提取订阅方法。
EventBus.kt
class EventBus private constructor() {
// 用来保存这些带注解的方法(订阅者的回调方法),MethodManager是封装类
var cacheMap: HashMap<Any, List<MethodManager>> = HashMap()
//注册遍历
fun register(getter: Any) {
var methodList = cacheMap[getter]
//如果之前没有遍历过,就遍历,然后保存
if (methodList == null) {
methodList = findAnnotationMethod(getter)
cacheMap[getter] = methodList
}
}
private fun findAnnotationMethod(getter: Any): List<MethodManager> {
val methodList: ArrayList<MethodManager> = ArrayList()
// 获取类 com.loveqrc.eventbus.MainActivity
var clazz: Class<*>? = getter.javaClass
// 获取类的所有方法
val methods = clazz!!.methods
while (clazz != null) {
val clazzName = clazz.name
//循环遍历终止条件
if (clazzName.startsWith("java.") || clazzName.startsWith("javax.")
|| clazzName.startsWith("android.") || clazzName.startsWith("androidx.")
) {
break
}
for (method in methods) {
// 注解为空的,那么就不是订阅的方法
val subscribe = method.getAnnotation(Subscribe::class.java) ?: continue
// public final void com.loveqrc.eventbus.MainActivity.message(java.lang.String)
//方法的参数
val parameterTypes = method.parameterTypes
if (parameterTypes.size != 1) {
throw RuntimeException(method.name + "方法必需有且只有一个参数")
}
// 完全符合要求、规范的方法,保存到方法对象中MethodManager(3个重要成员:方法、参数、线程)
// MethodManager为方法的封装类
methodList.add(MethodManager(parameterTypes[0], subscribe.threadMode, method))
}
clazz = clazz.superclass
}
return methodList
}
companion object {
@Volatile
private var instance: EventBus? = null
@JvmStatic
fun getDefault() = instance ?: synchronized(this) {
instance ?: EventBus().also {
instance = it
}
}
}
}
通过EventBus.getDefault()获取单例对象,然后调用register方法,通过注解找到订阅方法。其中MethodManager只是一个封装类,为后续调用EventBus.getDefault().post("")方法提供需要的信息。
MethodManager.kt
/**
* 保存符合要求的订阅方法封装类
*/
class MethodManager(
// 订阅者的回调方法(注解方法)的参数类型
// threadMode :订阅者的回调方法(注解方法)的线程模式
// method: 订阅者的回调方法(注解方法)
var type: Class<*>, var threadMode: ThreadMode, var method: Method
) {
@NonNull
override fun toString(): String {
return "MethodManager{" +
"type=" + type +
", threadMode=" + threadMode +
", method=" + method +
'}'
}
}
进行post消息传递
有了订阅者的信息,进行post消息传递就很简单了
EventBus.kt
class EventBus private constructor() {
......
var handler: Handler = Handler(Looper.getMainLooper())
// 创建一个子线程(缓存线程池)
var executorService: ExecutorService = Executors.newCachedThreadPool()
......
}
新增两个成员变量,用于切换到订阅者需要的线程。
EventBus.kt
class EventBus private constructor() {
......
fun post(setter: Any) {
// 订阅者已经登记,从登记表中找出
val keys = cacheMap.keys
for (key in keys) {
// 比如获取MainActivity对象
// 获取MainActivity中所有注解的方法
val methodList = cacheMap[key] ?: continue
// 循环每个方法
for (method in methodList) {
// 有可能多个方法的参数一样,从而都同时收到发送的消息
if (method.type.isAssignableFrom(setter.javaClass)) {
// 通过方法的类型匹配,从SecondActivity发送的对象(参数)
// 匹配MainActivity中所有注解的方法符合要求的,都发送消息
// class1.isAssignableFrom(class2) 判定此 Class 对象所表示的类或接口
// 与指定的 Class 参数所表示的类或接口是否相同,或是否是其超类或超接口
// 线程调度
when (method.threadMode) {
ThreadMode.POSTING -> {
invoke(method, key, setter)
}
ThreadMode.MAIN -> {
if (Looper.myLooper() == Looper.getMainLooper()) {
invoke(method, key, setter)
} else {
handler.post {
invoke(method, key, setter);
}
}
}
ThreadMode.BACKGROUND -> {
if (Looper.myLooper() == Looper.getMainLooper()) {
executorService.execute {
invoke(method, key, setter)
}
} else {
invoke(method, key, setter)
}
}
else -> {
//do nothing
}
}
}
}
}
}
// 找到匹配方法后,通过反射调用MainActivity中所有符合要求的方法
private fun invoke(method: MethodManager, getter: Any, setter: Any) {
val execute = method.method
try {
execute.invoke(getter, setter)
} catch (e: Exception) {
e.printStackTrace()
}
}
......
}
代码的注释已经很明了,在cacheMap中,找到符合的订阅方法,然后反射调用。
总结
总体来说EventBus反射的方式原理还是相对简单的,下一步分析如何通过apt实现EventBus功能。