EventBus Cette page décrit comment utiliser EventBus3, comment créer une instance EventBus avec une portée autre que EventBus.getDefault (), qui n'est pas souvent mentionnée, et ThreadMode, qui est pratique mais nécessite une attention particulière. Je pense que vous devriez lire ce qui suit pour savoir pourquoi vous utilisez EventBus en premier lieu. Utilisation du bus d'événements sur Android
Je suis également arrivé à ce qui précède au début, mais quand j'ai lu le contenu pour l'utiliser, c'était la description d'EventBus2, et j'ai trouvé que l'utilisation est différente dans EventBus3, donc j'écrirai un exemple dans EventBus3. De plus, comme l'exemple affiché était un peu différent de l'objectif que je voulais utiliser, je décrirai également ce que je devrais faire pour mon objectif. ** Il semble qu'il y ait une idée fausse répandue selon laquelle EventBus ne peut être utilisé que dans la portée globale **, donc je mentionnerai également que ce n'est pas le cas. De plus, ** ThreadMode peut être spécifié pour contrôler le thread d'exécution, ce qui, à mon avis, est une fonction très pratique, mais j'ai trouvé qu'il fallait faire attention à son comportement **, j'ai donc lu le code source et l'ai découvert. Décrivez également les points.
Il est facile à utiliser comme suit. Cependant, même sur la page officielle et d'autres sites, il n'y a que des cas d'utilisation où d'autres classes notifient Activity, et je n'ai pas pu trouver de cas d'utilisation où Activity notifie d'autres classes, donc je l'écrirai dans ce cas.
Décrivez ce qui suit dans build.gradle.
build.gradle
compile 'org.greenrobot:eventbus:3.0.0'
Créer une classe pour l'événement
MessageEvent.java
public class MessageEvent {
private int eventType
public MessageEvent(int type) {
this.eventType = type;
}
public int getEventType() {
return eventType;
}
}
Classe que vous souhaitez être notifié
Subscriber.java
public class Subscriber {
@Subscribe
public void onEvent(MessageEvent event) {
// do something
}
}
Envoyer une notification MainActivity
MainActivity.java
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener {
private Subscriber mSubscriber;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSubscriber = new Subscriber();
}
@Override
protected void onStart() {
super.onStart();
EventBus.getDefault().register(mSubscriber);
}
@Override
protected void onStop() {
super.onStop();
EventBus.getDefault().unregister(mSubscriber);
}
@SuppressWarnings("StatementWithEmptyBody")
@Override
public boolean onNavigationItemSelected(MenuItem item) {
// Send event
EventBus.getDefault().post(new MessageEvent(item.getItemId()));
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
}
Le but est d'enregistrer l'instance à laquelle vous souhaitez être notifié en utilisant ʻEventBus.getDefault (). Register () . EventBus contient en interne un ensemble d'instances enregistrées, et lorsque ʻEventBus.getDefault (). Post ()
est appelé, chacun des ensembles d'instances devient un argument au moment de la publication. Nous vérifions s'il existe une méthode qui @ Subscribe
est le type de la classe, et s'il existe une méthode correspondante, nous vous en informerons en appelant cette méthode.
Dans cet exemple
EventBus.getDefault().post(new MessageEvent(item.getItemId()));
Est appelée, donc la méthode qui prend MessageEvent comme argument est appelée à partir des méthodes pour lesquelles @ Subscribe
est défini dans l'instance enregistrée dans EventBus (uniquement mSubscriber cette fois).
Les pages et articles officiels que j'ai vus ne montrent que des exemples utilisant l'instance EventBus par défaut en utilisant ʻEventBus.getDefault () . ʻSi vous utilisez uniquement EventBus.getDefault ()
, toutes les instances enregistrées avec register ()
seront la portée de la destination de publication. C'est facile, mais cela va à l'encontre de la règle empirique selon laquelle la portée d'un objet doit être aussi étroite que possible, et à mesure que le nombre de classes augmente, il devient difficile de savoir quelle méthode est réellement appelée.
Moins connu, en fait, ʻEventBus.getDefault () est juste une méthode pratique qui renvoie une instance singleton d'EventBus, et l'utilisateur crée une instance EventBus différente en exécutant
new EventBus () `. C'est également possible. Puisque les instances qui sont «register ()» ne sont pas partagées entre les instances EventBus, la portée doit être définie pour chaque instance EventBus par «register ()» uniquement des instances spécifiques dans l'instance EventBus générée. Peut être fait.
Un exemple de création d'une instance EventBus pour ViewModel1 et d'une instance EventBus pour ViewModel2 est illustré ci-dessous.
DifferentScopeActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mEventBusForViewModel1 = new EventBus();
mEventBusForViewModel2 = new EventBus();
mViewModel1 = new ViewModel1();
mViewModel2 = new ViewModel2();
}
@Override
protected void onStart() {
super.onStart();
mEventBusForViewModel1.register(mViewModel1);
mEventBusForViewModel2.register(mViewModel2);
}
@Override
protected void onStop() {
super.onStop();
mEventBusForViewModel1.unregister(mViewModel1);
mEventBusForViewModel2.unregister(mViewModel2);
}
Même dans l'annonce qui était très populaire à DroidKaigi 2017 ci-dessous
Architecture MVVM réalisée par DataBinding
EventBus est mondial, il est donc difficile de dire d'où viennent les notifications
Je pense que le présentateur est probablement conscient qu'il n'y a qu'un moyen de l'utiliser en utilisant ʻEventBus.getDefault () `. Je pense qu'il n'y a aucun moyen connu de créer un nouveau bus d'événements. (Je tiens à souligner que la critique de l'annonce ci-dessus n'est pas claire. Je l'ai donnée comme exemple d'un malentendu courant.)
ThreadMode
En spécifiant ThreadMode dans @ Subscribe
, vous pouvez définir le thread à exécuter.
POSTING
Si @Subscribe (threadMode = ThreadMode.POSTING)
est spécifié, il fonctionnera en interrompant le thread dans le même thread que le thread qui a effectué ** post. Par conséquent, aucune publication n'est renvoyée tant que la méthode @ Subscribe
n'est pas terminée. ** **
Veuillez noter que si vous publiez depuis MainThread et que le côté @ Subscribe
effectue un traitement intensif, ce sera l'ANR.
Dans le cas de (new Handler ()). Post, l'action de publication aurait dû se terminer immédiatement, ce qui est différent.
MAIN
Si vous spécifiez @Subscribe (threadMode = ThreadMode.MAIN)
, il sera toujours exécuté dans le thread principal du processus. ** Si le côté post s'exécute également sur MainThread, il s'exécutera en interrompant le thread comme si POSTING était spécifié. Par conséquent, la publication ne se termine pas tant que la méthode «@ Subscribe» n'est pas terminée. ** **
BACKGROUND
Si @Subscribe (threadMode = ThreadMode.BACKGROUND)
est spécifié, le traitement sera exécuté par des threads autres que MainThread. ** Si le côté post n'est pas MainThread, il fonctionne en interrompant le thread comme si POSTING était spécifié. Par conséquent, aucune publication n'est renvoyée tant que la méthode @ Subscribe
n'est pas terminée. ** **
Si la post-exécution est MainThread,
ExecutorService service = Executors.newCachedThreadPool();
service.execute(runnable);
Le traitement est presque le même que celui de. Cela garantit que chaque processus est exécuté en parallèle (à moins qu'un très grand nombre de messages ne soit exécuté), il n'y a donc pas de bloc entre les méthodes qui sont «@ Subscribe».
Si le côté post est MainThread, ** le traitement côté @ Subscribe
n'est pas sérialisé (séquentialisé) **, donc s'il y a des ressources à protéger dans la méthode @ Subscribe
Doit être correctement protégé.
Pour donner un exemple
Subscriber.java
int resource = 0;
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void handleEvent() {
resource++;
}
Publisher.java
private void postManyEvents() {
for(int i=0; i<10000; i++) {
EventBus.getDefault().post();
}
}
Si vous publiez 10000 fois sous la forme de, il n'y a aucune garantie que la ressource de l'Abonné sera de 10000 (car l'opérateur ++ ne fonctionne pas de manière atomique). Pour résoudre ce problème, utilisez une classe atomique comme indiqué ci-dessous, ou protégez-la avec un bloc synchronisé ou similaire.
Subscriber.java
AtomicInteger resource = new AtomicInteger(0);
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void handleEvent() {
resource.incrementAndGet();
}
Publisher.java
private void postManyEvents() {
for(int i=0; i<10000; i++) {
EventBus.getDefault().post();
}
}
ASYNC
Si @Subscribe (threadMode = ThreadMode.ASYNC)
est spécifié, le traitement est toujours exécuté dans le thread d'arrière-plan quel que soit le thread publié.
Il s'agit du même comportement que la spécification BACKGROUND lorsque le côté post est le filetage principal. En d'autres termes, les mêmes précautions que lors de la spécification de BACKGROUND sont nécessaires.
Il est préférable de ne pas traiter autant que possible dans MainThread, mais il est difficile de les utiliser correctement car une incohérence ou une exception des données se produit à des endroits inattendus s'ils sont tous des threads séparés.
Je pense que l'utilisation de seulement deux, MainThread et WorkerThread, est le meilleur équilibre entre la sécurité et la réponse, donc fondamentalement, la politique de spécifier POSTING et de lancer le traitement à WorkerThread dans la méthode est la meilleure. Cela peut être sûr.
S'il y a un processus qui doit être exécuté dans MainThread du côté @ Subscribe
, il sera nécessaire de spécifier MAIN.
De plus, je pense qu'il vaut mieux spécifier ASYNC pour un traitement qui ne pose aucun problème même s'il fonctionne en parallèle.
Décrit EventBus3 pour libérer le code Android de l'enfer des rappels. Veuillez noter que ThreadMode est étonnamment peu intuitif.
Si vous spécifiez @ WorkerThread
ou quelque chose comme ça, il semble être plus facile à utiliser personnellement si vous travaillez en série avec un seul SubThread, alors faisons-le vous-même.
Recommended Posts