谷歌最近宣布了新的架构WorkManager
组件。它可以很容易地安排在doWork()
类实现Worker
同步工作,但如果我想要做的一些背景异步工作方式是什么?例如,我要让使用改造的网络服务调用。我知道我可以做一个同步的网络请求,但它会阻塞线程,只是觉得不妥。对此有任何解决方案或它只是目前还不支持?
默认情况下,运行的WorkManager在后台线程上的业务。如果你已经在后台线程上运行,并需要同步(阻塞)调用WorkManager中,使用同步()来访问这些方法。
因此,如果你不使用synchronous()
,你可以放心地执行从doWork()
同步网络调用。这也是从设计的角度来看一个更好的办法,因为回调是凌乱。
也就是说,如果你真的想从火异步doWork()
工作,你需要暂停执行线程和使用wait/notify
机制(或者其他某个线程管理机制,例如Semaphore
)恢复它的异步作业完成后。不是我会建议在大多数情况下。
作为一个方面说明,WorkManager的是在非常早期的alpha。
我用的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];
}
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;
}
}
如果你在谈论异步工作,你可以将你的工作纳入RxJava观测量/单打。
有一组像.blockingGet()
或.blockingFirst()
运营商的,其将Observable<T>
成阻塞T
Worker
执行对后台线程,所以不要担心NetworkOnMainThreadException
。
我已经使用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()
}
随着协同程序的力量,你可以“同步”这样的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
}