[JAVA] RxAndroid et RxSwing Scheduler

En référence à RxAndroid, j'ai créé un Scheduler compatible avec RxJava 2.x pour le thread d'interface utilisateur de Swing. ** Pas Swift **. Merci pour votre soutien.

doOnSubscribe

Le but de cet article

Est le but de cet article. Il semble que personne n'utilise RxSwing. De plus, aucune connaissance de Swing n'est requise.

RxSwing existe, mais

RxSwing existe en tant que l'un des projets ReactiveX. https://github.com/ReactiveX/RxSwing

Mais contrairement à RxAndroid et RxCocoa, Non inclus dans ReactiveX pour les plates-formes et les frameworks. http://reactivex.io/languages.html

De plus, il n'y a pas encore de support pour RxJava 1.x.

Il existe une demande d'extraction qui prend en charge RxJava 2.x, mais il semble qu'elle a été laissée sans surveillance. https://github.com/ReactiveX/RxSwing/pull/63

Le projet officiel RxSwing2 a également un cadre, mais cette Pull Request est également négligée. https://github.com/ReactiveX/RxSwing2/pull/1

Il y a aussi la question de savoir combien de temps Swing restera, Peut-être que RxJava 3 sortira avant la fin de Swing. (Je dis quelque chose d'approprié.) Je pense qu'il est difficile d'éteindre et de maintenir RxSwing2 compatible avec RxJava2 dans un cycle aussi précoce, donc Est-ce un endroit où la sortie et les critiques sont hésitantes?

En attendant, s'il y a un support minimal, ce serait le contrôle des threads pour exécuter l'interface utilisateur. * 1

Fil UI

Comme le fil d'Android UI Swing dispose également d'un fil de discussion (EDT) pour exécuter l'interface utilisateur. (L'événement ici est un événement lié à l'interface utilisateur.) Cependant, bien que le fil d'interface utilisateur soit le fil principal sur Android, * 2 L'EDT de Swing est un thread distinct du thread principal Java.

Dans la série Rx, le contrôle des threads est laissé à Scheduler. Pour que vous puissiez écrire du code avec presque la même API dans la série Rx La gestion des threads dans chaque plate-forme et langue est confinée dans le Scheduler. Looper et Handler pour Android, GCD pour iOS, Il semble que chacun soit utilisé. https://github.com/ReactiveX/RxAndroid/blob/2.x/rxandroid/src/main/java/io/reactivex/android/schedulers/AndroidSchedulers.java https://github.com/ReactiveX/RxSwift/blob/master/RxSwift/Schedulers/MainScheduler.swift

Heureusement, dans le cas de Swing, il n'est pas nécessaire de traiter directement avec Thread ou ʻExecutor` de Java. Les classes «Timer» et «SwingUtilities» sont utilisées. https://github.com/ReactiveX/RxSwing/blob/0.x/src/main/java/rx/schedulers/SwingScheduler.java

Scheduler dans RxJava

public abstract Worker createWorker();

Créer un Scheduler dans RxJava, c'est avant tout implémenter les méthodes suivantes.

public abstract Worker createWorker();

Ce «Woker» doit au moins implémenter les méthodes suivantes et «Jetable».

public abstract Disposable schedule(@NonNull Runnable run, long delay, @NonNull TimeUnit unit);
public interface Disposable {
    void dispose();
    boolean isDisposed();
}

Responsabilités du «travailleur»

Et à propos du Runnable passé à la méthode schedule ()

Il est de la responsabilité du «travailleur» de garantir.

RxAndroid Scheduler

Si vous souhaitez exécuter l'interface utilisateur avec RxJava Pour RxAndroid, utilisez ʻAndroidSchedulers.mainThread () . Ceci est une instance de HandlerScheduler` Celui créé en passant le gestionnaire du thread principal au constructeur est réutilisé.

new HandlerScheduler(new Handler(Looper.getMainLooper()));

AndroidSchedulers.java HandlerScheduler.java

En utilisant la fonction de Handler, le rappel est appelé à l'heure spécifiée et le rappel est supprimé. Extrait ci-dessous, mais si vous ne pouvez pas attendre, regardez le code et dites «Je vois».

RxSwing2 Travailleur

Comme mentionné ci-dessus, il y a deux PR dans le référentiel officiel qui prennent en charge RxJava 2.x, C'est fondamentalement la même chose, donc je ne regarderai que le Scheduler et Worker qui sont PR pour RxSwing2.

Il y a peu de différence avec celle de RxJava 1.x. Cependant, il y a une chose à craindre.

Ci-dessous un extrait de la classe RxSwing2 Worker. https://github.com/UeliKurmann/RxSwing2/blob/8012ae881aa58cbb829433554489f9b83e6411ea/src/main/java/rx/schedulers/SwingScheduler.java

		// Worker of RxSwing2 for RxJava2.x
		
		private final CompositeDisposable innerSubscription = new CompositeDisposable();

		@Override
		public Disposable schedule(final Runnable action, long delayTime, TimeUnit unit) {
			//réduction

			//Retourner insideSubscription tel quel
			return innerSubscription;
		}

		@Override
		public void dispose() {
			innerSubscription.dispose();
		}

		@Override
		public boolean isDisposed() {
			return innerSubscription.isDisposed();
		}

En d'autres termes, les deux processus suivants sont égaux.

Et cette utilisation n'est pas «Composite jetable» Je pense que vous devriez utiliser Disposables.empty (). L'utilisation de «CompositeDisposable» peut simplement être un vestige de l'ancien RxSwing.

RxSwing1 Travailleur

Pour ceux qui ont oublié RxJava 1.x, voici les classes correspondantes.

Version classe Méthode
RxJava1.x Subscription unsubscribe()
RxJava2.x Disposable dispose()

Ci-dessous, un extrait de la classe Worker de RxSwing1. https://github.com/ReactiveX/RxSwing/blob/281ddb9500bea4ce8b44fccde907963712647ab4/src/main/java/rx/schedulers/SwingScheduler.java

        // Worker of RxSwing for RxJava1.x

        private final CompositeSubscription innerSubscription = new CompositeSubscription();

        @Override
        public Subscription schedule(final Action0 action, long delayTime, TimeUnit unit) {
            //réduction
            			
            final BooleanSubscription s = BooleanSubscription.create();

            //réduction

            innerSubscription.add(s);

            // wrap for returning so it also removes it from the 'innerSubscription'
            return Subscriptions.create(new Action0() {

                @Override
                public void call() {
                    timer.stop();
                    s.unsubscribe();
                    innerSubscription.remove(s);
                }
            });
        }

        @Override
        public void unsubscribe() {
            innerSubscription.unsubscribe();
        }

        @Override
        public boolean isUnsubscribed() {
            return innerSubscription.isUnsubscribed();
        }

Appelons RxSwing pour RxJava1.x RxSwing1. Dans RxSwing1, les deux ʻunsubscribe () `sont différents comme décrit ci-dessus.

RxAndroid Travailleur

Par exemple, il est possible que RxJava2.x ait changé ce qui est exigé de Woker. Pour savoir ce qui est juste, jetez un œil à notre Jake God RxAndroid.

        // Worker of RxAndroid for RxJava2.x

        private volatile boolean disposed;

 @Override
        public Disposable schedule(Runnable run, long delay, TimeUnit unit) {
            
            //réduction

            ScheduledRunnable scheduled = new ScheduledRunnable(handler, run);

            Message message = Message.obtain(handler, scheduled);
            message.obj = this; // Used as token for batch disposal of this worker's runnables.

            handler.sendMessageDelayed(message, Math.max(0L, unit.toMillis(delay)));
            
            //réduction

            return scheduled;
        }

        @Override
        public void dispose() {
            disposed = true;
            handler.removeCallbacksAndMessages(this /* token */);
        }

        @Override
        public boolean isDisposed() {
            return disposed;
        }
    private static final class ScheduledRunnable implements Runnable, Disposable {
        private final Handler handler;
        private final Runnable delegate;

        private volatile boolean disposed;

        ScheduledRunnable(Handler handler, Runnable delegate) {
            this.handler = handler;
            this.delegate = delegate;
        }

        @Override
        public void run() {
            try {
                delegate.run();
            } catch (Throwable t) {
                RxJavaPlugins.onError(t);
            }
        }

        @Override
        public void dispose() {
            disposed = true;
            handler.removeCallbacks(this);
        }

        @Override
        public boolean isDisposed() {
            return disposed;
        }
    }

Dans RxAndroid, qui devrait prendre en charge RxJava 2.x comme ceci, Les deux opérations suivantes ne sont pas égales.

  • dispose () vers l'instance Worker
  • dispose () à l'instance Disposable renvoyée par schedule () de l'instance Worker

Donc, le fait que les deux «dispose ()» soient égaux est susceptible de poser problème. Cependant, je n'ai pas trouvé de modèle d'utilisation. Il est préférable de montrer le bien ou le mal quel que soit le nombre d'utilisateurs de RxAndroid et la croyance en Jake God. Je suis à court de pouvoir et d'intérêt ici.

Exemple d'implémentation de RxSwing Scheduler correspondant à RxJava 2.x

alors, "Dans un cas, vous pouvez appeler schedule () pour chaque tâche et` dispose () ʻindividuellement. " Je pense qu'il est de la responsabilité du «planificateur» de renvoyer un tel «travailleur».

Sur la base de ce qui précède, il s'agit d'un exemple d'implémentation de RxSwing Scheduler correspondant à RxJava 2.x. https://github.com/guignol/SwingBinding/blob/05913b51d9aa6daf8cb0aa3113c699f23879c77e/src/main/java/com/github/guignol/swing/rx/SwingScheduler.java

public class SwingScheduler extends Scheduler {

    private static final SwingScheduler INSTANCE = new SwingScheduler();

    public static SwingScheduler getInstance() {
        return INSTANCE;
    }

    private SwingScheduler() {
    }

    @Override
    public Disposable scheduleDirect(Runnable run) {
        //Pourquoi TODO Rx Android remplace cela
        //TODO Si vous le laissez tel quel, est-il possible qu'il soit éliminé par Disposable Task?
        return super.scheduleDirect(run);
    }

    @Override
    public Worker createWorker() {
        return new InnerSwingScheduler();
    }

    private static class InnerSwingScheduler extends Worker {

        private final CompositeDisposable composite = new CompositeDisposable();

        @Override
        public Disposable schedule(Runnable original, long delayTime, TimeUnit unit) {
            if (original == null) throw new NullPointerException("run == null");
            if (unit == null) throw new NullPointerException("unit == null");

            final long delay = Math.max(0, unit.toMillis(delayTime));
            assertThatTheDelayIsValidForTheSwingTimer(delay);

            final Disposable local;
            if (delay == 0) {
                local = Disposables.empty();
                if (SwingUtilities.isEventDispatchThread()) {
                    //Exécution immédiate
                    original.run();
                } else {
                    SwingUtilities.invokeLater(() -> {
                        if (composite.isDisposed() || local.isDisposed()) {
                            return;
                        }
                        original.run();
                        composite.remove(local);
                    });
                }
            } else {
                final Timer timer = new Timer((int) delay, null);
                local = Disposables.fromRunnable(timer::stop);
                timer.setRepeats(false);
                timer.addActionListener(e -> {
                    if (composite.isDisposed() || local.isDisposed()) {
                        return;
                    }
                    original.run();
                    composite.remove(local);
                });
                timer.start();
            }
            composite.add(local);

            //Le Swing Scheduler d'Ueli Kurmann retourne un jetable composite pour ouvrier et ne peut pas se débarrasser tâche par tâche
            //Android Scheduler fournit également une élimination basée sur les tâches, donc je pense que c'est nécessaire, mais je n'ai pas trouvé de modèle d'utilisation qui fasse une différence.
            return local;
        }

        @Override
        public void dispose() {
            composite.dispose();
        }

        @Override
        public boolean isDisposed() {
            return composite.isDisposed();
        }

        private static void assertThatTheDelayIsValidForTheSwingTimer(long delay) {
            if (delay < 0 || delay > Integer.MAX_VALUE) {
                throw new IllegalArgumentException(String.format("The swing timer only accepts non-negative delays up to %d milliseconds.", Integer.MAX_VALUE));
            }
        }
    }
}

C'est un petit code, mais Il existe un exemple appelé RxAndroid, il existe un exemple d'implémentation appelé RxSwing, C'était une tâche amusante.

doOnDispose

Il y a d'autres mystères qui m'intéressent à propos de Scheduler, mais c'est tout pour cet article. Merci pour votre travail.

Remarque

*1

En fait, RxAndroid est une bibliothèque qui fournit un Scheduler. Il semble que RxSwing2 puisse être publié si une telle politique est adoptée.

*2

Il ne fonctionne pas sur la JVM, alors c'est tout, Zygote fourche le processus de l'application et l'exécute en premier dans la méthode main () de la classe ActivtyThread. Ici, la boucle d'événements qui devient le thread d'interface utilisateur tourne autour. Par conséquent, il est identique au thread principal de Java. Pour plus d'informations sur ce domaine, lisez «Technologies prenant en charge Android». Allez parce que c'est le meilleur. https://www.amazon.co.jp/dp/4774187593

Recommended Posts

RxAndroid et RxSwing Scheduler
À propos de l'instruction et de l'instruction if
Instructions Java while et for
[Android] Causes et remèdes pour TransactionTooLargeException
SDK AWS pour Java 1.11.x et 2.x
Java pour les débutants, les expressions et les opérateurs 1
Valeurs par défaut pour MaxHeapSize et InitialHeapSize
Java pour les débutants, les expressions et les opérateurs 2
Classes et instances Java pour les débutants