精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

一篇帶給你EventBus原理解析

開發(fā) 前端
EventBus基于觀察者模式,原理是大體也是將觀察者與要觀察的消息注冊到一個Map,當(dāng)有消息發(fā)布時,從Map中找到觀察者,并調(diào)用觀察者對應(yīng)的方法,下面我們基于源碼進(jìn)行解析來看看是不是這樣的原理。

EventBus基于觀察者模式,原理是大體也是將觀察者與要觀察的消息注冊到一個map,當(dāng)有消息發(fā)布時,從map中找到觀察者,并調(diào)用觀察者對應(yīng)的方法,下面我們基于源碼進(jìn)行解析來看看是不是這樣的原理

不廢話,沒有賣課,直接來

1、EventBus訂閱

(1)訂閱整體流程

EventBus的定義始于register方法:

public void register(Object subscriber) {
//獲取訂閱者/觀察者的class對象
Class<?> subscriberClass = subscriber.getClass();
//從訂閱者class對象中找出所有訂閱消息的方法列表
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
//變量訂閱消息的方法列表
//定義消息
subscribe(subscriber, subscriberMethod);
}
}
}

register方法主要是從訂閱者class對象中找出所有訂閱消息的方法列表,然后對每個訂閱方法調(diào)用subscribe進(jìn)行訂閱:

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//方法訂閱的消息類型,即訂閱方法的參數(shù)類型class
Class<?> eventType = subscriberMethod.eventType;
//創(chuàng)建訂閱者/觀察者
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//從事件/訂閱者map中,找出訂閱事件的訂閱者列表
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
//事件/訂閱者map中沒有事件相關(guān)的訂閱者,則:
//新建訂閱事件的訂閱者列表
subscriptions = new CopyOnWriteArrayList<>();
//并將事件的class作為key,事件訂閱者列表作為value存入事件/訂閱者map
subscriptionsByEventType.put(eventType, subscriptions);
} else {
//
if (subscriptions.contains(newSubscription)) {
//已經(jīng)訂閱過,則拋出異常
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
//按優(yōu)先級將訂閱者加入對應(yīng)的位置
subscriptions.add(i, newSubscription);
break;
}
}
//訂閱者/事件map中找到訂閱事件列表
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
//如果訂閱者/事件map沒有訂閱事件列表,則:
//新建訂閱事件列表,并存入訂閱者/事件map中,key是訂閱者,value是要訂閱的事件列表
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
if (subscriberMethod.sticky) {
//如果訂閱事件是粘性事件,則:
if (eventInheritance) {
//事件允許發(fā)布到其父類或者父接口,則:
// 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) {
//遍歷粘性事件map
Class<?> candidateEventType = entry.getKey();
if (eventType.isAssignableFrom(candidateEventType)) {
//事件是粘性事件的父類、父接口,則:
Object stickyEvent = entry.getValue();
//發(fā)布粘性事件
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
//發(fā)布粘性事件
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}


  • subscribe訂閱事件主要是將訂閱者與要訂閱的事件存入事件/訂閱者map中,等待發(fā)布消息時從map中找到訂閱者并調(diào)用對應(yīng)的訂閱方法消耗處理事件。
  • subscribe訂閱時有兩個map,一個是subscriptionsByEventType(事件/訂閱者map,key是事件,value是訂閱者列表),一個是typesBySubscriber(訂閱者/事件列表map,key是訂閱者,value是要訂閱的事件列表),subscriptionsByEventType用于發(fā)布消息時從map中快速找到訂閱者列表并調(diào)用相應(yīng)的訂閱方法,typesBySubscriber用于取消訂閱時,從map中快速找到事件列表,根據(jù)此事件列表快速從subscriptionsByEventType取消訂閱。
  • 訂閱消息時確實也是將訂閱者與定要訂閱的事件存入事件/訂閱者map中。

register方法中的subscriberMethodFinder.findSubscriberMethods(subscriberClass),它從訂閱者class對象中找到要所有要訂閱的事件,我們看看它是怎么實現(xiàn)的:

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//從緩存中獲取訂閱者的所有事件方法列表
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//緩存匯總沒有,則:
if (ignoreGeneratedIndex) {
//如果忽略從編譯器期自動生成訂閱者/事件關(guān)系文件中查找,則:
//通過反射訂閱者class找到訂閱事件方法列表
subscriberMethods = findUsingReflection(subscriberClass);
} else {
//如果從編譯器期自動生成訂閱者/事件關(guān)系文件中查找,則:
//從訂閱者/事件關(guān)系文件中查找訂閱事件方法列表
subscriberMethods = findUsingInfo(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
//將訂閱者的訂閱事件列表進(jìn)行緩存
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
  • findSubscriberMethods有兩個策略。
  • 一是通過反射訂閱者class對象找到所有的訂閱事件方法列表。
  • 一是從編譯期自動生成的訂閱者/事件關(guān)系文件中查找,默認(rèn)是從訂閱者/事件關(guān)系文件中查找。

(2)反射查找訂閱事件方法列表

private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
//從查找狀態(tài)對象復(fù)用池中獲取/創(chuàng)建一個新的查找狀態(tài)對象
FindState findState = prepareFindState();
//初始化查找狀態(tài)對象,設(shè)置訂閱者class
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
//通過反射查找查找狀態(tài)對象當(dāng)前class,最開始從訂閱者class開始查找,接著遞歸其父類或者父接口查找
findUsingReflectionInSingleClass(findState);
//繼續(xù)從其父類/父接口中查找
findState.moveToSuperclass();
}
//返回訂閱事件方法列表,并將查找狀態(tài)對象緩存到復(fù)用池中
return getMethodsAndRelease(findState);
}
  • findUsingReflection會從當(dāng)前訂閱者的class開始查找所有的訂閱事件方法列表;
  • 接著遞歸其父類或者父接口繼續(xù)查找,真正查找一個class的訂閱事件方法列表是findUsingReflectionInSingleClass方法:
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// This is faster than getMethods, especially when subscribers are fat classes like Activities
//反射獲取當(dāng)前class定義的方法列表
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
// Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
//出現(xiàn)異常,則獲取所有方法(包含所有父類/父接口)
methods = findState.clazz.getMethods();
//并標(biāo)記不需要從父類/父接口匯總查找
findState.skipSuperClasses = true;
}
for (Method method : methods) {
//遍歷方法列表
//獲取方法訪問性
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
//方法是public、靜態(tài)、抽象,則:
//反射獲取方法的參數(shù)類型列表
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
//方法參數(shù)只有一個參數(shù),則:
//反射獲取方法的Subscribe注解對象
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
//方法被Subscribe注解修飾,則:
//獲取參數(shù)類型class
Class<?> eventType = parameterTypes[0];
if (findState.checkAdd(method, eventType)) {
//獲取事件方法的線程模型
ThreadMode threadMode = subscribeAnnotation.threadMode();
//收集注解信息:線程模型、優(yōu)先級、粘性,創(chuàng)建事件方法,并加入訂閱者的訂閱事件方法列表中
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
//如果方法嚴(yán)格校驗并且被Subscribe注解修飾,則拋出方法參數(shù)多于1個的異常
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException("@Subscribe method " + methodName +
"must have exactly 1 parameter but has " + parameterTypes.length);
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
//如果方法嚴(yán)格校驗并且被Subscribe注解修飾,則拋出方法非public、非靜態(tài)、非抽象的異常
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException(methodName +
" is a illegal @Subscribe method: must be public, non-static, and non-abstract");
}
}
}

findUsingReflectionInSingleClass通過反射從class中獲取方法列表,并按Subscribe注解規(guī)則(規(guī)則是:方法被Subscribe修飾、是public/static/abstract、只有一個參數(shù))進(jìn)行過濾,找到匹配的事件方法,并加入訂閱事件方法列表

(3)編譯期注解處理器,從編譯期生成的訂閱者/事件關(guān)系文件中查找訂閱方法列表

  • 從編譯期生成的訂閱者/事件關(guān)系文件中查找訂閱方法列表,涉及編譯期注解處理器解析注解生成訂閱者/事件方法列表關(guān)系文件,然后運行時從這些文件中根據(jù)訂閱者class直接找到事件方法列表。
  • EventBus的編譯期注解處理器是獨立的模塊,依賴使用它可以在編譯期自動解析生成訂閱者/事件方法列表關(guān)系文件,避免運行時通過反射查找事件方法列表的性能開銷.

EventBusAnnotationProcessor 是EventBus的編譯期注解處理器實現(xiàn)類,其核心源碼如下:

//訂閱者/事件方法列表關(guān)系列表
private final ListMap<TypeElement, ExecutableElement> methodsByClass = new ListMap<>();
//被忽略的類
private final Set<TypeElement> classesToSkip = new HashSet<>();
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
Messager messager = processingEnv.getMessager();
try {
//讀取模塊定義的訂閱者/事件索引類名(含包名+類名)
String index = processingEnv.getOptions().get(OPTION_EVENT_BUS_INDEX);
if (index == null) {
messager.printMessage(Diagnostic.Kind.ERROR, "No option " + OPTION_EVENT_BUS_INDEX +
" passed to annotation processor");
return false;
}
verbose = Boolean.parseBoolean(processingEnv.getOptions().get(OPTION_VERBOSE));
int lastPeriod = index.lastIndexOf('.');
//訂閱者/事件索引類包名
String indexPackage = lastPeriod != -1 ? index.substring(0, lastPeriod) : null;
//......
//通過注解收集訂閱者/事件方法列表關(guān)系列表
collectSubscribers(annotations, env, messager);
//根據(jù)訂閱語法規(guī)則,生成忽略被忽略的觀察者列表
checkForSubscribersToSkip(messager, indexPackage);
if (!methodsByClass.isEmpty()) {
//生成訂閱者/事件索引類文件
createInfoIndexFile(index);
} else {
messager.printMessage(Diagnostic.Kind.WARNING, "No @Subscribe annotations found");
}
writerRoundDone = true;
} catch (RuntimeException e) {
// IntelliJ does not handle exceptions nicely, so log and print a message
e.printStackTrace();
messager.printMessage(Diagnostic.Kind.ERROR, "Unexpected error in EventBusAnnotationProcessor: " + e);
}
return true;
}
//通過注解收集訂閱者/事件方法列表關(guān)系列表
private void collectSubscribers(Set<? extends TypeElement> annotations, RoundEnvironment env, Messager messager) {
for (TypeElement annotation : annotations) {
//遍歷注解類型列表
//從注解類型元信息中獲取匹配的注解元信息列表
Set<? extends Element> elements = env.getElementsAnnotatedWith(annotation);
for (Element element : elements) {
//遍歷注解元信息列表
if (element instanceof ExecutableElement) {
//如果是方法元信息,則:
ExecutableElement method = (ExecutableElement) element;
if (checkHasNoErrors(method, messager)) {
//校驗方法元信息,如果方法是public且只有一個參數(shù),則:
//獲取方法所屬的類類型元信息,此類類型就是對應(yīng)的訂閱者,方法是消耗處理事件
TypeElement classElement = (TypeElement) method.getEnclosingElement();
//將訂閱者/事件方法加入訂閱者/事件方法列表map中,key是訂閱者,value是事件方法列表
methodsByClass.putElement(classElement, method);
}
} else {
messager.printMessage(Diagnostic.Kind.ERROR, "@Subscribe is only valid for methods", element);
}
}
}
}
private void checkForSubscribersToSkip(Messager messager, String myPackage) {
for (TypeElement skipCandidate : methodsByClass.keySet()) {
//遍歷訂閱者/事件方法列表map
//訂閱者class類類型元信息
TypeElement subscriberClass = skipCandidate;
while (subscriberClass != null) {
//循環(huán):訂閱者class不為空,則:
if (!isVisible(myPackage, subscriberClass)) {
//如果訂閱者class不是public或者與要生成的索引類不是同包,則忽略此訂閱則,將其加入忽略列表
boolean added = classesToSkip.add(skipCandidate);
//.......
break;
}
//取出訂閱者要訂閱的事件方法列表
List<ExecutableElement> methods = methodsByClass.get(subscriberClass);
if (methods != null) {
for (ExecutableElement method : methods) {
//遍歷訂閱的事件方法列表
String skipReason = null;
VariableElement param = method.getParameters().get(0);
TypeMirror typeMirror = getParamTypeMirror(param, messager);
if (!(typeMirror instanceof DeclaredType) ||
!(((DeclaredType) typeMirror).asElement() instanceof TypeElement)) {
//參數(shù)類型無法處理(不是基類性、類/接口類型、數(shù)組類型、類型變量、空類型),則標(biāo)間忽略原因,一般不會出現(xiàn)此情況
skipReason = "event type cannot be processed";
}
if (skipReason == null) {
TypeElement eventTypeElement = (TypeElement) ((DeclaredType) typeMirror).asElement();
if (!isVisible(myPackage, eventTypeElement)) {
//如果參數(shù)類型對應(yīng)索引類不可訪問:如不是public或者不在同包名等,則標(biāo)間忽略原因
skipReason = "event type is not public";
}
}
if (skipReason != null) {
//如果被標(biāo)記被忽略,則:
//加入忽略列表
boolean added = classesToSkip.add(skipCandidate);
//.......
break;
}
}
}
//獲取訂閱類的父類/父接口,繼續(xù)遞歸過濾
subscriberClass = getSuperclass(subscriberClass);
}
}
}
//生成訂閱者/事件方法列表索引類文件
private void createInfoIndexFile(String index) {
BufferedWriter writer = null;
try {
//生成訂閱者/事件方法列表索引類文件,文件為索引類全限定名
JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(index);
int period = index.lastIndexOf('.');
//索引類包名
String myPackage = period > 0 ? index.substring(0, period) : null;
//索引類名
String clazz = index.substring(period + 1);
//創(chuàng)建文件讀寫器
writer = new BufferedWriter(sourceFile.openWriter());
if (myPackage != null) {
//文件寫入類包名
writer.write("package " + myPackage + ";\n\n");
}
//文件寫入引用的類
writer.write("import org.greenrobot.eventbus.meta.SimpleSubscriberInfo;\n");
writer.write("import org.greenrobot.eventbus.meta.SubscriberMethodInfo;\n");
writer.write("import org.greenrobot.eventbus.meta.SubscriberInfo;\n");
writer.write("import org.greenrobot.eventbus.meta.SubscriberInfoIndex;\n\n");
writer.write("import org.greenrobot.eventbus.ThreadMode;\n\n");
writer.write("import java.util.HashMap;\n");
writer.write("import java.util.Map;\n\n");
writer.write("/** This class is generated by EventBus, do not edit. */\n");
//文件寫入定義類并實現(xiàn)SubscriberInfoIndex 接口
writer.write("public class " + clazz + " implements SubscriberInfoIndex {\n");
//文件寫入聲明訂閱者/事件方法列表map,key是訂閱者class,value是SubscriberInfo訂閱信息對象
writer.write(" private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;\n\n");
//文件寫入靜態(tài)塊
writer.write(" static {\n");
//文件寫入創(chuàng)建訂閱者/事件方法列表map
writer.write(" SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();\n\n");
//調(diào)用writeIndexLines完成將訂閱者/事件方法列表寫入map中
writeIndexLines(writer, myPackage);
writer.write(" }\n\n");
//文件寫入putIndex方法,方法用于將訂閱者要訂閱的信息存入map中
writer.write(" private static void putIndex(SubscriberInfo info) {\n");
writer.write(" SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);\n");
writer.write(" }\n\n");
//文件寫入實現(xiàn)SubscriberInfoIndex 接口的getSubscriberInfo方法,方法是根據(jù)訂閱者class從map中取訂閱者信息并返回
writer.write(" @Override\n");
writer.write(" public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {\n");
writer.write(" SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);\n");
writer.write(" if (info != null) {\n");
writer.write(" return info;\n");
writer.write(" } else {\n");
writer.write(" return null;\n");
writer.write(" }\n");
writer.write(" }\n");
writer.write("}\n");
} catch (IOException e) {
throw new RuntimeException("Could not write source for " + index, e);
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
//Silent
}
}
}
}
//完成將訂閱者/事件方法列表寫入map中
private void writeIndexLines(BufferedWriter writer, String myPackage) throws IOException {
for (TypeElement subscriberTypeElement : methodsByClass.keySet()) {
//遍歷收集到的訂閱者/事件方法列表
if (classesToSkip.contains(subscriberTypeElement)) {
//訂閱者被忽略
continue;
}
//獲取訂閱者class
String subscriberClass = getClassString(subscriberTypeElement, myPackage);
if (isVisible(myPackage, subscriberTypeElement)) {
//調(diào)用索引類的putIndex方法,創(chuàng)建訂閱信息類SimpleSubscriberInfo,作為參數(shù),其作用是寫入如下語句:
putIndex(new SimpleSubscriberInfo(subscriberClass, true, SubscriberMethodInfo[]))
writeLine(writer, 2,
"putIndex(new SimpleSubscriberInfo(" + subscriberClass + ".class,",
"true,", "new SubscriberMethodInfo[] {");
List<ExecutableElement> methods = methodsByClass.get(subscriberTypeElement);
writeCreateSubscriberMethods(writer, methods, "new SubscriberMethodInfo", myPackage);
writer.write(" }));\n\n");
} else {
writer.write(" // Subscriber not visible to index: " + subscriberClass + "\n");
}
}
}
//創(chuàng)建SubscriberMethodInfo對象并寫入索引文件
private void writeCreateSubscriberMethods(BufferedWriter writer, List<ExecutableElement> methods,
String callPrefix, String myPackage) throws IOException {
for (ExecutableElement method : methods) {
//遍歷事件方法列表
//獲取方法參數(shù)列表
List<? extends VariableElement> parameters = method.getParameters();
//獲取第一個參數(shù)類型對象
TypeMirror paramType = getParamTypeMirror(parameters.get(0), null);
//參數(shù)類型元對象
TypeElement paramElement = (TypeElement) processingEnv.getTypeUtils().asElement(paramType);
//獲取方法名稱
String methodName = method.getSimpleName().toString();
//獲取參數(shù)類型類型,即事件類型
String eventClass = getClassString(paramElement, myPackage) + ".class";
//獲取方法Subscribe注解對象
Subscribe subscribe = method.getAnnotation(Subscribe.class);
List<String> parts = new ArrayList<>();
//創(chuàng)建SubscriberMethodInfo
parts.add(callPrefix + "(\"" + methodName + "\",");
String lineEnd = "),";
if (subscribe.priority() == 0 && !subscribe.sticky()) {
//如果注解信息中優(yōu)先級為0并且非粘性事件,則
if (subscribe.threadMode() == ThreadMode.POSTING) {
//如果線程模式為POSTING,則:
//創(chuàng)建:new SubscriberMethodInfo(methodName, eventClass)
parts.add(eventClass + lineEnd);
} else {
//線程模式不是POSTING,則:
//創(chuàng)建:new SubscriberMethodInfo(methodName, eventClass, ThreadMode.xxx)
parts.add(eventClass + ",");
parts.add("ThreadMode." + subscribe.threadMode().name() + lineEnd);
}
} else {
//如果注解信息中優(yōu)先級不是0或者是粘性事件,則
//創(chuàng)建:new SubscriberMethodInfo(methodName, eventClass, ThreadMode.xxx, priority, sticky)
parts.add(eventClass + ",");
parts.add("ThreadMode." + subscribe.threadMode().name() + ",");
parts.add(subscribe.priority() + ",");
parts.add(subscribe.sticky() + lineEnd);
}
writeLine(writer, 3, parts.toArray(new String[parts.size()]));
if (verbose) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Indexed @Subscribe at " +
method.getEnclosingElement().getSimpleName() + "." + methodName +
"(" + paramElement.getSimpleName() + ")");
}
}
}

編譯器注解處理器其實做的是跟運行時反射獲取訂閱者class的訂閱事件方法列表有點類似,都是從訂閱者class收集subscribe注解修飾的訂閱事件方法列表。

編譯器注解處理器主要做:

  • 收集本模塊中被subscribe注解修飾的方法及其所屬的類,并生成訂閱者/事件方法列表map。
  • 一個模塊一個索引類,生成本模塊的索引類。
  • 將訂閱者/事件方法列表map寫入索引類中的訂閱者/訂閱信息map中,供運行時根據(jù)訂閱者class快速獲取訂閱信息SubscriberInfo。

(4)從編譯期生成的訂閱者/事件關(guān)系文件中查找訂閱方法列表

從編譯期生成的訂閱者/事件索引類文件中查找訂閱方法列表的入口是findUsingInfo:

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
//從查找狀態(tài)對象復(fù)用池中獲取/創(chuàng)建一個新的查找狀態(tài)對象
FindState findState = prepareFindState();
//初始化查找狀態(tài)對象,設(shè)置訂閱者class
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
//從索引類中查找訂閱者對應(yīng)的定義信息對象
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) {
//如果訂閱者訂閱的訂閱信息對象不為空,則:
//從訂閱信息對象中獲取訂閱事件方法數(shù)組
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for (SubscriberMethod subscriberMethod : array) {
//將數(shù)組轉(zhuǎn)為list
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {
//如果索引類中沒有匹配的,則通過運行時注解反射獲取訂閱事件方法列表
findUsingReflectionInSingleClass(findState);
}
//遞歸從父類/父接口繼續(xù)查找
findState.moveToSuperclass();
}
//返回訂閱事件方法列表
return getMethodsAndRelease(findState);
}

findUsingInfo方法先從索引類中找到訂閱者class對應(yīng)的訂閱事件方法列表,如果沒有則轉(zhuǎn)為運行時注解通過反射查找

從索引類中找到訂閱者class對應(yīng)的訂閱信息對象的源碼如下:

private SubscriberInfo getSubscriberInfo(FindState findState) {
if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
//訂閱信息對象不為空,且有父級訂閱信息對象,則:
SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
if (findState.clazz == superclassInfo.getSubscriberClass()) {
//如果訂閱者class與父級訂閱信息對象的class一樣,則返回父級訂閱信息對象
return superclassInfo;
}
}
if (subscriberInfoIndexes != null) {
for (SubscriberInfoIndex index : subscriberInfoIndexes) {
//遍歷所有的索引類,即遍歷所有模塊的索引類(一個模塊一個索引類)
//從索引類訂閱者/訂閱信息map中查找訂閱信息對象
SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
if (info != null) {
//找到則返回
return info;
}
}
}
return null;
}

  • 從編譯期生成的訂閱者/事件關(guān)系文件中查找訂閱方法列表性能上比運行時注解反射獲取事件方法列表更好。
  • 因為少了反射查找,編譯器每個模塊都會生成一個索引文件,該索引文件記錄的是訂閱者/訂閱信息map。
  • 查找事件方法列表時時間在索引文件進(jìn)行查找并轉(zhuǎn)換為事件方法列表。

2、EventBus發(fā)布消息

EventBus基于觀察者模式,所以其發(fā)布消息的流程大致為從觀察者列表中找到匹配的觀察者,并將消息傳遞給匹配的觀察者處理。

EventBus是不是這樣的處理邏輯呢,我們來看看其發(fā)布源碼,EventBus的發(fā)布消息入口是post方法:

public void post(Object event) {
//從ThreadLocal獲取當(dāng)前線程的PostingThreadState對象
PostingThreadState postingState = currentPostingThreadState.get();
//發(fā)布消息隊列
List<Object> eventQueue = postingState.eventQueue;
//將消息加入隊列中
eventQueue.add(event);
if (!postingState.isPosting) {
//如果發(fā)布隊列沒有運行,則:
//標(biāo)記發(fā)布狀態(tài)所處的線程是否是主線程
postingState.isMainThread = isMainThread();
//標(biāo)記正在發(fā)布消息
postingState.isPosting = true;
if (postingState.canceled) {
//已取消發(fā)布,則拋出異常
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
while (!eventQueue.isEmpty()) {
//遍歷消息隊列
//每次從隊列刪除首個消息,并進(jìn)行發(fā)布
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}

post主要做的是將消息加入隊列中,并遍歷隊列對每個消息進(jìn)行一一發(fā)布,我們繼續(xù)看下postSingleEvent:

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
//如果支持發(fā)布的消息的父級類型消息,則:
//從當(dāng)前消息class找出其所有的父類/父接口類型列表,含本身
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
//遍歷消息類型列表
Class<?> clazz = eventTypes.get(h);
//對每一種消息類型進(jìn)行發(fā)布消息
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
//如果不支持發(fā)布的消息的父級類型消息,則:
//直接發(fā)布當(dāng)前消息
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
//......
}

postSingleEvent中分為兩種情況,一種支持遍歷當(dāng)前消息的父級類型及本身作為消息,然后按照類型一一發(fā)布消息。

一種僅支持消息本身類型,則直接發(fā)布當(dāng)前消息,我們繼續(xù)往下看postSingleEventForEventType:

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
//根據(jù)消息類型class從消息/訂閱者列表map中找到訂閱此消息的訂閱者列表
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
//遍歷此消息的訂閱者列表
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
//調(diào)用postToSubscription將消息傳遞給訂閱者進(jìn)行處理
postToSubscription(subscription, event, postingState.isMainThread);
aborted = postingState.canceled;
} finally {
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
if (aborted) {
break;
}
}
return true;
}
return false;
}

postSingleEventForEventType中根據(jù)消息類型class從消息/訂閱者列表map中找到訂閱者列表,然后遍歷訂閱者列表,將消息傳遞給每個訂閱者進(jìn)行消息的處理。

我們繼續(xù)往下看postToSubscription:

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
//如果訂閱方法要求線程模式是POSTING,即在當(dāng)前線程進(jìn)行處理,則:
//直接調(diào)用invokeSubscriber方法回調(diào)訂閱者的訂閱方法處理消息
invokeSubscriber(subscription, event);
break;
case MAIN:
//如果訂閱方法要求線程模式是MAIN,即要求在主線程進(jìn)行處理,則:
if (isMainThread) {
//當(dāng)前已是主線程,則直接調(diào)用invokeSubscriber方法回調(diào)訂閱者的訂閱方法處理消息
invokeSubscriber(subscription, event);
} else {
//當(dāng)前不是主線程,則將使用handler轉(zhuǎn)入主線程中處理
mainThreadPoster.enqueue(subscription, event);
}
break;
case MAIN_ORDERED:
//如果訂閱方法要求線程模式是MAIN_ORDERED,即要求在主線程進(jìn)行串行處理,則:
if (mainThreadPoster != null) {
//如果設(shè)置了主線程handler,則使用handler轉(zhuǎn)入主線程中處理
mainThreadPoster.enqueue(subscription, event);
} else {
// temporary: technically not correct as poster not decoupled from subscriber
//如果沒有設(shè)置主線程handler,則直接在當(dāng)前線程處理
invokeSubscriber(subscription, event);
}
break;
case BACKGROUND:
//如果訂閱方法要求線程模式是BACKGROUND,即要求在子線程進(jìn)行處理,則:
if (isMainThread) {
//當(dāng)前是主線程,則將消息轉(zhuǎn)入子線程進(jìn)行處理
backgroundPoster.enqueue(subscription, event);
} else {
//當(dāng)前已子線程,則直接在當(dāng)前線程處理
invokeSubscriber(subscription, event);
}
break;
case ASYNC:
//如果訂閱方法要求線程模式是ASYNC,即要求在異步子線程進(jìn)行處理,則:
//將消息轉(zhuǎn)入子線程進(jìn)行處理
asyncPoster.enqueue(subscription, event);
break;
default:
//無效的線程模型,拋出異常
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}

postToSubscription中主要是對線程模型進(jìn)行處理:

  • POSTING:直接在當(dāng)前線程處理。
  • MAIN:如果當(dāng)前已是主線程,則直接處理消息;如果當(dāng)前不是主線程,則通過handler轉(zhuǎn)入主線程處理。
  • MAIN_ORDERED:主線程中串行處理,如果已經(jīng)設(shè)置主線程Handler,則將消息加入消息隊列等待loop進(jìn)行調(diào)度;如果沒有設(shè)置主線程Handler則直接在當(dāng)前線程處理。
  • BACKGROUND:在子線程中處理,如果當(dāng)前是主線程,則將消息轉(zhuǎn)入子線程中再進(jìn)行處理;如果當(dāng)前已是子線程,則直接在當(dāng)前線程處理。
  • *ASYNC:異步處理消息,不管當(dāng)前是主線程還是子線程,都需要加入新的子線程中再進(jìn)行處理。

直接處理消息的是通過invokeSubscriber方法,看名稱就是表示反射調(diào)用訂閱者的訂閱方法處理消息,那是不是呢?我們看看其源碼實現(xiàn):

void invokeSubscriber(Subscription subscription, Object event) {
try {
//通過反射調(diào)用訂閱者的訂閱方法處理消息
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
} catch (InvocationTargetException e) {
handleSubscriberException(subscription, event, e.getCause());
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unexpected exception", e);
}
}

就是通過反射調(diào)用訂閱者的訂閱方法,將消息傳遞給訂閱方法處理的;

(1)切換到主線程(MAIN)/主線程串行(MAIN_ORDERED)處理消息:

不管MAIN還是MAIN_ORDERED都是通過mainThreadPoster,將消息放入mainThreadPoster的隊列中,mainThreadPoster其實就是主線程中的Handler:

//使用mainThreadSupport創(chuàng)建主線程消息發(fā)布器
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
public interface MainThreadSupport {
boolean isMainThread();
Poster createPoster(EventBus eventBus);
class AndroidHandlerMainThreadSupport implements MainThreadSupport {
private final Looper looper;
public AndroidHandlerMainThreadSupport(Looper looper) {
this.looper = looper;
}
@Override
public boolean isMainThread() {
return looper == Looper.myLooper();
}
@Override
public Poster createPoster(EventBus eventBus) {
//創(chuàng)建HandlerPoster作為mainThreadPoster
return new HandlerPoster(eventBus, looper, 10);
}
}
}

mainThreadPoster默認(rèn)是通過MainThreadSupport.AndroidHandlerMainThreadSupport.createPoster創(chuàng)建的,也就是mainThreadPoster默認(rèn)是HandlerPoster的實例對象,我們看看HandlerPoster:

public class HandlerPoster extends Handler implements Poster {
private final PendingPostQueue queue;
private final int maxMillisInsideHandleMessage;
private final EventBus eventBus;
private boolean handlerActive;
protected HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
super(looper);
this.eventBus = eventBus;
this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
queue = new PendingPostQueue();
}
public void enqueue(Subscription subscription, Object event) {
//將消息進(jìn)行包裝
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
//將消息加入等待隊列中
queue.enqueue(pendingPost);
if (!handlerActive) {
handlerActive = true;
//發(fā)送一個空消息
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
}
}
}
@Override
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
while (true) {
//死循環(huán)
//從等待隊列中取出消息
PendingPost pendingPost = queue.poll();
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
handlerActive = false;
//沒有等待處理的消息,則退出循環(huán)
return;
}
}
}
//回調(diào)EventBus的invokeSubscriber處理消息
eventBus.invokeSubscriber(pendingPost);
long timeInMethod = SystemClock.uptimeMillis() - started;
if (timeInMethod >= maxMillisInsideHandleMessage) {
//沒超過10毫秒重新發(fā)送空消息重新調(diào)度
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
rescheduled = true;
return;
}
}
} finally {
handlerActive = rescheduled;
}
}
}

  • 切換到主線程主要使用的是主線程的Handler實現(xiàn)。
  • 每次將消息加入等待隊列,然后Handler發(fā)送一個空消息。
  • 在Handler的handleMessage中遍歷等待隊列消息出隊并進(jìn)行處理。

(2)切換到子線程(BACKGROUND)處理消息:

切換到子線程處理消息是通過backgroundPoster的enqueue方法,backgroundPoster是BackgroundPoster的實現(xiàn)類,我們看看其內(nèi)部實現(xiàn):

final class BackgroundPoster implements Runnable, Poster {
private final PendingPostQueue queue;
private final EventBus eventBus;
private volatile boolean executorRunning;
BackgroundPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
}
public void enqueue(Subscription subscription, Object event) {
//將消息進(jìn)行包裝
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
//將消息加入等待隊列中
queue.enqueue(pendingPost);
if (!executorRunning) {
executorRunning = true;
//線程加入使用線程池中,等待回調(diào)run方法
eventBus.getExecutorService().execute(this);
}
}
}
@Override
public void run() {
try {
try {
while (true) {
//死循環(huán)
//從等待隊列中獲取等待處理的消息,1000表示如果隊列為空等待1000毫秒
PendingPost pendingPost = queue.poll(1000);
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
executorRunning = false;
//如果消息為空,則退出循環(huán)
return;
}
}
}
//調(diào)用EventBus反射調(diào)用訂閱者的訂閱方法處理消息
eventBus.invokeSubscriber(pendingPost);
}
} catch (InterruptedException e) {
eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interruppted", e);
}
} finally {
executorRunning = false;
}
}
}

  • 切換到子線程處理消息主要是將消息加入等待隊列,backgroundPoster本身是一個線程實現(xiàn),將backgroundPoster本身加入線程池中運行。
  • backgroundPoster線程執(zhí)行時遍歷等待隊列,從隊列中取出等待處理的消息,通過eventBus.invokeSubscriber處理消息。

其中默認(rèn)使用的線程池是緩存線程池:

executorService = builder.executorService;
public class EventBusBuilder {
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}

(3)異步消息(ASYNC):

異步消息是通過asyncPoster的enqueue方法實現(xiàn),asyncPoster是AsyncPoster的實例對象,它本身就是一個線程,我們看看其實現(xiàn):

class AsyncPoster implements Runnable, Poster {
private final PendingPostQueue queue;
private final EventBus eventBus;
AsyncPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
}
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
//將消息加入隊列中
queue.enqueue(pendingPost);
//線程加入線程池中
eventBus.getExecutorService().execute(this);
}
@Override
public void run() {
//每次取出一個消息
PendingPost pendingPost = queue.poll();
if(pendingPost == null) {
throw new IllegalStateException("No pending post available");
}
//調(diào)用EventBus反射調(diào)用訂閱者的訂閱方法處理消息
eventBus.invokeSubscriber(pendingPost);
}
}

異步消息的實現(xiàn)主要是將消息加入等待隊列,然后將線程加入隊列,線程執(zhí)行是每次去等待隊列的一個消息(隊尾,先進(jìn)先出原則),最后調(diào)用EventBus反射調(diào)用訂閱者的訂閱方法處理消息。

異步消息使用的線程池跟BACKGROUND模式使用的線程池是同一個,都是緩存線程池,與BACKGROUND的區(qū)別是:

  • 異步消息每次都將線程加入線程池中。
  • 而BACKGROUND則不需要每次。
  • 異步消息線程執(zhí)行每次只去隊尾消息進(jìn)行處理,而BACKGROUND線程執(zhí)行時會處理隊列中的所有消息。

總結(jié):

  • EventBus基于觀察者模式,其首先需要先將訂閱者與消息類型進(jìn)行注冊到一個消息/訂閱者列表map中,發(fā)布消息時從map找找到對應(yīng)的訂閱者并調(diào)用相應(yīng)的訂閱方法進(jìn)行處理消息。
  • 注冊時通過訂閱者class通過反射收集subscribe注解修飾的方法列表及注解信息,最終注冊到消息/訂閱者列表map中,這個運行時注解是先對耗時的;所以EventBus提供了編譯期注解處理器來做個收集的工作,降低性能損耗。
  • 發(fā)布消息時根據(jù)消息類型class從消息/訂閱者列表map中找到對應(yīng)的訂閱者列表并進(jìn)行一一處理,同時根據(jù)注解中的線程模式進(jìn)行線程切換處理。

圖片


責(zé)任編輯:姜華 來源: Android開發(fā)編程
相關(guān)推薦

2021-04-08 11:00:56

CountDownLaJava進(jìn)階開發(fā)

2021-05-24 08:09:21

SentinelRedis 流控原理

2023-04-09 21:39:48

JavaScript開源

2021-07-12 06:11:14

SkyWalking 儀表板UI篇

2021-06-09 09:08:10

LDOlowdropoutr穩(wěn)壓器

2022-01-14 11:45:40

JVM 虛擬機Java

2022-01-17 11:28:55

JVM 虛擬機Java

2022-04-29 14:38:49

class文件結(jié)構(gòu)分析

2021-07-21 09:48:20

etcd-wal模塊解析數(shù)據(jù)庫

2021-03-12 09:21:31

MySQL數(shù)據(jù)庫邏輯架構(gòu)

2021-04-14 14:16:58

HttpHttp協(xié)議網(wǎng)絡(luò)協(xié)議

2024-06-13 08:34:48

2023-03-29 07:45:58

VS編輯區(qū)編程工具

2022-02-17 08:53:38

ElasticSea集群部署

2021-06-21 14:36:46

Vite 前端工程化工具

2022-03-22 09:09:17

HookReact前端

2021-04-01 10:51:55

MySQL鎖機制數(shù)據(jù)庫

2021-01-28 08:55:48

Elasticsear數(shù)據(jù)庫數(shù)據(jù)存儲

2021-07-08 07:30:13

Webpack 前端Tree shakin

2023-03-13 09:31:04

點贊
收藏

51CTO技術(shù)棧公眾號

v8888av| 日本手机在线视频| 97超视频在线观看| 欧美色图麻豆| 一区二区三区四区精品| 国产毛片久久久久久| 国模私拍一区二区国模曼安| 国产欧美日韩在线看| 99久久国产免费免费| 91在线视频在线观看| 成人羞羞视频播放网站| 亚洲黄色成人网| 91制片厂毛片| 蜜桃麻豆影像在线观看| 一色屋精品亚洲香蕉网站| 精品一区2区三区| 国产精品探花视频| 免费视频久久| 国内精品久久影院| 欧美性x x x| 欧美欧美黄在线二区| 欧美成人伊人久久综合网| 天天色综合社区| 亚洲优女在线| 亚洲国产裸拍裸体视频在线观看乱了 | 日本不卡一区二区三区视频| www.黄色小说.com| 美女网站色91| 国产精品91一区| 国产一区二区99| 国内精品99| 久久九九免费视频| 国产探花视频在线播放| 日韩三级毛片| 亚洲国产精品资源| 色哟哟无码精品一区二区三区| 日韩三区四区| 欧美午夜不卡在线观看免费| 免费在线激情视频| 欧美aa在线| 香蕉乱码成人久久天堂爱免费| 永久免费在线看片视频| 久操视频在线观看| 国产精品不卡在线| 一区二区精品免费视频| 9色在线观看| 欧美国产一区二区在线观看| 日本视频一区二区在线观看| 欧美视频免费一区二区三区| 97精品国产97久久久久久久久久久久 | 日韩成人三级视频| 成年视频在线观看| 亚洲欧美另类图片小说| 青青草视频国产| 久久香蕉av| 亚洲一区二区三区四区中文字幕| 奇米777四色影视在线看| 在线看一级片| 亚洲国产精品一区二区久久| 国产精品第157页| 超碰在线网站| 欧美日韩午夜剧场| 亚洲 中文字幕 日韩 无码| 视频二区不卡| 欧美日韩国产大片| 亚洲五月激情网| 1313精品午夜理伦电影| 亚洲成av人片在线观看香蕉| 青青草视频播放| 国产一区二区三区网| 一区二区三区www| 三级黄色录像视频| 欧美色图麻豆| 欧美中文字幕视频| 最新中文字幕第一页| 久久国产视频网| 亚洲综合国产精品| 色综合久久久久久| 国产色爱av资源综合区| 制服国产精品| 成人影音在线| 欧美亚洲综合另类| 久久久久久久久久毛片| 欧美电影免费网站| 伊人亚洲福利一区二区三区| 亚洲国产精品免费在线观看| 亚洲精品九九| 国产精品亚洲网站| www.97超碰| 久久蜜桃av一区精品变态类天堂| 日韩中文一区| 欧美另类tv| 在线一区二区视频| 国产91在线免费观看| 亚洲va久久久噜噜噜久久| 久久精品成人欧美大片古装| 久久草视频在线| 蜜臀av一区二区| 加勒比在线一区二区三区观看| 风间由美一区| 午夜精品影院在线观看| 污污的网站免费| 青草久久视频| 欧美日本国产在线| 性色av一区二区三区四区| 国产99久久精品| 亚洲精品一区二区三区樱花| 精精国产xxxx视频在线野外| 欧美日韩不卡一区| 亚洲黄色免费在线观看| 午夜精品一区二区三区国产| 日韩美女在线观看| 蜜臀久久久久久999| 国产精品福利一区二区三区| 日韩黄色片视频| 欧美黄视频在线观看| 最近2019年日本中文免费字幕 | 亚洲欧美日韩一区在线观看| 91免费高清视频| 高清在线观看av| 午夜久久久久久电影| 欧美性猛交乱大交| 欧美丰满老妇| 国产精品美女av| 人人九九精品| 婷婷国产v国产偷v亚洲高清| 中文字幕人妻熟女人妻a片| 成人三级视频| 国产精品99久久久久久白浆小说 | 久久亚洲精品中文字幕冲田杏梨| 在线免费观看av网址| 99久久夜色精品国产网站| 国产精品久久久久久久久电影网| 精品一区视频| 久久精品国产精品| 91国产免费视频| 日本一区二区在线不卡| 黄色片久久久久| 要久久电视剧全集免费| 热99精品里视频精品| 天堂中文资源在线| 偷拍亚洲欧洲综合| 日韩片在线观看| 国产欧美三级| 蜜桃av噜噜一区二区三| av日韩电影| 亚洲人午夜精品免费| 欧美bbbbbbbbbbbb精品| 91丨九色porny丨蝌蚪| aa在线免费观看| 西野翔中文久久精品字幕| 97精品国产91久久久久久| 男人天堂手机在线观看| 欧美日韩一区二区三区在线免费观看 | bt天堂新版中文在线地址| 一区二区三区视频播放| 久久久久这里只有精品| 午夜小视频免费| 一本久久精品一区二区| 青娱乐国产视频| 九九视频精品免费| 日韩一二区视频| 风间由美一区二区av101| 2019中文字幕在线观看| 国产在线高清| 欧美日韩国产a| 久久久久久久久久久久久女过产乱| 国产精品资源站在线| 国产精品一色哟哟| 欧美亚洲大陆| 国产精品久久久久秋霞鲁丝| 毛片在线看片| 亚洲第一视频网| 精品人妻一区二区色欲产成人| 国产日韩高清在线| 99日在线视频| 国产精品久久777777毛茸茸| 日本一区二区三区免费观看| 高清一区二区中文字幕| 97精品国产aⅴ7777| 中文字幕日本在线| 日韩精品一区二区三区四区 | 三级久久三级久久| 亚洲免费视频播放| 日韩欧美四区| 国产欧美精品久久久| 波多野结衣在线播放| 亚洲品质视频自拍网| 国产乱人乱偷精品视频a人人澡| 亚洲国产精品自拍| 99自拍偷拍视频| 成人成人成人在线视频| 日本人视频jizz页码69| 日韩午夜一区| av不卡在线免费观看| 日韩av网站在线免费观看| 国产日韩欧美综合| 92国产精品| 欧美激情乱人伦| 91社区在线观看| 日韩精品在线观看一区| 国产精品久久影视| 在线观看视频一区二区欧美日韩| 欧美人妻精品一区二区免费看| 久久九九久精品国产免费直播| 色诱av手机版| 精品亚洲免费视频| 国产成人综合一区| 夜夜嗨一区二区三区| 992tv快乐视频| 成人同人动漫免费观看| 蜜桃导航-精品导航| 6080亚洲理论片在线观看| 国产女人精品视频| 亚洲成av在线| 欧美在线中文字幕| 美女扒开腿让男人桶爽久久软| 久久成人av网站| 在线观看免费黄色| 精品亚洲一区二区三区在线观看| 亚洲女人18毛片水真多| 在线不卡一区二区| 一级黄色录像大片| 欧美在线制服丝袜| 潘金莲一级淫片aaaaaa播放| 午夜国产精品一区| 久久久久久久九九九九| 亚洲啪啪综合av一区二区三区| 欧美另类69xxxx| 国产欧美日韩精品一区| 亚洲av无码一区二区三区人 | 麻豆成人av在线| 久久久久国产精品熟女影院| 香蕉成人久久| 啊啊啊一区二区| 欧美亚洲专区| 国产91在线视频观看| 日韩一级在线| 免费毛片网站在线观看| 亚洲激情二区| 777精品久无码人妻蜜桃| 影音先锋中文字幕一区| 免费无码毛片一区二三区| 欧美三区视频| www.av片| 亚洲一区二区毛片| 黄色片一级视频| 日韩激情一区二区| 九色91popny| 九九久久精品视频| 亚洲色图欧美自拍| 高清国产一区二区三区| 国产免费a级片| av在线播放成人| 国产美女精品久久| 亚洲国产精品传媒在线观看| 一级黄色录像毛片| 综合久久给合久久狠狠狠97色 | 一本一道久久a久久精品综合蜜臀| 91在线视频在线观看| 欧美午夜片在线看| 国产巨乳在线观看| 精品国产一区二区三区久久影院| 全国男人的天堂网| 亚洲欧洲日产国码av系列天堂| 韩国精品视频| 久久精品在线播放| 精灵使的剑舞无删减版在线观看| 91国产高清在线| 天天综合网站| 96精品久久久久中文字幕| 乱中年女人伦av一区二区| 日韩欧美精品一区二区| 91成人影院| 欧美丰满熟妇bbbbbb百度| 美腿丝袜亚洲综合| av漫画在线观看| 国产网站一区二区三区| 希岛爱理中文字幕| 精品人伦一区二区三区蜜桃免费| 国产精品第六页| 精品美女一区二区| 国产在线你懂得| 欧美精品免费播放| 亚洲天堂导航| 亚洲自拍偷拍色片视频| 综合伊思人在钱三区| 福利网在线观看| 久久久久久婷| 无码国产精品一区二区免费式直播| 久久久综合精品| 免费人成视频在线| 欧美午夜精品久久久久久孕妇| 国产草草影院ccyycom| 国产午夜精品久久久| av毛片在线免费看| 国产精品精品视频| 大陆精大陆国产国语精品| 亚洲狠狠婷婷综合久久久| 在线看片欧美| 91视频福利网| 国产精品无圣光一区二区| 国产一级一片免费播放| 欧美日韩中文字幕一区二区| 欧美 日韩 人妻 高清 中文| 色777狠狠综合秋免鲁丝| 亚洲少妇视频| 国产精品久久久久久久小唯西川 | 97视频在线免费播放| 国产一区二区伦理片| 摸摸摸bbb毛毛毛片| 欧美性感美女h网站在线观看免费| 国产女人高潮时对白| 在线日韩欧美视频| 竹内纱里奈兽皇系列在线观看| 91久色国产| 91麻豆精品国产91久久久平台| 六月丁香婷婷在线| a美女胸又www黄视频久久| 欧美日韩综合一区二区| 欧美日韩国产另类一区| 国产女人在线观看| 日本aⅴ大伊香蕉精品视频| 国产精品三p一区二区| 美女av免费观看| 国产呦精品一区二区三区网站| 欧美 日韩 国产 成人 在线观看| 欧美日韩国内自拍| 五月婷婷丁香网| 992tv成人免费影院| 狠狠一区二区三区| 中国丰满熟妇xxxx性| 国产精品资源站在线| 午夜69成人做爰视频| 日韩欧美精品三级| 亚洲夜夜综合| 91精品久久久久久蜜桃| 久久久久亚洲| av在线免费观看不卡| 一区二区三区加勒比av| 亚洲高清精品视频| 久久99国产综合精品女同| 亚洲天堂中文字幕在线观看| 日本免费黄色小视频| 国产高清不卡一区二区| 欧美第一页在线观看| 欧美一区二区黄| 懂色av一区| 久久伊人一区| 日韩和的一区二区| 亚洲熟女毛茸茸| 欧美一级一级性生活免费录像| 婷婷丁香在线| 黄色小网站91| 噜噜噜91成人网| 免费成人深夜天涯网站| 欧美日韩免费一区二区三区 | 激情久久av一区av二区av三区| 天天摸天天碰天天爽天天弄| 欧美在线视频一区二区| 精品视频亚洲| 拔插拔插华人永久免费| 亚洲综合色噜噜狠狠| 青青久草在线| 国产精品香蕉在线观看| 牛夜精品久久久久久久99黑人| 波多野结衣加勒比| 一本到不卡免费一区二区| 99视频在线观看地址| 91精品国产91久久久久青草| 第84页国产精品| 久久精品国产一区二区三区日韩| 男女av一区三区二区色多| 久久久久亚洲av无码a片| 欧美精品久久一区二区三区| 男人添女人下部高潮视频在线观看| 久久国产精品99久久久久久丝袜| 视频一区二区三区在线| sm捆绑调教视频| 亚洲国产免费av| 国产成人77亚洲精品www| 中国老女人av| 99精品视频一区二区三区| 嫩草影院一区二区三区| 欧美成在线视频| 亚洲春色h网| 国产免费中文字幕| 精品久久久久久久久久ntr影视| 成人免费在线电影| aa成人免费视频| 玖玖玖国产精品| 波多野结衣亚洲色图| 亚洲欧美日韩图片| 试看120秒一区二区三区| www.四虎成人| 亚洲一区精品在线| 天堂地址在线www| 欧美乱偷一区二区三区在线| 国产一区不卡精品| 亚洲综合久久网|