Eventbus原理分析,自己动手写一个简易的Eventbus
Eventbus是由greenrobot组织贡献,一个Android事件发布/订阅轻量级框架,通过解耦发布者和订阅者简化Android事件传递
EventBus可以代替Android传统的Intent,Handler,Broadcast或接口函数,在Fragment,Activity,Service线程之间传递数据,进行线程间通信。
这里EventBus的用法就不多说了,项目地址https://github.com/greenrobot/EventBus
下面用一张图来说明下Eventbus的设计思想:
接下来就根据这张图来手写一个简易Eventbus,来深入了解下其中的原理(小弟正在学kotlin,所以就用kotlin来实现)
1.定义三个类分别对应图中得招聘方,应聘方和服务方(Eventbus)
招聘方:
import android.content.Intent
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//这个过程相当于图中的register
Eventbus.register(this)
btn.setOnClickListener {
startActivity(Intent(this, Main2Activity::class.java))
}
}
//这里是对应图中招聘方的要求和应聘方的简历符合后通知面试的过程
@Subscribe(threadMode = ThreadMode.MAIN)
fun event(person: Person) {
tv1.text=person.name
tv2.text=person.phone
}
override fun onDestroy() {
super.onDestroy()
Eventbus.unRegister(this)
}
}
2.应聘方
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main2.*
class Main2Activity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
btn2.setOnClickListener{
Thread(Runnable {
Eventbus.post(Person("swl","123456"))
}).start()
finish()
}
}
}
而这个person就对应图中应聘方得简历
data class Person(val name:String,val phone:String)
接下来是最重要的部分:服务方
服务方要做的事:(1)提供注册接口给招聘方,保存招聘方的岗位要求
(2)提供post接口给应聘方发送简历
(3)要去匹配应聘方的简历和招聘方的招聘要求,并通知招聘方
ThreadMode可以先不管,后面注释会说明
首先定义一个注解类,来标识在Eventbus这个平台注册的所有岗位,ThreadMode线程模式,见注释
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class Subscribe(val threadMode: ThreadMode = ThreadMode.MAIN)
然后要定义一个保存面试方式,简历和上班地点(上班地点先不管,见注释)
/**
method:就表示面试岗位
eventType:表示简历
threadMode:指定线程(上班地点,子公司还是母公司),见注释
*/
data class SubscribeMethod(val method: Method, val eventType: Class<*>,val threadMode: ThreadMode) {}
import android.os.Looper
import android.util.Log
import java.lang.RuntimeException
import java.lang.reflect.Method
/**
* @desc
* @auth ${user}
* @time 2018/8/10 10:00
*/
object Eventbus {
var map = mutableMapOf<Any, ArrayList<SubscribeMethod>>()
var handler = Handler(Looper.getMainLooper())
//第一步,提供注册接口给招聘方,保存招聘方的岗位要求
fun register(any: Any) {
map[any] ?: putSubscribeMethod(any)
}
fun unRegister(any: Any){
val list = map[any]
list!!.clear()
map.remove(any)
}
fun putSubscribeMethod(any: Any?){
var subscibeList = mutableListOf<SubscribeMethod>() as ArrayList<SubscribeMethod>
var clazz:Class<Any>?= any!!::class.java as Class<Any>?
while (clazz != null) {
val name = clazz.name
//如果是系统类我们直接跳出
if (name.startsWith(".java") || name.startsWith(".javax") || name.startsWith(".android")) {
break
}
val methods = clazz.declaredMethods
methods.forEach {
//其实这里就是取出在Eventbus注册了的所有面试岗位,通过注解名来获取
val annotation = it.getAnnotation(Subscribe::class.java) ?: return@forEach
val types = it.parameterTypes
//一个岗位只能发一份简历
if (types.size > 1) {
throw RuntimeException("不能同时注册两个消息")
}
//上班地点,线程切换,见注释
val mode = annotation.threadMode
if(Looper.myLooper()== Looper.getMainLooper()){
if(mode==ThreadMode.MAIN){
Log.i("swl","注册线程"+Thread.currentThread().name)
putSubscribeMethod(it, types, mode, subscibeList, any)
}else{
Thread(Runnable {
Log.i("swl","注册线程"+Thread.currentThread().name)
putSubscribeMethod(it, types, mode, subscibeList, any)
}).start()
}
}else{
if(mode==ThreadMode.BACKGROUND){
Log.i("swl","注册线程"+Thread.currentThread().name)
putSubscribeMethod(it, types, mode, subscibeList, any)
}else{
handler.post(Runnable { putSubscribeMethod(it, types, mode, subscibeList, any) })
}
}
}
clazz = clazz.superclass as Class<Any>?
}
}
/**
保存招聘方的招聘要求信息
*/
private fun putSubscribeMethod(it: Method, types: Array<Class<*>>, mode: ThreadMode, subscibeList: ArrayList<SubscribeMethod>, any: Any) {
val subscribeMethod = SubscribeMethod(it, types[0], mode)
subscibeList.add(subscribeMethod)
map.put(any, subscibeList)
}
/**
第二部,提供post接口给应聘方发送简历,并遍历查询招聘方的所有岗位,直到找到与应聘方简历符合的岗位后,通知面试
*/
fun post(any: Any) {
for ((k,v) in map){
v.forEach{
if(it.eventType==any.javaClass){
if(Looper.myLooper()== Looper.getMainLooper()){
if(it.threadMode==ThreadMode.MAIN){
Log.i("swl","发送线程"+Thread.currentThread().name)
/**
找到符合双方要求的岗位,通过反射调用,通知面试
*/
it.method.invoke(k,any)
}else{
Thread(Runnable {
Log.i("swl","发送线程"+Thread.currentThread().name)
it.method.invoke(k,any)
}).start()
}
}else{
if(it.threadMode==ThreadMode.BACKGROUND){
Log.i("swl","发送线程"+Thread.currentThread().name)
}else{
handler.post(Runnable { it.method.invoke(k, any) })
}
}
return
}
}
}
}
}
其实到这里Eventbus的基本功能就实现了,但我们用官方Eventbus时(@Subscribe(threadMode = ThreadMode.MAIN)),threadMode 这个参数就是为了线程切换
注释:
ThreadMode类(这里简单处理,复制了Eventbus中的两个线程模式):
enum class ThreadMode {
/**
* Subscriber will be called in Android's main thread (sometimes referred to as UI thread). If the posting thread is
* the main thread, event handler methods will be called directly. Event handlers using this mode must return
* quickly to avoid blocking the main thread.
*/
MAIN,
/**
* Subscriber will be called in a background thread. If posting thread is not the main thread, event handler methods
* will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single
* background thread, that will deliver all its events sequentially. Event handlers using this mode should try to
* return quickly to avoid blocking the background thread.
*/
BACKGROUND,
}
可以看到ThreadMode就是一个枚举类,有两个成员变量( MAIN主线程 BACKGROUND子线程 )
aaa@qq.com(threadMode = ThreadMode.MAIN)
在Subscribe时候我们会跟上一个指定线程模型的参数
2.根据这个指定的线程模型,在注册的时候切换线程
val mode = annotation.threadMode
/**
当前线程在主线程,指定线程模型在子线程,就切换到子线程进行注册
当前线程在子线程,指定线程模型在主线程,就切换到主线程
*/
if(Looper.myLooper()== Looper.getMainLooper()){//当前线程在主线程
if(mode==ThreadMode.MAIN){
Log.i("swl","注册线程"+Thread.currentThread().name)
putSubscribeMethod(it, types, mode, subscibeList, any)
}else{
/**
指定线程模型在是BACKGROUND,则新开一个线程执行注册
*/
Thread(Runnable {
Log.i("swl","注册线程"+Thread.currentThread().name)
putSubscribeMethod(it, types, mode, subscibeList, any)
}).start()
}
}else{ //当前线程在子线程
if(mode==ThreadMode.BACKGROUND){
Log.i("swl","注册线程"+Thread.currentThread().name)
putSubscribeMethod(it, types, mode, subscibeList, any)
}else{
/**
指定线程模型在是BACKGROUND,则切换到主线程注册
*/
handler.post(Runnable { putSubscribeMethod(it, types, mode, subscibeList, any) })
}
}
同理在post方法中也是一样
fun post(any: Any) {
for ((k, v) in map) {
v.forEach {
if (it.eventType == any.javaClass) {
if (Looper.myLooper() == Looper.getMainLooper()) {
if (it.threadMode == ThreadMode.MAIN) {
Log.i("swl", "发送线程" + Thread.currentThread().name)
it.method.invoke(k, any)
} else {
Thread(Runnable {
Log.i("swl", "发送线程" + Thread.currentThread().name)
it.method.invoke(k, any)
}).start()
}
} else {
if (it.threadMode == ThreadMode.BACKGROUND) {
Log.i("swl", "发送线程" + Thread.currentThread().name)
it.method.invoke(k, any)
} else {
handler.post(Runnable { it.method.invoke(k, any) })
}
}
return
}
}
}
}
至此一个简易版EventBus就完成了,如有哪里说得不正确,欢迎指正
上一篇: MySQL常用命令大全脚本之家总结