[JAVA] Comment utiliser EventBus3 et ThreadMode

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.

Comment utiliser

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).

Définition des instances EventBus avec différentes étendues

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».

Précautions à prendre pour spécifier le CONTEXTE

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.

Utilisation correcte du mode filetage

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.

finalement

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

Comment utiliser EventBus3 et ThreadMode
Comment utiliser StringBurrer et Arrays.toString.
Comment utiliser l'égalité et l'égalité (comment utiliser l'égalité)
Comment utiliser OrientJS et OrientDB ensemble
Comment configurer et utiliser kapt
Comment utiliser les méthodes substring et substr
Comment utiliser @Builder et @NoArgsConstructor ensemble
Comment utiliser Map
Comment utiliser rbenv
Comment utiliser with_option
Comment utiliser fields_for
Comment utiliser java.util.logging
Comment utiliser la carte
Comment utiliser collection_select
Comment utiliser Twitter4J
Comment utiliser active_hash! !!
Comment utiliser MapStruct
Comment utiliser TreeSet
[Comment utiliser l'étiquette]
Comment utiliser l'identité
Comment utiliser le hachage
Comment utiliser Dozer.mapper
Comment utiliser Gradle
Comment utiliser org.immutables
Comment utiliser java.util.stream.Collector
Comment utiliser VisualVM
Comment utiliser Map
[Ruby] Comment utiliser la méthode gsub et la sous-méthode
Comment utiliser le contrôle segmenté et les points à noter
Comment utiliser la portée et le traitement des passes (servist)
[Java] Comment utiliser la classe Calendar et la classe Date
Comment utiliser l'API Chain
[Java] Comment utiliser Map
Comment utiliser Queue avec priorité
[Rails] Comment utiliser enum
Comment utiliser java Facultatif
Comment utiliser JUnit (débutant)
Comment utiliser le retour Ruby
[Rails] Comment utiliser enum
Comment utiliser @Builder (Lombok)
Comment utiliser la classe Java
Comment utiliser Big Decimal
[Java] Comment utiliser removeAll ()
Comment utiliser String [] args
Comment utiliser la jonction de rails
Comment utiliser Java Map
Ruby: Comment utiliser les cookies
Comment utiliser Dependant :: Destroy
Comment utiliser Eclipse Debug_Shell
Comment utiliser Apache POI
[Rails] Comment utiliser la validation
Comment utiliser les variables Java
[Rails] Comment utiliser authenticate_user!