EventBus-Kotlin
EventBus 원리
EventBus 는 관찰자 모드 를 기반 으로 하고 핵심 은 사건 이다.이벤트 게시 와 구독 을 통 해 구성 요소 간 의 통신 을 실현 합 니 다.EventBus 는 기본적으로 하나의 사례 로 존재 합 니 다.자바 에 서 는 Synchronized 를 사용 하여 스 레 드 안전 을 확보 해 야 합 니 다.
일반적으로 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 {
// , ,
private val executorService: ExecutorService = Executors.newCachedThreadPool()
// tag ""
private const val TAG_DEFAULT = ""
//
private val subscriberMap = mutableMapOf>()
// Method find helper
private val methodFinder = SubscriberMethodFinder(subscriberMap)
/**
* 。
* @param obj
*/
fun register(obj: Any) = executorService.execute {
methodFinder.findSubscribeMethods(obj)
}
/**
* 。
* @param event
* @param tag , “”
*/
@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
*/
fun unregister(obj: Any) = executorService.execute {
methodFinder.removeSubscriberMethod(obj)
}
fun getExecutorService() = executorService
}
디자인 요점:
4.567917.등록 과 취소 사용 스 레 드 는 찾 는 과정 이 시간 이 걸 리 기 때 문 입 니 다.(컴 파일 할 때 주석 을 사용 하 는 것 을 고려 할 수 있 습 니 다)4.567917.post 는 기본 매개 변 수 를 사 용 했 습 니 다.기본 매개 변 수 는''이 고 그 존 재 를 무시 할 수 있 습 니 다.그러나 같은 이벤트 유형 을 받 아들 이 는 특별한 맞 춤 형 방법 이 필요 할 때 유용 하 다
왜 Builder 모드 를 사용 하여 EventBus 를 실현 하지 않 았 습 니까?답:사건 없어 요
4.567917.사건 의 유형[EventType]에 따라 관찰자 의 정 보 를 저장 합 니 다
이벤트 종류 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 {
// , true
if (this === other) {
return true
}
// ,
if (other == null || (other.javaClass.name !== this.javaClass.name)) {
return false
}
// , obj this null
val eventType = other as EventType
val tagJudge = tag == eventType.tag
val eventJudge = eventClass.name == eventType.eventClass.name
// tag
// EventType
return tagJudge && eventJudge
}
override fun hashCode(): Int {
var hash = 7
hash = hash * 31 + eventClass.hashCode()
hash = hash * 31 + tag.hashCode()
return hash
}
}
디자인 요점:
4.567917.이런 존 재 는 주로 사건 유형 정 보 를 저장 하기 위 한 것 이다
4.567917.이것 은 Map 의 key 로 존재 하기 때문에 eventClass 와 tag 의 유형 은 모두 val 로 변 할 수 없습니다
4.567917.똑 같이 key 의 원인 으로 우 리 는 equals 와 hashCode 방법 을 다시 썼 다
주해-Subscriber
주 해 는 여러분 에 게 너무 흔 합 니 다.일부 주입 프레임 워 크 를 사용 할 때 자주 볼 수 있 습 니 다.Subscriber 주 해 는 구독 이 벤트 를 표시 하 는 방법 입 니 다.
package core.zs.eventbus.annotation
import core.zs.eventbus.ThreadMode
/**
*
* tag: tag
* mode: , post
*
* Created by Jonash on 2018/2/5.
*/
@Target(AnnotationTarget.FUNCTION) //
@Retention(AnnotationRetention.RUNTIME) // ,
annotation class Subscriber(val tag: String, val mode: ThreadMode = ThreadMode.POSTING)
디자인 요점:
주 해 는 방법 에 사용 된다
4.567917.주 해 를 반사 적 으로 해석 해 야 하기 때문에 실행 시 주 해 를 사용 합 니 다
IEvent
package core.zs.eventbus
/**
* .
*
* Created by Jonash on 2018/2/5.
*/
abstract class IEvent
디자인 포인트:
4.567917.표시 로 만 존재 하 며 구독 방법 을 검사 하 는 매개 변 수 는 IEvent 를 계승 해 야 합 니 다
4.567917.사건 에 대한 요구 가 있 으 면 이런 종 류 를 수정 할 수 있다
Subscription
package core.zs.eventbus
import java.lang.ref.WeakReference
import java.lang.reflect.Method
/**
*
*
* Created by Jonash on 2018/2/5.
*/
class Subscription(val subscriber: WeakReference,
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
}
/**
* , 。
*/
internal fun invoke(event: IEvent) {
targetMethod.invoke(subscriber.get(), event)
}
}
디자인 요점:
이 종 류 는 구독 자의 상세 한 정 보 를 저장 하 는 데 사용 된다
4.567917.중복 저장 을 방지 하기 위해 equals 와 hashCode 방법 을 다시 썼 습 니 다
4.567917.뒤의 반사 호출 방법 을 위해 invoke 방법 을 설계 했다
Subscriber MethodFinder-발견 자
상세 하 게 EventBus 의 코드 를 자세히 읽 었 다 면 이러한 장점 에 대해 잘 알 고 있 습 니 다.주로 실제 적 으로 관찰 자 를 찾 고 관찰 자 를 제거 하 는 데 사 용 됩 니 다.
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>) {
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
//
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
//
parameterTypes?.let {
if (parameterTypes.size == 1) {
var type = parameterTypes[0]
if (isAssignableFrom(IEvent::class.java, type)) {
val eventType = EventType(type as Class, 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? = getMatchEventType(type)
if (subscriptionLists == null) {
subscriptionLists = CopyOnWriteArrayList()
}
// equals hashCode
if (subscriptionLists.contains(subscription)) {
return
}
subscriptionLists.add(subscription)
// key map
subscriberMap.put(type, subscriptionLists)
}
internal fun getMatchEventType(type: EventType): CopyOnWriteArrayList? {
val keys = subscriberMap.keys
return keys.firstOrNull { it == type }?.let { subscriberMap[it] }
}
@Synchronized
fun removeSubscriberMethod(subscriber: Any) {
//
var iterator = subscriberMap.values.iterator()
while (iterator.hasNext()) {
val subscriptions: MutableList? = iterator.next()
subscriptions?.let { it: MutableList? ->
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)
}
}
}
}
// Event , map
if (subscriptions == null || subscriptions.isEmpty()) {
iterator.remove()
}
}
}
/** */
private fun isSameObject(subOne: Any, subTwo: Any) = subOne === subTwo
//
private fun isSystemClass(clazzName: String): Boolean {
if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")) {
return true
}
return false
}
// IEvent
private fun isAssignableFrom(a: Class, b: Class): Boolean = a.isAssignableFrom(b)
}
이벤트 Dispatcher-이벤트 배포 자
이벤트 발송 방법 보기:
/**
* 。
* @param event
* @param tag , “”
*/
@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)
}
}
EventDispatcher 의 실현:
package core.zs.eventbus
import android.os.Looper
import core.zs.eventbus.handler.*
import java.util.concurrent.CopyOnWriteArrayList
/**
*
*
* 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) =
list.forEach { it: Subscription ->
it.let {
val subscriber = it.subscriber.get()
subscriber?.let { subscriber: Any ->
val eventHandler = getEventHandler(it.threadMode)
eventHandler.handleEvent(it, event)
}
}
}
// ThreadMode
private fun getEventHandler(mode: ThreadMode): EventHandler = when (mode) {
ThreadMode.POSTING -> postHandler
ThreadMode.ASYNC -> asyncHandler
ThreadMode.MAIN -> mainHandler
ThreadMode.BACKGROUND -> bgHandler
}
}
이렇게 쉽게 이 루어 졌 다.
EventHandler
우 리 는 주 해 를 설계 할 때 구독 자가 스 레 드 를 실행 하 는 모델 을 정의 했다.
package core.zs.eventbus
/**
* ThreadMode
*
* Created by Jonash on 2018/2/5.
*/
enum class ThreadMode {
POSTING, MAIN, ASYNC, BACKGROUND
}
솔직히 이 종 류 는 완전히 보 는 자바 EventBus 코드 입 니 다.
EventHandler 의 실현:
package core.zs.eventbus.handler
import core.zs.eventbus.IEvent
import core.zs.eventbus.Subscription
interface EventHandler {
fun handleEvent(subscription: Subscription, event: IEvent)
}
디자인 요점:
4.567917.EventHandler 는 하나의 인터페이스 이다
4.567917.필요 하 다 면 이 인 터 페 이 스 를 충분히 실현 할 수 있 습 니 다
4.567917.ThreadMode 의 몇 가지 모델 에 대응 하기 위해 디자인 은 네 가지 유형의 사건 처 리 를 실현 했다
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 캐 시 풀
캐 시 풀 의 실현 은 주로 두 가지 내용 을 포함 합 니 다.PendingPost Queue 와 PendingPost 는 일반적인 실현 을 사 용 했 습 니 다.
PendingPostQueue
package core.zs.eventbus
/**
* PendingPost
*
* 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
/**
* 。
* 。
*
* 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()
@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)
}
}
}
}
}
총결산 어
EventBus 는 우리 의 개발 에서 이렇게 흔히 볼 수 있 기 때문에 우 리 는 이 간략화 Kotlin 버 전 을 실현 하기 시 작 했 습 니 다.보시 고 얻 은 것 이 있 기 를 바 랍 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.