EventBus 이해 및 소스 분석
EventBus에서 해결해야 할 문제
일상적인 인코딩에서 우리는 많은 네트워크 요청, 데이터베이스 조작 등을 만날 수 있다. 일반적인 상황에서 이런 조작은
를 통해 이루어진다.예를 들면 Volley
입니다.ImageRequest request = new ImageRequest(url,
new Response.Listener<Bitmap>() {
@Override
public void onResponse(Bitmap bitmap) {
mImageView.setImageBitmap(bitmap);
}
}, 0, 0, null,
new Response.ErrorListener() {
public void onErrorResponse(VolleyError error) {
mImageView.setImageResource(R.drawable.image_load_error);
}
});
이때 당신은 많은
를 사용해야 한다면 한 가지 문제를 발견하고 생각하기 시작할 것이다.예를 들어 인터넷 상태 변화, 앱의 설치 상황, 내용의 다운로드 상황을 감청해야 하는 물건을 개발하고 있다.많은
가 존재하면'이 사건들을 감청자에게 어떻게 통지하는가'는 복용할 수 있는 모듈이다. 이것이 바로 EventBus
존재의 의미이다.여기서 여러분이 한 가지 문제를 알아야 합니다.
자체가 복용할 수 있는 모듈입니다.EventBus
를 통해 자신의 사건을 발표할 수 있다. 사용자는 이 모듈에서 자신에게 관심이 있는 내용을 등록하면 된다.EventBus의 이점 및 도입 문제
장점이 비교적 뚜렷하다. 바로 하나
를 독립한 것이다. 호출자는 이 모듈을 사용하여 일부 라인 전환 문제를 차단하고 게시 구독 기능을 간단하게 실현할 수 있다.나쁜 점은 비교적 은연중에 있을 수 있지만, 이러한 수요는 우리의 중시를 불러일으키기에 충분하다
어쨌든 프로젝트 안에 대량의 이벤트 상호작용이 있다면
EventBus
를 통해 실현할 수 있다. 그렇지 않으면 모듈 내부에서 실현하는 것을 추천한다
EventBus 소스 분석EventBus.java
원본 코드 읽기는 외관 클래스부터 시작하는데 여기
EventBus.java
는 핵심 인터페이스가 모두 이 클래스에서 이루어진다. 내용에 관심이 있는 호출자는register 방법을 사용하고 사건이 발생할 때onEvent에서 해당하는 리셋을 받는다.register(Object object);
registerSticky(Object object);
unRegister(Object object);
post(Object object);
먼저 초기화 부분을 보고, 어떻게 하나의 예를 실현할 수 있는지 봅시다.
// volatile , defaultInstance , , 。
static volatile EventBus defaultInstance;
/** Convenience singleton for apps using a process-wide EventBus instance. */
public static EventBus getDefault() {
// ,volatile 。 defaultInstance ,
if (defaultInstance == null) {
synchronized (EventBus.class) {
// , defaultInstance , double-check
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
// , defaultInstance , lazy
//
static {
defaultInstance = new EventBus();
}
EventBus
Event Bus Builder를 구현하여 Builder 방식으로 구축할 때 더욱 쉽게public static EventBusBuilder builder() {
return new EventBusBuilder();
}
다음 중점 보기
register(Object subscriber, boolean sticky, int priority)
방법private synchronized void register(Object subscriber, boolean sticky, int priority) {
// subscriberMethodFinder , subscriber , 。
List<SubscriberMethod> subscriberMethods
= subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
for (SubscriberMethod subscriberMethod : subscriberMethods) {
// ,subscribe
subscribe(subscriber, subscriberMethod, sticky, priority);
}
}
findSubscriberMethods라는 방법은 이벤트Bus를 실현하는 핵심 코드로 이벤트Bus가 은밀하게 정의한 상호작용 프로토콜을 포함한다.이 방법에서 이벤트버스의 사용을 어떻게 쟁취하는지 볼 수 있다.
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
String key = subscriberClass.getName();
List<SubscriberMethod> subscriberMethods;
// Class , 。
synchronized (methodCache) {
subscriberMethods = methodCache.get(key);
}
// synchronized
if (subscriberMethods != null) {
return subscriberMethods;
}
subscriberMethods = new ArrayList<SubscriberMethod>();
Class<?> clazz = subscriberClass;
HashSet<String> eventTypesFound = new HashSet<String>();
StringBuilder methodKeyBuilder = new StringBuilder();
while (clazz != null) {
String name = clazz.getName();
// JDK
if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {
// Skip system classes, this just degrades performance
break;
}
// Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
//
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
String methodName = method.getName();
if (methodName.startsWith(ON_EVENT_METHOD_NAME)) {
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
// Public
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length());
ThreadMode threadMode;
// ‘OnEvent’, ‘OnEvent’, , 4
// ThreadModel
if (modifierString.length() == 0) {
threadMode = ThreadMode.PostThread;
} else if (modifierString.equals("MainThread")) {
threadMode = ThreadMode.MainThread;
} else if (modifierString.equals("BackgroundThread")) {
threadMode = ThreadMode.BackgroundThread;
} else if (modifierString.equals("Async")) {
threadMode = ThreadMode.Async;
} else {
if (skipMethodVerificationForClasses.containsKey(clazz)) {
continue;
} else {
throw new EventBusException("Illegal onEvent method, check for typos: " + method);
}
}
//
Class<?> eventType = parameterTypes[0];
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(methodName);
methodKeyBuilder.append('>').append(eventType.getName());
// , onEventMainThread>DownloadInfo
String methodKey = methodKeyBuilder.toString();
if (eventTypesFound.add(methodKey)) {
// Only add if not already found in a sub class
subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType));
}
}
} else if (!skipMethodVerificationForClasses.containsKey(clazz)) {
Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + clazz + "."
+ methodName);
}
}
}
// EventBus ,EventBus
clazz = clazz.getSuperclass();
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called "
+ ON_EVENT_METHOD_NAME);
} else {
synchronized (methodCache) {
methodCache.put(key, subscriberMethods);
}
return subscriberMethods;
}
}
이제 subscriber Class의 내용을 이벤트버스에 구독하는 방법을 봅시다.
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
Class<?> eventType = subscriberMethod.eventType;
// Subscription 。 CopyOnWriteArrayList , ,
// CopyOnWriteArrayList , copy,
// copy, 。
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<Subscription>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
// Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
// subscriberMethod.method.setAccessible(true);
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
// ,
if (i == size || newSubscription.priority > subscriptions.get(i).priority) {
subscriptions.add(i, newSubscription);
break;
}
}
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<Class<?>>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
if (sticky) {
// , Builder , , 20%
if (eventInheritance) {
// Existing sticky events of all subclasses of eventType have to be considered.
// Note: Iterating over all events may be inefficient with lots of sticky events,
// thus data structure should be changed to allow a more efficient lookup
// (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
for (Map.Entry<Class<?>, Object> entry : entries) {
Class<?> candidateEventType = entry.getKey();
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
// sticky event,
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
//
public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
unubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
//
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
if (!subscriptionFound) {
if (logNoSubscriberMessages) {
Log.d(TAG, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
EventBus ThreadModel
이벤트버스는 PostThread,MainThread,BackgroundThread,Async 등 총 4종의 Thread 모델을 제공했다.
에서 진행해야 할 비동기 처리를 충분히 지원할 수 있다.EventBus의 스레드 변환 방식
무릇 실제 프로젝트를 겪으면'생산'과'소비'가 충돌하는 상황이 자주 존재하기 때문에 여기서'생산자와 소비자'모델을 사용해야 한다.
이벤트버스에서 생산자와 소비자 모델의 실현은 주로
PendingPostQueue
안에 있다.PendingPostQueue의 실현은 비교적 간단하다. 주로 enqueue와poll에서 synchronized 동기화를 통해 이루어진다.
synchronized void enqueue(PendingPost pendingPost) {
if (pendingPost == null) {
throw new NullPointerException("null cannot be enqueued");
}
// Post
if (tail != null) {
tail.next = pendingPost;
tail = pendingPost;
} else if (head == null) {
// ,
head = tail = pendingPost;
} else {
throw new IllegalStateException("Head present, but no tail");
}
notifyAll();
}
synchronized PendingPost poll() {
PendingPost pendingPost = head;
//
if (head != null) {
head = head.next;
if (head == null) {
tail = null;
}
}
return pendingPost;
}
// PendingPost, pendingPostPool , PendingPost , , 。
static void releasePendingPost(PendingPost pendingPost) {
pendingPost.event = null;
pendingPost.subscription = null;
pendingPost.next = null;
synchronized (pendingPostPool) {
// Don't let the pool grow indefinitely
if (pendingPostPool.size() < 10000) {
pendingPostPool.add(pendingPost);
}
}
}
EventBus 릴리즈 시점의
// ThreadModel ( PostThread) Poster, Post `` ``, 。
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case PostThread:
invokeSubscriber(subscription, event);
break;
case MainThread:
// poster
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case BackgroundThread:
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case Async:
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Kotlin의 기초 - 2부지난 글에서는 Kotlin이 무엇인지, Kotlin의 특징, Kotlin에서 변수 및 데이터 유형을 선언하는 방법과 같은 Kotlin의 기본 개념에 대해 배웠습니다. 유형 변환은 데이터 변수의 한 유형을 다른 데이터...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.