[JAVA] RxAndroid und RxSwing Scheduler

In Bezug auf RxAndroid habe ich einen mit RxJava 2.x kompatiblen Scheduler für den UI-Thread von Swing erstellt. ** Nicht schnell **. Danke für Ihre Unterstützung.

doOnSubscribe

Das Ziel dieses Artikels

Ist das Ziel dieses Artikels. Es scheint, dass niemand RxSwing verwendet. Außerdem sind keine Swing-Kenntnisse erforderlich.

RxSwing existiert aber

RxSwing existiert als eines der ReactiveX-Projekte. https://github.com/ReactiveX/RxSwing

Aber im Gegensatz zu RxAndroid und RxCocoa, Nicht in "ReactiveX für Plattformen und Frameworks" enthalten. http://reactivex.io/languages.html

Außerdem wird RxJava 1.x noch nicht unterstützt.

Es gibt eine Pull-Anfrage, die RxJava 2.x unterstützt, aber es scheint, als wäre sie unbeaufsichtigt geblieben. https://github.com/ReactiveX/RxSwing/pull/63

Das offizielle RxSwing2-Projekt hat ebenfalls einen Rahmen, aber diese Pull-Anfrage wird ebenfalls vernachlässigt. https://github.com/ReactiveX/RxSwing2/pull/1

Es ist auch die Frage, wie lange Swing bleiben wird, Vielleicht kommt RxJava 3 heraus, bevor Swing vorbei ist. (Ich sage etwas Passendes.) Ich denke, es ist schwierig, RxSwing2 in einem so frühen Zyklus mit RxJava2 kompatibel zu machen Ist es ein Ort, an dem die Veröffentlichung und die Bewertungen dafür zögern?

Wenn nur minimale Unterstützung vorhanden ist, ist es eine Thread-Steuerung, die Benutzeroberfläche auszuführen. * 1

UI-Thread

Wie der Android UI Thread Swing verfügt außerdem über einen Event Dispatch Thread (EDT) zum Ausführen der Benutzeroberfläche. (Ereignis hier ist ein Ereignis im Zusammenhang mit der Benutzeroberfläche.) Obwohl der UI-Thread der Haupt-Thread unter Android ist, * 2 Swings EDT ist ein vom Java-Hauptthread separater Thread.

In der Rx-Serie bleibt die Thread-Steuerung dem Scheduler überlassen. Damit können Sie Code mit fast derselben API in der Rx-Serie schreiben Die Behandlung von Threads in jeder Plattform und Sprache ist im "Scheduler" beschränkt. Looper und Handler für Android, GCD für iOS, Es scheint, dass jeder verwendet wird. 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

Glücklicherweise müssen Sie sich im Fall von Swing nicht direkt mit Javas "Thread" oder "Executor" befassen. Die Klassen "Timer" und "SwingUtilities" werden verwendet. https://github.com/ReactiveX/RxSwing/blob/0.x/src/main/java/rx/schedulers/SwingScheduler.java

Scheduler in RxJava

public abstract Worker createWorker();

Das Erstellen eines Schedulers in RxJava implementiert in erster Linie die folgenden Methoden.

public abstract Worker createWorker();

Dieser "Woker" muss mindestens die folgenden Methoden und "Einweg" implementieren.

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

Verantwortlichkeiten des Arbeiters

Und über das Runnable, das an die Schedule () -Methode übergeben wurde

Es liegt in der Verantwortung des "Arbeiters", dies zu gewährleisten.

RxAndroid Scheduler

Wenn Sie die Benutzeroberfläche mit RxJava ausführen möchten Verwenden Sie für RxAndroid "AndroidSchedulers.mainThread ()". Dies ist eine Instanz von "HandlerScheduler" Derjenige, der durch Übergeben des Handlers für den Hauptthread an den Konstruktor erstellt wurde, wird wiederverwendet.

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

AndroidSchedulers.java HandlerScheduler.java

Die Handler-Funktion wird verwendet, um einen Rückruf zu einem bestimmten Zeitpunkt aufzurufen oder einen Rückruf zu löschen. Auszug unten, aber wenn Sie nicht warten können, schauen Sie sich den Code an und sagen Sie "Ich verstehe".

RxSwing2 Worker

Wie oben erwähnt, gibt es im offiziellen Repository zwei PRs, die RxJava 2.x unterstützen. Es ist im Grunde das gleiche, also werde ich nur die "Scheduler" und "Worker" betrachten, die PR für RxSwing2 sind.

Es gibt kaum einen Unterschied zu RxJava 1.x. Es gibt jedoch eine Sache, über die man sich Sorgen machen muss.

Unten finden Sie einen Auszug aus der RxSwing2-Klasse "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) {
			//Kürzung

			//InnerSubscription so wie es ist zurückgeben
			return innerSubscription;
		}

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

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

Mit anderen Worten sind die folgenden zwei Prozesse gleich.

Und diese Verwendung ist nicht "Composite Disposable" Ich denke, Sie sollten "Disposables.empty ()" verwenden. Die Verwendung von "CompositeDisposable" kann einfach ein Überbleibsel des alten RxSwing sein.

RxSwing1 Worker

Für diejenigen, die RxJava 1.x vergessen haben, sind hier die entsprechenden Klassen.

Version Klasse Methode
RxJava1.x Subscription unsubscribe()
RxJava2.x Disposable dispose()

Unten finden Sie einen Auszug aus der Worker-Klasse von 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) {
            //Kürzung
            			
            final BooleanSubscription s = BooleanSubscription.create();

            //Kürzung

            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();
        }

Nennen wir RxSwing für RxJava1.x RxSwing1. In RxSwing1 unterscheiden sich die beiden "unsubscribe ()" wie oben beschrieben.

RxAndroid Worker

Zum Beispiel ist es möglich, dass RxJava2.x die Anforderungen von Woker geändert hat. Um herauszufinden, was richtig ist, schauen Sie sich unseren RxAndroid von Jake God's an.

        // Worker of RxAndroid for RxJava2.x

        private volatile boolean disposed;

 @Override
        public Disposable schedule(Runnable run, long delay, TimeUnit unit) {
            
            //Kürzung

            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)));
            
            //Kürzung

            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;
        }
    }

In RxAndroid, das RxJava 2.x wie dieses unterstützen sollte, Die folgenden zwei Operationen sind nicht gleich.

  • dispose () für die Worker-Instanz
  • dispose () an die Disposable-Instanz, die von sched () der Worker-Instanz zurückgegeben wird

Die Gleichheit der beiden "dispose ()" ist also wahrscheinlich ein Problem. Ich habe jedoch kein Verwendungsmuster gefunden. Es ist am besten, das Richtige oder Falsche zu zeigen, unabhängig von der Anzahl der RxAndroid-Benutzer und dem Glauben an Jake God. Ich habe hier keine Macht und kein Interesse mehr.

Implementierungsbeispiel für RxSwing Scheduler entsprechend RxJava 2.x.

damit, "In einem Fall können Sie für jede Aufgabe" sched () "aufrufen und" dispose () "einzeln aufrufen." Ich denke, es liegt in der Verantwortung des "Schedulers", einen solchen "Arbeiter" zurückzugeben.

Basierend auf dem oben Gesagten ist es ein Implementierungsbeispiel für RxSwing Scheduler, das RxJava 2.x entspricht. 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) {
        //Warum TODO Rx Android dies überschreibt
        //TODO Wenn Sie es so lassen, wie es ist, besteht die Möglichkeit, dass es von Disposable Task entsorgt wird?
        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()) {
                    //Sofortige Ausführung
                    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);

            //Der Swing Scheduler von Ueli Kurmann gibt ein Composite Disposable für Arbeiter zurück und kann nicht auf Aufgabenbasis entsorgen
            //Android Scheduler bietet auch eine aufgabenbasierte Entsorgung, daher denke ich, dass dies notwendig ist, aber ich habe kein Verwendungsmuster gefunden, das einen Unterschied macht.
            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));
            }
        }
    }
}

Es ist ein kleiner Code, aber Es gibt ein Beispiel namens RxAndroid, es gibt ein Implementierungsbeispiel namens RxSwing. Es war eine lustige Aufgabe.

doOnDispose

Es gibt noch andere Rätsel, auf die ich bei Scheduler neugierig bin, aber das war's für diesen Artikel. Danke für deine Arbeit.

Hinweis

*1

Tatsächlich ist RxAndroid eine Bibliothek, die einen Scheduler bereitstellt. Es scheint, dass RxSwing2 freigegeben werden kann, wenn eine solche Richtlinie angewendet wird.

*2

Es läuft nicht auf der JVM, also das war's, Zygote gabelt den Prozess der App und führt ihn zuerst in der main () -Methode der ActivtyThread-Klasse aus. Hier geht die Ereignisschleife um, die zum UI-Thread wird. Daher ist es dasselbe wie der Hauptthread von Java. Weitere Informationen zu diesem Bereich finden Sie unter "Technologien, die Android unterstützen". Komm schon, weil es das Beste ist. https://www.amazon.co.jp/dp/4774187593

Recommended Posts

RxAndroid und RxSwing Scheduler
Über für Anweisung und wenn Anweisung
Java während und für Anweisungen
[Android] Ursache und Abhilfe für TransactionTooLargeException
AWS SDK für Java 1.11.x und 2.x.
Java für Anfänger, Ausdrücke und Operatoren 1
Standardwerte für MaxHeapSize und InitialHeapSize
Java für Anfänger, Ausdrücke und Operatoren 2
Klassen und Instanzen Java für Anfänger