作為一個(gè)Android開發(fā)者档泽,如果你還沒(méi)開始使用Kotlin,那么你可能要思考下自己的步伐是不是慢了抑胎。本文帶著大家動(dòng)手來(lái)寫一個(gè)Kotlin版本的EventBus阿逃,一方面來(lái)看看Kotlin長(zhǎng)什么樣,一方面理解下EventBus的設(shè)計(jì)思想搀菩。
EventBus原理
EventBus 是基于觀察者模式破托,核心是事件土砂。通過(guò)事件的發(fā)布和訂閱實(shí)現(xiàn)組件之間的通信,EventBus默認(rèn)是一個(gè)單例存在吴叶,在Java中還需要使用Synchronized來(lái)保證線程安全锌俱。
通俗來(lái)講贸宏,EventBus通過(guò)注冊(cè)將所有訂閱事件的方法儲(chǔ)存在集合中吭练,當(dāng)有事件發(fā)布的時(shí)候,根據(jù)某些規(guī)則签赃,匹配出符合條件的方法锦聊,調(diào)用執(zhí)行箩绍,從而實(shí)現(xiàn)組件間的通信材蛛。
發(fā)布的事件相當(dāng)于被觀察者,注冊(cè)的對(duì)象相當(dāng)于觀察者芽淡,被觀察者和觀察者是一對(duì)多的關(guān)系挣菲。當(dāng)被觀察者狀態(tài)發(fā)生變化,即發(fā)布事件的時(shí)候唉窃,觀察者對(duì)象將會(huì)得到通知并作出響應(yīng)纹份,即執(zhí)行對(duì)應(yīng)的方法廷痘。
入口-EventBus
還是直接粘貼上代碼吧笋额,畢竟這是最直觀的兄猩。
package core.zs.eventbus
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
/**
* EventBus.
*
* Created by Jonash on 2018/2/5.
*/
object EventBus {
// 因?yàn)樽?cè),取消可能耗時(shí),因此使用多線程進(jìn)行管理
private val executorService: ExecutorService = Executors.newCachedThreadPool()
// 默認(rèn)的事件tag為""
private const val TAG_DEFAULT = ""
// 很據(jù)事件類型找到所有的注解方法信息
private val subscriberMap = mutableMapOf<EventType, CopyOnWriteArrayList<Subscription>>()
// Method find helper
private val methodFinder = SubscriberMethodFinder(subscriberMap)
/**
* 注冊(cè)觀察者鸠姨。
* @param obj 觀察者對(duì)象
*/
fun register(obj: Any) = executorService.execute {
methodFinder.findSubscribeMethods(obj)
}
/**
* 發(fā)送事件通知讶迁。
* @param event 事件
* @param tag 事件標(biāo)簽巍糯,默認(rèn)值為“”
*/
@JvmOverloads
fun post(event: IEvent, tag: String = TAG_DEFAULT) {
val eventType = EventType(event.javaClass, tag)
val list = methodFinder.getMatchEventType(eventType)
list?.let {
EventDispatcher.dispatchEvent(event, it)
}
}
/**
* 取消觀察者祟峦。
* @param obj 觀察者對(duì)象
*/
fun unregister(obj: Any) = executorService.execute {
methodFinder.removeSubscriberMethod(obj)
}
fun getExecutorService() = executorService
}
設(shè)計(jì)要點(diǎn):
- EventBus類型為object,實(shí)際上編譯為Java后是一個(gè)單例的存在徙鱼。
- 注冊(cè)和取消注冊(cè)使用線程疆偿,因?yàn)椴檎业牡倪^(guò)程是一個(gè)耗時(shí)的存在杆故。(你可以考慮使用編譯時(shí)注解)
- post使用了默認(rèn)參數(shù)溉愁,默認(rèn)參數(shù)為"",你甚至可以忽略它的存在;但是當(dāng)你需要特別定制接收同一事件類型的方法時(shí)奕塑,它會(huì)體現(xiàn)出用處家肯。
- 為什么沒(méi)使用Builder模式來(lái)實(shí)現(xiàn)EventBus?答:沒(méi)事件讨衣。
- 根據(jù)事件的類型[EventType]來(lái)存儲(chǔ)觀察者信息反镇。
事件類型EventType
package core.zs.eventbus
/**
* 記錄事件類型信息。
*
* Created by Jonash on 2018/2/5.
*/
class EventType(private val eventClass: Class<*>, private val tag: String) {
override fun equals(other: Any?): Boolean {
// 比較內(nèi)存引用地址夕玩,相同則返回 true
if (this === other) {
return true
}
// 判斷是否為空燎孟,是否屬于同一種類型
if (other == null || (other.javaClass.name !== this.javaClass.name)) {
return false
}
// 能執(zhí)行到這里缤弦,說(shuō)明obj和this同類且非 null
val eventType = other as EventType
val tagJudge = tag == eventType.tag
val eventJudge = eventClass.name == eventType.eventClass.name
// tag相同
// EventType是同一個(gè)類型
return tagJudge && eventJudge
}
override fun hashCode(): Int {
var hash = 7
hash = hash * 31 + eventClass.hashCode()
hash = hash * 31 + tag.hashCode()
return hash
}
}
設(shè)計(jì)要點(diǎn):
- 此類的存在主要是為了存儲(chǔ)事件類型信息碍沐。
- 因?yàn)榇祟愂亲鳛镸ap的key存在衷蜓,所以eventClass和tag的類型均為val不可變磁浇。
- 同樣是作為key的原因置吓,我們重寫了equals和hashCode方法。
注解-Subscriber
注解對(duì)于大家太常見(jiàn)了友题,在使用一些注入框架時(shí)經(jīng)常見(jiàn)到度宦,Subscriber注解用于標(biāo)注訂閱事件的方法。
package core.zs.eventbus.annotation
import core.zs.eventbus.ThreadMode
/**
* 事件接受方法的注解類
* tag:事件tag
* mode:觀察者處理事件的線程离唬,默認(rèn)為post
*
* Created by Jonash on 2018/2/5.
*/
@Target(AnnotationTarget.FUNCTION) // 在方法上使用
@Retention(AnnotationRetention.RUNTIME) // 運(yùn)行時(shí)注解输莺,因?yàn)橐褂梅瓷?annotation class Subscriber(val tag: String, val mode: ThreadMode = ThreadMode.POSTING)
設(shè)計(jì)要點(diǎn):
- 注解使用在方法上模闲。
- 因?yàn)橐瓷浣馕鲎⒔庹负矗虼耸褂眠\(yùn)行時(shí)注解殷蛇。
IEvent
package core.zs.eventbus
/**
* 事件實(shí)現(xiàn)接口.
*
* Created by Jonash on 2018/2/5.
*/
abstract class IEvent
設(shè)計(jì)點(diǎn):
- 僅僅是作為一個(gè)標(biāo)示存在粒梦,檢查訂閱方法的參數(shù)必須繼承了IEvent匀们。
- 如果對(duì)事件有要求,可以修改該類重抖。
Subscription
package core.zs.eventbus
import java.lang.ref.WeakReference
import java.lang.reflect.Method
/**
* 訂閱方法的詳細(xì)信息
*
* Created by Jonash on 2018/2/5.
*/
class Subscription(val subscriber: WeakReference<Any>,
private val targetMethod: Method,
val threadMode: ThreadMode) {
override fun equals(other: Any?): Boolean {
if (this === other) {
return true
}
if (other == null || (other::class !== this::class)) {
return false
}
val subscription = other as Subscription
val judgeSubscriber = this.subscriber.get() === subscription.subscriber.get()
val judgeMethod = this.targetMethod.name == subscription.targetMethod.name
return judgeSubscriber && judgeMethod
}
override fun hashCode(): Int {
var hash = 7
hash = hash * 31 + subscriber.hashCode()
hash = hash * 31 + targetMethod.hashCode()
hash = hash * 31 + threadMode.hashCode()
return hash
}
/**
* 根據(jù)傳入的實(shí)例钟沛,反射調(diào)用實(shí)例方法恨统。
*/
internal fun invoke(event: IEvent) {
targetMethod.invoke(subscriber.get(), event)
}
}
設(shè)計(jì)要點(diǎn):
- 該類用于存儲(chǔ)訂閱者的詳細(xì)信息畜埋。
- 為了防止重復(fù)存儲(chǔ)由捎,重寫了equals和hashCode方法饿凛。
- 為了后面的反射調(diào)用方法涧窒,設(shè)計(jì)了invoke方法。
SubscriberMethodFinder-發(fā)現(xiàn)者
詳細(xì)如果你仔細(xì)閱讀了EventBus的代碼硬鞍,已經(jīng)對(duì)該類優(yōu)點(diǎn)眼熟固该,它主要用于實(shí)際的查找觀察者伐坏、移除觀察者握联。
package core.zs.eventbus
import core.zs.eventbus.annotation.Subscriber
import java.lang.ref.WeakReference
import java.lang.reflect.Modifier
import java.util.concurrent.CopyOnWriteArrayList
/**
* 訂閱者輔助類
*
* Created by Jonash on 2018/2/5.
*/
class SubscriberMethodFinder(private val subscriberMap: MutableMap<EventType, CopyOnWriteArrayList<Subscription>>) {
companion object {
private val BRIDGE = 0x40
private val SYNTHETIC = 0x1000
private val MODIFIERS_IGNORE = Modifier.ABSTRACT or Modifier.STATIC or BRIDGE or SYNTHETIC
}
/**
* 查找訂閱者中所有的注解信息金闽。
* @param subscriber 訂閱者
*/
@Synchronized
fun findSubscribeMethods(subscriber: Any) {
var clazz: Class<*>? = subscriber.javaClass
while (clazz != null && !isSystemClass(clazz.name)) {
var methods = try {
// 返回所有的方法代芜,包括public/private/protected/default
clazz.declaredMethods
} catch (e: Exception) {
// public方法
clazz.methods
}
for (method in methods) {
val modifiers = method.modifiers
// 過(guò)濾方法的修飾符
if (Modifier.PUBLIC and modifiers != 0 && modifiers and MODIFIERS_IGNORE == 0) {
// 獲取到注解
val annotation = method.getAnnotation(Subscriber::class.java)
// 如果注解不為空
if (annotation != null) {
val parameterTypes = method.parameterTypes
// 方法只接收一個(gè)參數(shù)
parameterTypes?.let {
if (parameterTypes.size == 1) {
var type = parameterTypes[0]
if (isAssignableFrom(IEvent::class.java, type)) {
val eventType = EventType(type as Class<IEvent>, annotation.tag)
val subscription = Subscription(WeakReference(subscriber), method, annotation.mode)
// 保存訂閱信息
subscribe(eventType, subscription)
}
}
}
}
}
}
clazz = clazz.superclass
}
}
@Synchronized
private fun subscribe(type: EventType, subscription: Subscription) {
var subscriptionLists: CopyOnWriteArrayList<Subscription>? = getMatchEventType(type)
if (subscriptionLists == null) {
subscriptionLists = CopyOnWriteArrayList()
}
// 這就是為什么我們要重寫equals和hashCode方法的原因
if (subscriptionLists.contains(subscription)) {
return
}
subscriptionLists.add(subscription)
// 將事件類型key和訂閱者信息存儲(chǔ)到map中
subscriberMap.put(type, subscriptionLists)
}
internal fun getMatchEventType(type: EventType): CopyOnWriteArrayList<Subscription>? {
val keys = subscriberMap.keys
return keys.firstOrNull { it == type }?.let { subscriberMap[it] }
}
@Synchronized
fun removeSubscriberMethod(subscriber: Any) {
// 注意刪除的時(shí)候要使用迭代器
var iterator = subscriberMap.values.iterator()
while (iterator.hasNext()) {
val subscriptions: MutableList<Subscription>? = iterator.next()
subscriptions?.let { it: MutableList<Subscription>? ->
val subIterator = it!!.iterator()
while (subIterator.hasNext()) {
val subscription = subIterator.next()
// 獲取引用
val cacheObject = subscription.subscriber.get()
cacheObject?.let {
if (isSameObject(cacheObject, subscriber)) {
subscriptions.remove(subscription)
}
}
}
}
// 如果針對(duì)某個(gè)Event的訂閱者數(shù)量為空了,那么需要從map中清除
if (subscriptions == null || subscriptions.isEmpty()) {
iterator.remove()
}
}
}
/** 判斷是否是同一個(gè)對(duì)象 */
private fun isSameObject(subOne: Any, subTwo: Any) = subOne === subTwo
// 判斷類是否為系統(tǒng)類
private fun isSystemClass(clazzName: String): Boolean {
if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")) {
return true
}
return false
}
// 判斷某個(gè)類是否實(shí)現(xiàn)了IEvent接口
private fun isAssignableFrom(a: Class<*>, b: Class<*>): Boolean = a.isAssignableFrom(b)
}
EventDispatcher-事件分發(fā)者
看下怎樣發(fā)送一個(gè)事件:
/**
* 發(fā)送事件通知罚随。
* @param event 事件
* @param tag 事件標(biāo)簽,默認(rèn)值為“”
*/
@JvmOverloads
fun post(event: IEvent, tag: String = TAG_DEFAULT) {
val eventType = EventType(event.javaClass, tag)
// 找出所有的事件訂閱者
val list = methodFinder.getMatchEventType(eventType)
list?.let {
// 分發(fā)事件
EventDispatcher.dispatchEvent(event, it)
}
}
EventDispatcher的實(shí)現(xiàn):
package core.zs.eventbus
import android.os.Looper
import core.zs.eventbus.handler.*
import java.util.concurrent.CopyOnWriteArrayList
/**
* 事件分發(fā)者
*
* Created by Jonash on 2018/2/5.
*/
object EventDispatcher {
private val postHandler = PostEventHandler()
private val mainHandler = MainEventHandler(Looper.getMainLooper())
private val asyncHandler = AsyncEventHandler()
private val bgHandler = BackgroundHandler()
fun dispatchEvent(event: IEvent, list: CopyOnWriteArrayList<Subscription>) =
list.forEach { it: Subscription ->
it.let {
val subscriber = it.subscriber.get()
subscriber?.let { subscriber: Any ->
val eventHandler = getEventHandler(it.threadMode)
eventHandler.handleEvent(it, event)
}
}
}
// 很據(jù)ThreadMode獲取對(duì)應(yīng)的事件處理器
private fun getEventHandler(mode: ThreadMode): EventHandler = when (mode) {
ThreadMode.POSTING -> postHandler
ThreadMode.ASYNC -> asyncHandler
ThreadMode.MAIN -> mainHandler
ThreadMode.BACKGROUND -> bgHandler
}
}
實(shí)現(xiàn)的是如此的easy。
EventHandler
我們?cè)谠O(shè)計(jì)注解的時(shí)候狭郑,定義了一個(gè)訂閱者執(zhí)行線程的模式翰萨。
package core.zs.eventbus
/**
* ThreadMode枚舉類
*
* Created by Jonash on 2018/2/5.
*/
enum class ThreadMode {
POSTING, MAIN, ASYNC, BACKGROUND
}
說(shuō)實(shí)話這個(gè)類完全是看的Java EventBus的代碼亩鬼。
EventHandler的實(shí)現(xiàn):
package core.zs.eventbus.handler
import core.zs.eventbus.IEvent
import core.zs.eventbus.Subscription
interface EventHandler {
fun handleEvent(subscription: Subscription, event: IEvent)
}
設(shè)計(jì)要點(diǎn):
- EventHandler就是一個(gè)接口。
- 如果你需要黄绩,你完全可以實(shí)現(xiàn)該接口爽丹。
- 為了對(duì)應(yīng)ThreadMode的幾種模式辛蚊,設(shè)計(jì)實(shí)現(xiàn)了四種類型的事件處理袋马。
PostEventHandler
package core.zs.eventbus.handler
import core.zs.eventbus.IEvent
import core.zs.eventbus.Subscription
class PostEventHandler : EventHandler {
override fun handleEvent(subscription: Subscription, event: IEvent) =subscription.invoke(event)
}
package core.zs.eventbus.handler
import android.os.Handler
import android.os.Looper
import android.os.Message
import core.zs.eventbus.IEvent
import core.zs.eventbus.PendingPost
import core.zs.eventbus.PendingPostQueue
import core.zs.eventbus.Subscription
class MainEventHandler(looper: Looper) : Handler(looper), EventHandler {
private val queue: PendingPostQueue = PendingPostQueue()
private var handlerActive = false
override fun handleMessage(msg: Message?) {
while (true) {
var pendingPost = queue.poll()
if (pendingPost == null) {
synchronized(this) {
pendingPost = queue.poll()
if (pendingPost == null) {
handlerActive = false
return
}
}
}
pendingPost!!.subscription!!.invoke(pendingPost!!.event!!)
PendingPost.releasePendingPost(pendingPost!!)
}
}
override fun handleEvent(subscription: Subscription, event: IEvent) {
val post = PendingPost.obtainPendingPost(subscription, event)
synchronized(this) {
queue.enqueue(post)
if (!handlerActive) {
handlerActive = true
sendMessage(Message.obtain())
}
}
}
}
AsyncEventHandler
package core.zs.eventbus.handler
import core.zs.eventbus.*
class AsyncEventHandler : EventHandler {
private val queue: PendingPostQueue = PendingPostQueue()
override fun handleEvent(subscription: Subscription, event: IEvent) {
val pendingPost = PendingPost.obtainPendingPost(subscription, event)
queue.enqueue(pendingPost)
EventBus.getExecutorService().execute {
val pendingPost = queue.poll() ?: throw IllegalStateException("No pending post available")
pendingPost.subscription!!.invoke(pendingPost!!.event!!)
PendingPost.releasePendingPost(pendingPost)
}
}
}
BackgroundHandler
package core.zs.eventbus.handler
import core.zs.eventbus.*
class BackgroundHandler : Runnable, EventHandler {
private val queue: PendingPostQueue = PendingPostQueue()
@Volatile
private var executorRunning: Boolean = false
override fun handleEvent(subscription: Subscription, event: IEvent) {
val pendingPost = PendingPost.obtainPendingPost(subscription, event)
synchronized(this) {
queue.enqueue(pendingPost)
if (!executorRunning) {
executorRunning = true
EventBus.getExecutorService().execute(this)
}
}
}
override fun run() = try {
try {
while (true) {
var pendingPost = queue.poll(1000)
if (pendingPost == null) {
synchronized(this) {
pendingPost = queue.poll()
if (pendingPost == null) {
executorRunning = false
return
}
}
}
pendingPost!!.subscription!!.invoke(pendingPost!!.event!!)
PendingPost.releasePendingPost(pendingPost!!)
}
} catch (e: InterruptedException) {
e.printStackTrace()
}
} finally {
executorRunning = false
}
}
PendingPost緩存池
緩存池的實(shí)現(xiàn)主要包含兩塊內(nèi)容:PendingPostQueue和PendingPost,使用了常規(guī)的實(shí)現(xiàn)墓懂。
PendingPostQueue
package core.zs.eventbus
/**
* PendingPost隊(duì)列
*
* Created by Jonash on 2018/2/6.
*/
class PendingPostQueue {
private var head: PendingPost? = null
private var tail: PendingPost? = null
@Synchronized
fun enqueue(post: PendingPost) = when {
tail != null -> {
tail!!.next = post
tail = post
}
head == null -> {
head = post
tail = post
}
else -> throw IllegalStateException("Head present, but no tail")
}
@Synchronized
fun poll(): PendingPost? {
val post = head
if (head != null) {
head = head!!.next
if (head == null) {
tail = null
}
}
return post
}
@Synchronized
@Throws(InterruptedException::class)
fun poll(maxMillisToWait: Int): PendingPost? {
if (head == null) {
Thread.sleep(maxMillisToWait.toLong())
}
return poll()
}
}
PendingPost:
package core.zs.eventbus
/**
* 待處理請(qǐng)求捕仔。<br/>
* 使用緩存池進(jìn)行管理請(qǐng)求榜跌。
*
* Created by Jonash on 2018/2/6.
*/
class PendingPost(var event: IEvent?, var subscription: Subscription?, var next: PendingPost? = null) {
companion object {
private val pool = arrayListOf<PendingPost>()
@JvmStatic
fun obtainPendingPost(subscription: Subscription, event: IEvent): PendingPost {
if (pool.size > 0) {
val pendingPost = pool.removeAt(pool.size - 1)
pendingPost.next = null
pendingPost.subscription = subscription
pendingPost.event = event
return pendingPost
}
return PendingPost(event, subscription)
}
@JvmStatic
fun releasePendingPost(pendingPost: PendingPost) {
pendingPost.event = null
pendingPost.subscription = null
pendingPost.next = null
synchronized(pool) {
if (pool.size < 1000) {
pool.add(pendingPost)
}
}
}
}
}
總結(jié)語(yǔ)
EventBus在我們的開發(fā)中如此常見(jiàn)钓葫,以至于我們才動(dòng)手實(shí)現(xiàn)了這個(gè)簡(jiǎn)化Kotlin版本础浮,希望你看完有所收獲奠骄。