Асинхронный рабочий в Android WorkManager - PullRequest
0 голосов
/ 18 мая 2018

Google недавно анонсировал новый WorkManager компонент архитектуры.Это позволяет легко планировать синхронную работу, реализуя doWork() в Worker классе, но что если я хочу выполнить некоторую асинхронную работу в фоновом режиме?Например, я хочу сделать вызов сетевой службы с помощью Retrofit.Я знаю, что могу сделать синхронный сетевой запрос, но он блокирует поток и просто чувствует себя не так.Есть ли какое-то решение для этого или просто не поддерживается в данный момент?

Ответы [ 6 ]

0 голосов
/ 04 июля 2019

С помощью сопрограмм вы можете «синхронизировать» doWork() следующим образом:

Приостановить метод получения местоположения (асинхронно):

private suspend fun getLocation(): Location = suspendCoroutine { continuation ->
    val mFusedLocationClient = LocationServices.getFusedLocationProviderClient(appContext)
    mFusedLocationClient.lastLocation.addOnSuccessListener {
        continuation.resume(it)
    }.addOnFailureListener {
        continuation.resumeWithException(it)
    }
}

Пример вызова в doWork():

override fun doWork(): Result {
    val loc = runBlocking {
        getLocation()
    }
    val latitude = loc.latitude
}
0 голосов
/ 16 октября 2018

К вашему сведению: ListenableWorker , предназначенный для асинхронной работы.

Редактировать: Вот несколько примеров примеров использования.Я вырезал большие куски кода, которые, на мой взгляд, не являются иллюстративными, поэтому есть большая вероятность, что здесь есть небольшая ошибка или две.

Это для задачи, которая принимает String photoKey и получает метаданные с сервера., выполняет некоторую работу сжатия, а затем загружает сжатую фотографию.Это происходит вне основного потока.Вот как мы отправляем запрос на работу:

private void compressAndUploadFile(final String photoKey) {
    Data inputData = new Data.Builder()
            .putString(UploadWorker.ARG_PHOTO_KEY, photoKey)
            .build();
    Constraints constraints = new Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED)
            .build();
    OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(UploadWorker.class)
            .setInputData(inputData)
            .setConstraints(constraints)
            .build();
    WorkManager.getInstance().enqueue(request);
}

А в UploadWorker:

public class UploadWorker extends ListenableWorker {
    private static final String TAG = "UploadWorker";
    public static final String ARG_PHOTO_KEY = "photo-key";

    private String mPhotoKey;

    /**
     * @param appContext   The application {@link Context}
     * @param workerParams Parameters to setup the internal state of this worker
     */
    public UploadWorker(@NonNull Context appContext, @NonNull WorkerParameters workerParams) {
        super(appContext, workerParams);
        mPhotoKey = workerParams.getInputData().getString(ARG_PHOTO_KEY);
    }

    @NonNull
    @Override
    public ListenableFuture<Payload> onStartWork() {
        SettableFuture<Payload> future = SettableFuture.create();
        Photo photo = getPhotoMetadataFromServer(mPhotoKey).addOnCompleteListener(task -> {
            if (!task.isSuccessful()) {
                Log.e(TAG, "Failed to retrieve photo metadata", task.getException());
                future.setException(task.getException());
                return;
            }
            MyPhotoType photo = task.getResult();
            File file = photo.getFile();
            Log.d(TAG, "Compressing " + photo);
            MyImageUtil.compressImage(file, MyConstants.photoUploadConfig).addOnCompleteListener(compressionTask -> {
                if (!compressionTask.isSuccessful()) {
                    Log.e(TAG, "Could not parse " + photo + " as an image.", compressionTask.getException());
                    future.set(new Payload(Result.FAILURE));
                    return;
                }
                byte[] imageData = compressionTask.getResult();
                Log.d(TAG, "Done compressing " + photo);
                UploadUtil.uploadToServer(photo, imageData);
                future.set(new Payload(Result.SUCCESS));
            });
        });
        return future;
    }
}
0 голосов
/ 24 июля 2018

Я использовал BlockingQueue, что упрощает синхронизацию потоков и передачу результата, вам потребуется только один объект

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

Также не забудьте освободить ресурсы, если ваш работник был остановлен, этоглавное преимущество перед .blockingGet(), так как теперь вы можете правильно бесплатно отменить задание Rx.

override fun onStopped(cancelled: Boolean) {
    disposable.dispose()
}
0 голосов
/ 21 мая 2018

Если вы говорите об асинхронной работе, вы можете переместить свою работу в RxJava Observables / Singles.

Существует набор операторов, таких как .blockingGet() или .blockingFirst(), который преобразует Observable<T> в блокировку T

Worker работает в фоновом потоке, поэтому не беспокойтесь о NetworkOnMainThreadException.

0 голосов
/ 19 мая 2018

Я использовал обратный отсчет и ждал, пока он достигнет 0, что произойдет только после того, как асинхронный обратный вызов обновил его.Смотрите этот код:

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

    }
0 голосов
/ 18 мая 2018

Per WorkManager docs :

По умолчанию WorkManager выполняет свои операции в фоновом потоке.Если вы уже работаете в фоновом потоке и нуждаетесь в синхронных (блокирующих) вызовах WorkManager, используйте synchronous () для доступа к таким методам.

Поэтому, если вы не используете synchronous(), вы можете безопасно выполнять синхронизацию сетевых вызовов с doWork().Это также лучший подход с точки зрения проектирования, потому что обратные вызовы беспорядочные.

Тем не менее, если вы действительно хотите запускать асинхронные задания из doWork(), вам нужно приостановить поток выполнения и возобновить его после асинхронной работы.завершение работы с использованием механизма wait/notify (или другого механизма управления потоками, например Semaphore).Не то, что я бы рекомендовал в большинстве случаев.

В качестве примечания, WorkManager находится на очень ранней стадии альфа.

...