EventBus 소스 코드 깊이 분석
25637 단어 Android 소스 코드 분석
본 분석 을 바탕 으로 하 는 코드 는 여기 서 clone 에서 볼 수 있 습 니 다. 강력 한 건 의 는 코드 를 보면 서 본 고 를 조회 하 는 것 입 니 다. 본 글 은 주로 eventbus 의 핵심 사상 을 분석 합 니 다. 버 전 차이 로 인해 일부 세부 사항 이 다 를 수 있 지만 저 는 여러분 들 이 디 테 일 에 빠 져 헤 어 나 오지 못 하고 높 은 각도 에서 그의 사상 을 이해 하 는 것 이 우리 의 목적 이 라 고 생각 합 니 다. 따라서...일부 기본 개념 은 이미 많은 문장 이 이미 말 했 으 니, 다음 글 은 더 이상 군말 하지 않 겠 다.
이전의 사 고 를 분석 하 다
// RegisterActivity.java
// onCreate
Bus.getDefault().register(this);
// onDestroy
Bus.getDefault().unRegister(this);
//
@BusReceiver(mode = EventMode.Thread)
public void onThreadEvent(final String event) {
Log.v(TAG, "onThreadEvent=" + Thread.currentThread().getName());
appendLog("onThreadEvent event=" + event
+ " thread=" + Thread.currentThread().getName());
}
// PostActivity.java
// onCreate
Bus.getDefault().post("Hello EventBus");
4 개의 중요 한 데이터 구조
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BusReceiver {
//
Bus.EventMode mode() default Bus.EventMode.Main;
}
Bus.getDefault().post("Hello EventBus");
public class MethodInfo {
//
public final Method method;
// Class
public final Class> targetType;
// Class
public final Class> eventType;
//
public final String name;
// mode
public final Bus.EventMode mode;
public MethodInfo(final Method method, final Class> targetClass, final Bus.EventMode mode) {
this.method = method;
this.targetType = targetClass;
this.eventType = method.getParameterTypes()[0];
this.mode = mode;
this.name = targetType.getName() + "." + method.getName()
+ "(" + eventType.getName() + ")";
}
// else codes
}
class Subscriber {
public final MethodInfo method;
public final Object target;
// 4 , MethodInfo
public final Class> targetType;
public final Class> eventType;
public final Bus.EventMode mode;
public final String name;
public Subscriber(final MethodInfo method, final Object target) {
this.method = method;
this.target = target;
this.eventType = method.eventType;
this.targetType = method.targetType;
this.mode = method.mode;
this.name = method.name;
}
// , event post
public Object invoke(Object event)
throws InvocationTargetException, IllegalAccessException {
return this.method.method.invoke(this.target, event);
}
// else codes
}
public class EventEmitter implements Runnable {
private static final String TAG = Bus.TAG;
public final Bus bus;
public final Object event;
public final Subscriber subscriber;
public final Bus.EventMode mode;
public final boolean debug;
public EventEmitter(final Bus bus, final Object event,
final Subscriber subscriber, final boolean debug) {
this.bus = bus;
this.event = event;
this.subscriber = subscriber;
this.mode = subscriber.mode;
this.debug = debug;
}
@Override
public void run() {
try {
if (debug) {
Log.v(TAG, "sending event:[" + event
+ "] to subscriber:[" + subscriber
+ "] at thread:" + Thread.currentThread().getName());
}
subscriber.invoke(event);
} catch (Exception e) {
if (debug) {
Log.e(TAG, "sending event:[" + event + "] to subscriber:["
+ subscriber + "] failed, reason: " + e, e);
}
}
}
@Override
public String toString() {
return "{" +
"event:[" + event +
"] to subscriber:[" + subscriber +
"]}";
}
네 개의 데이터 구 조 를 본 후에 그의 교묘 한 디자인 에 감탄 하고 층 층 포장 을 이용 하여 결합 을 풀 며 단일 한 직책 의 원칙 에 부합 되 고 구 조 를 뚜렷하게 유지 하기 쉽다.
등록 시 행동
MethodInfo
Bus.getDefault().register(this)
final static Map> sMethodCache = new ConcurrentHashMap>();
// ,
private Set getMethods(Class> targetClass) {
// key
String cacheKey = targetClass.getName();
Set methods;
// , ,
synchronized (Cache.sMethodCache) {
methods = Cache.sMethodCache.get(cacheKey);
}
if (methods == null) {
/**
*
*/
methods = mMethodFinder.find(this, targetClass);
synchronized (Cache.sMethodCache) {
Cache.sMethodCache.put(cacheKey, methods);
}
}
return methods;
}
private final Map, Set> mSubscriberMap;
의 구체 적 인 실현 을 보 세 요. 코드 는 간단 합 니 다. MethodHelp. java 류 에서 보 세 요. 간단 한 정 리 를 하 세 요. 이러한 역할 은 요구 에 부 합 된 Method 조합 을 MethodInfo 집합 으로 선별 하 는 것 입 니 다. 선택 한 규칙 은 다음 과 같은 몇 가지 가 있 습 니 다 (isValid Method 함수 에서)mMethodFinder.find(this, targetClass);
함 수 는 반드시 Public !Modifier.isPublic(method.getModifiers())
함 수 는 정적 Modifier.isStatic(method.getModifiers())
함 수 는 하나의 매개 변수 method.getParameterTypes().length != 1
이 조건 은 매우 중요 합 니 다. getDeclared Methods 에 존재 하 는 bug 를 복원 하 는 것 입 니 다. 여기 public static Set findSubscriberMethodsByAnnotation(
final Class> targetClass) {
final MethodConverter converter = new MethodConverter() {
@Override
public MethodInfo convert(final Method method) {
// check annotation
final BusReceiver annotation = method.getAnnotation(BusReceiver.class);
if (annotation == null) {
return null;
}
if (!isValidMethod(method)) {
return null;
}
return new MethodInfo(method, targetClass, annotation.mode());
}
};
return findSubscriberMethods(targetClass, converter);
}
/**
* class , " ", , , while ,
* for
*/
public static Set findSubscriberMethods(
final Class> targetClass, MethodConverter converter) {
Class> clazz = targetClass;
final Set methods = new HashSet();
while (!shouldSkipClass(clazz)) {
final Method[] clsMethods = clazz.getDeclaredMethods();
for (final Method method : clsMethods) {
final MethodInfo methodInfo = converter.convert(method);
if (methodInfo != null) {
methods.add(methodInfo);
}
}
// search more methods in super class
clazz = clazz.getSuperclass();
}
return methods;
}
행동
매개 변수 형식 을 key 로 하 는 이 유 는 이벤트 버스 가 함수 이름 이 아 닌 매개 변수 형식 을 호출 하 는 지 확인 하기 때 문 입 니 다. 따라서 post 의 매개 변수 가 설명 한 리 셋 함수 의 매개 변수 와 호 환 되 기만 하면 호출 할 수 있 습 니 다!
final static Map>> sEventTypeCache = new ConcurrentHashMap>>();
for (Class> eventType : eventTypes) {
postEventByType(event, eventType);
}
/**
*
*
* @param event
* @param eventType
* @param
*/
private synchronized void postEventByType(final E event, final Class> eventType) {
final Set subscribers = mSubscriberMap.get(eventType);
if (subscribers == null || subscribers.isEmpty()) {
return;
}
for (Subscriber subscriber : subscribers) {
sendEvent(new EventEmitter(this, event, subscriber, mDebug));
}
}
public void sendEvent(EventEmitter emitter) {
if (mDebug) {
Log.v(TAG, "send event:" + emitter);
}
if (EventMode.Sender.equals(emitter.mode)) {
mSenderScheduler.post(emitter);
} else if (EventMode.Main.equals(emitter.mode)) {
if (Helper.isMainThread()) {
mSenderScheduler.post(emitter);
} else {
mMainScheduler.post(emitter);
}
} else if (EventMode.Thread.equals(emitter.mode)) {
mThreadScheduler.post(emitter);
}
}
@Override
public void run() {
try {
subscriber.invoke(event);
} catch (Exception e) {}
}
등록 해제 시 행동
!Modifier.isVolatile(method.getModifiers())
public void unregister(final T target) {
if (mDebug) {
Log.v(TAG, "unregister() target:" + target);
mStopWatch.start("unregister()");
}
//mEventMap register
final Set> eventTypes = mEventMap.remove(target);
if (eventTypes == null || eventTypes.isEmpty()) {
Log.v(TAG, "unregister() no subscriber for target:" + target);
return;
}
for (Class> eventType : eventTypes) {
Set subscribers = mSubscriberMap.get(eventType);
if (subscribers == null || subscribers.isEmpty()) {
continue;
}
synchronized (mSubscriberMap) {
Iterator it = subscribers.iterator();
while (it.hasNext()) {
final Subscriber subscriber = it.next();
if (subscriber.target == target) {
it.remove();
}
}
}
}
}
}
캐 시 처리
static class Cache {
// key= target.getClass().getName()
// value= @BusReceiver
// targetTypeName->method set
final static Map> sMethodCache =
new ConcurrentHashMap>();
// key=
// value=
// eventTypeName-> event type set
final static Map>> sEventTypeCache =
new ConcurrentHashMap>>();
}
public static boolean shouldSkipClass(final Class> clazz) {
if (clazz == null || Object.class.equals(clazz)) {
return true;
}
final String clsName = clazz.getName();
return clsName.startsWith("java.")
|| clsName.startsWith("javax.")
|| clsName.startsWith("android.")
|| clsName.startsWith("com.android.");
}
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Gradle For Androidgradle 파일 은 하나의 항목 에 대응 하고 작업 은 구 축 된 스 크 립 트 에 정 의 됩 니 다.빌 드 작업 을 시작 할 때마다 Gradle 은 스 크 립 트 에 따라 항목 과 작업 대상 을 수집 하고 하나의...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.