Android中的WorkManager异步工人

问题描述 投票:21回答:5

谷歌最近宣布了新的架构WorkManager组件。它可以很容易地安排在doWork()类实现Worker同步工作,但如果我想要做的一些背景异步工作方式是什么?例如,我要让使用改造的网络服务调用。我知道我可以做一个同步的网络请求,但它会阻塞线程,只是觉得不妥。对此有任何解决方案或它只是目前还不支持?

android android-architecture-components android-workmanager
5个回答
11
投票

对于WorkManager docs

默认情况下,运行的WorkManager在后台线程上的业务。如果你已经在后台线程上运行,并需要同步(阻塞)调用WorkManager中,使用同步()来访问这些方法。

因此,如果你不使用synchronous(),你可以放心地执行从doWork()同步网络调用。这也是从设计的角度来看一个更好的办法,因为回调是凌乱。

也就是说,如果你真的想从火异步doWork()工作,你需要暂停执行线程和使用wait/notify机制(或者其他某个线程管理机制,例如Semaphore)恢复它的异步作业完成后。不是我会建议在大多数情况下。

作为一个方面说明,WorkManager的是在非常早期的alpha。


21
投票

我用的CountDownLatch和等待这个达到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];

    }

17
投票

FYI现在有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;
    }
}

7
投票

如果你在谈论异步工作,你可以将你的工作纳入RxJava观测量/单打。

有一组像.blockingGet().blockingFirst()运营商的,其将Observable<T>成阻塞T

Worker执行对后台线程,所以不要担心NetworkOnMainThreadException


1
投票

我已经使用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
投票

随着协同程序的力量,你可以“同步”这样的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
}
© www.soinside.com 2019 - 2024. All rights reserved.