Avant d'analyser le bus d'événements de goyave, regardons comment s'écrit le modèle d'observateur traditionnel :
L'interface sujet est un sujet abstrait, équivalent à l'observateur, qui contient un auditeur La liste des observateurs, la méthode attach enregistre l'auditeur dans cette liste, la méthode detach annule l'auditeur et la méthode notify est utilisée pour avertir les auditeurs de la liste lorsqu'un événement se produit
est généralement appelé dans la méthode d'implémentation notify La méthode de mise à jour de l'auditeur.
Observer est un observateur abstrait avec une méthode update La méthode update est appelée par la méthode notify du sujet spécifique.
Il s'agit d'une méthode de programmation traditionnelle pour les interfaces. La différence est que eventbus utilise une "interface implicite", une méthode de programmation basée sur l'annotation java
La différence est la suivante : la correspondance de cette "interface implicite" est générée lorsque le programme est en cours d'exécution, et basée sur la vraie. la signification de la relation correspondante entre l'interface et l'implémentation est établie au moment de la compilation, en revanche, "l'interface implicite" est plus flexible
Analysons comment l'interface implicite et l'implémentation établissent la liaison Pour déterminer la relation, regardez le code :
1 ##SubscriberRegistry类的register方法 2 void register(Object listener) { 3 Multimap<Class<?>, Subscriber> listenerMethods = findAllSubscribers(listener); 4 5 for (Map.Entry<Class<?>, Collection<Subscriber>> entry : listenerMethods.asMap().entrySet()) { 6 Class<?> eventType = entry.getKey(); 7 Collection<Subscriber> eventMethodsInListener = entry.getValue(); 8 9 CopyOnWriteArraySet<Subscriber> eventSubscribers = subscribers.get(eventType);10 11 if (eventSubscribers == null) {12 CopyOnWriteArraySet<Subscriber> newSet = new CopyOnWriteArraySet<Subscriber>();13 eventSubscribers = MoreObjects.firstNonNull(14 subscribers.putIfAbsent(eventType, newSet), newSet);15 }16 17 eventSubscribers.addAll(eventMethodsInListener);18 }19 }
La chose la plus utile dans cette méthode est la ligne 3. Une analyse grossière du reste du code est pour faciliter ce Mutimap, qui combine des événements de le même type L'écouteur Subsriber est ajouté à l'ensemble correspondant. Si l'ensemble Subsriber du type d'événement actuel est vide, ajoutez d'abord un ensemble vide
Suivez la méthode de la ligne 3 ci-dessus :
.1 /** 2 * Returns all subscribers for the given listener grouped by the type of event they subscribe to. 3 */ 4 private Multimap<Class<?>, Subscriber> findAllSubscribers(Object listener) { 5 Multimap<Class<?>, Subscriber> methodsInListener = HashMultimap.create(); 6 Class<?> clazz = listener.getClass(); 7 for (Method method : getAnnotatedMethods(clazz)) { 8 Class<?>[] parameterTypes = method.getParameterTypes(); 9 Class<?> eventType = parameterTypes[0];10 methodsInListener.put(eventType, Subscriber.create(bus, listener, method));11 }12 return methodsInListener;13 }
La méthode principale de cette méthode est la ligne 7, qui consiste à obtenir toutes les méthodes avec l'annotation Subscribe d'une classe spécifique. Le reste du code consiste à obtenir ces méthodes et à les insérer. une carte à valeurs multiples, puis Return
Suivez la méthode de la ligne 7 ci-dessus :
1 private static ImmutableList<Method> getAnnotatedMethods(Class<?> clazz) {2 return subscriberMethodsCache.getUnchecked(clazz);3 }
Après avoir suivi cela, Eclipse n'a aucun moyen de suivre. . On soupçonne qu'il est appelé par une classe anonyme interne. Une certaine méthode est associée (eclipse affiche également des liens d'appel incomplets vers des classes anonymes internes)
Jetons un coup d'œil à cela
subscriberMethodsCache
1 private static final LoadingCache<Class<?>, ImmutableList<Method>> subscriberMethodsCache =2 CacheBuilder.newBuilder()3 .weakKeys()4 .build(new CacheLoader<Class<?>, ImmutableList<Method>>() {5 @Override6 public ImmutableList<Method> load(Class<?> concreteClass) throws Exception {7 return getAnnotatedMethodsNotCached(concreteClass);8 }9 });
La méthode de chargement est Lors de l'appel, la méthode getAnnotatedMethodsNOtCached de la classe actuelle est appelée, suivie de cette méthode :
1 private static ImmutableList<Method> getAnnotatedMethodsNotCached(Class<?> clazz) { 2 Set<? extends Class<?>> supertypes = TypeToken.of(clazz).getTypes().rawTypes(); 3 Map<MethodIdentifier, Method> identifiers = Maps.newHashMap(); 4 for (Class<?> supertype : supertypes) { 5 for (Method method : supertype.getDeclaredMethods()) { 6 if (method.isAnnotationPresent(Subscribe.class) && !method.isSynthetic()) { 7 // TODO(cgdecker): Should check for a generic parameter type and error out 8 Class<?>[] parameterTypes = method.getParameterTypes(); 9 checkArgument(parameterTypes.length == 1,10 "Method %s has @Subscribe annotation but has %s parameters."11 + "Subscriber methods must have exactly 1 parameter.",12 method, parameterTypes.length);13 14 MethodIdentifier ident = new MethodIdentifier(method);15 if (!identifiers.containsKey(ident)) {16 identifiers.put(ident, method);17 }18 }19 }20 }21 return ImmutableList.copyOf(identifiers.values());22 }
La seconde line signifie obtenir la classe actuelle elle-même + la classe parent de la classe actuelle. Toutes les classes de l'interface sont placées dans un Set
La ligne 4 parcourt cet Set
La ligne 5 parcourt toutes les méthodes de chaque classe
La ligne 6 appelle la méthode La méthode isAnnotationPresent détermine que la méthode cible a l'annotation @Subscribe et que la méthode ne peut pas être une "méthode composite"
À la ligne 16, la méthode conditionnelle composée est placé dans la carte, et renvoyé à la ligne 21 !
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!