Frage Asynchroner Worker in Android WorkManager


Google hat kürzlich eine neue WorkManager-Architekturkomponente angekündigt. Es erleichtert das Planen von synchronem Arbeiten durch Implementieren doWork() im Worker Klasse, aber was, wenn ich etwas asynchrone Arbeit im Hintergrund machen möchte? Zum Beispiel möchte ich mit Retrofit einen Netzwerkdienst anrufen. Ich weiß, dass ich eine synchrone Netzwerkanfrage stellen kann, aber es würde den Thread blockieren und sich einfach falsch fühlen. Gibt es eine Lösung oder wird es gerade nicht unterstützt?


10
2018-05-18 02:35


Ursprung


Antworten:


Pro WorkManager-Dokumente:

Standardmäßig führt WorkManager seine Operationen in einem Hintergrundthread aus. Wenn Sie bereits mit einem Hintergrundthread arbeiten und synchrone (blockierende) Aufrufe an WorkManager benötigen, verwenden Sie synchrone (), um auf solche Methoden zuzugreifen.

Daher, wenn Sie nicht verwenden synchronous(), können Sie sicher Sync-Netzwerkanrufe von durchführen doWork(). Dies ist auch ein besserer Ansatz aus Sicht des Designs, da Rückrufe unordentlich sind.

Das heißt, wenn Sie wirklich asynchrone Jobs abfeuern möchten doWork(), müssen Sie den Ausführungsthread pausieren und nach Beendigung des asynchronen Jobs mit dem Befehl fortsetzen wait/notify Mechanismus (oder ein anderer Thread-Management-Mechanismus, z.B. Semaphore). Nicht etwas, was ich in den meisten Fällen empfehlen würde.

Als eine Randnotiz ist WorkManager in sehr frühem Alpha.


5
2018-05-18 02:48



Ich habe einen countdownlatch benutzt und habe darauf gewartet, dass 0 erreicht wird, was nur passiert, wenn der asynchrone Callback es aktualisiert hat. Siehe diesen Code:

public WorkerResult doWork() {

        final WorkerResult[] result = {WorkerResult.RETRY};
        CountDownLatch countDownLatch = new CountDownLatch(1);
        FirebaseFirestore db = FirebaseFirestore.getInstance();

        db.collection("collection").whereEqualTo("this","that").get().addOnCompleteListener(task -> {
            if(task.isSuccessful()) {
                task.getResult().getDocuments().get(0).getReference().update("field", "value")
                        .addOnCompleteListener(task2 -> {
                            if (task2.isSuccessful()) {
                                result[0] = WorkerResult.SUCCESS;
                            } else {
                                result[0] = WorkerResult.RETRY;
                            }
                            countDownLatch.countDown();
                        });
            } else {
                result[0] = WorkerResult.RETRY;
                countDownLatch.countDown();
            }
        });

        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return result[0];

    }

10
2018-05-19 12:15



Wenn Sie über einen asynchronen Job sprechen, können Sie Ihre Arbeit in RxJava Observables / Singles verschieben.

Es gibt eine Reihe von Operatoren wie .blockingGet() oder .blockingFirst() was transformiert Observable<T> in die Blockierung T 

Worker führt auf Hintergrundthread so mach dir keine Sorgen NetworkOnMainThreadException.


4
2018-05-21 05:54



Ich habe benutzt BlockingQueue, das vereinfacht die Synchronisierung von Threads und die Weitergabe von Ergebnissen, benötigen Sie nur ein Objekt

private var disposable = Disposables.disposed()


override fun doWork(): Result {
    val result = LinkedBlockingQueue<Result>()

    disposable = completable.subscribe(
            { result.put(Result.SUCCESS) },
            { result.put(Result.RETRY) }
    )

    return try {
        result.take()
    } catch (e: InterruptedException) {
        Result.RETRY
    }
}

Vergessen Sie auch nicht Ressourcen freizugeben, wenn Ihr Worker gestoppt wurde, dies ist der Hauptvorteil gegenüber .blockingGet() Wie jetzt können Sie Ihre Rx-Aufgabe ordnungsgemäß freigeben.

override fun onStopped(cancelled: Boolean) {
    disposable.dispose()
}

0
2017-07-24 15:29