由于我需要在WorkManager中异步执行工作,因此需要使用ListenableWorker
,它默认情况下在主(UI)线程上运行。由于这项工作可能是很长时间的处理任务,可能会冻结接口,因此我想在后台线程上执行它。在Working with WorkManager (Android Dev Summit '18) video中,Google工程师展示了如何手动配置WorkManager以在自定义Executor
上运行作品,因此我遵循了他的指导:
1)在AndroidManifest中禁用默认的WorkManager初始化程序:
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="com.example.myapp.workmanager-init"
tools:node="remove" />
2)在Application.onCreate中,使用自定义配置初始化WorkManager,在我的情况下是这样:
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Configuration configuration = new Configuration.Builder().setExecutor(Executors.newSingleThreadExecutor()).build();
WorkManager.initialize(this, configuration);
}
}
现在我的实际ListenableWorker
是这样的:
@NonNull
@Override
public ListenableFuture<Result> startWork() {
Log.d(TAG, "Work started.");
mFuture = ResolvableFuture.create();
Result result = doWork();
mFuture.set(result);
return mFuture;
}
private Result doWork() {
Log.d(TAG, "isMainThread? " + isMainThread());
mFusedLocationProviderClient.getLastLocation().addOnSuccessListener(new OnSuccessListener<Location>() {
@Override
public void onSuccess(Location location) {
if (location != null) {
// Since I still don't know how to communicate with the UI, I will just log the location
Log.d(TAG, "Last location: " + location);
return Result.success();
} else {
return Result.failure();
}
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
e.printStackTrace();
return Result.failure();
}
});
}
private boolean isMainThread() {
return Looper.getMainLooper().getThread() == Thread.currentThread();
}
即使我指定isMainThread()
WorkManager应该用作新的后台线程,为什么Executor
方法仍返回true,我该如何在后台线程上实际运行该工作?
编辑:ListenableWorker
,需要CountDownLatch
。
由于我需要在每次工作成功后重新安排工作的时间(PeriodicWorkRequest
的15分钟最小间隔的解决方法),因此我需要在上一部分工作返回成功后再进行工作,否则我的行为会很奇怪。这是必需的,因为显然ExistingWorkPolicy.APPEND
不能按预期工作。
用例是即使在后台,也要以相当频繁的间隔(5-10s)高精度请求位置更新。即使应用程序未运行(但不是强制停止),也可以通过SMS打开或关闭,也可以通过按钮(这是一个大学项目)来打开和关闭。
public class LocationWorker extends ListenableWorker {
static final String UNIQUE_WORK_NAME = "LocationWorker";
static final String KEY_NEW_LOCATION = "new_location";
private static final String TAG = "LocationWorker";
private ResolvableFuture<Result> mFuture;
private LocationCallback mLocationCallback;
private CountDownLatch mLatch;
private Context mContext;
public LocationWorker(@NonNull final Context appContext, @NonNull WorkerParameters workerParams) {
super(appContext, workerParams);
mContext = appContext;
Utils.setRequestingLocationUpdates(mContext, true);
mLatch = new CountDownLatch(1);
mLocationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
LocationUtils.getInstance(mContext).removeLocationUpdates(this);
Location location = locationResult.getLastLocation();
Log.d(TAG, "Work " + getId() + " returned: " + location);
mFuture.set(Result.success(Utils.getOutputData(location)));
// Rescheduling work
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(LocationWorker.class).setInitialDelay(10, TimeUnit.SECONDS).build();
WorkManager.getInstance().enqueueUniqueWork(LocationWorker.UNIQUE_WORK_NAME, ExistingWorkPolicy.KEEP, request);
Log.d(TAG, "Rescheduling work. New ID: " + request.getId());
// Relase lock
mLatch.countDown();
}
};
}
@NonNull
@Override
public ListenableFuture<Result> startWork() {
Log.d(TAG, "Starting work " + getId());
mFuture = ResolvableFuture.create();
LocationUtils.getInstance(mContext).requestSingleUpdate(mLocationCallback, new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
LocationUtils.getInstance(mContext).removeLocationUpdates(mLocationCallback);
Utils.setRequestingLocationUpdates(mContext, false);
WorkManager.getInstance().cancelUniqueWork(UNIQUE_WORK_NAME);
mFuture.set(Result.failure());
// Relase lock
mLatch.countDown();
}
});
try {
mLatch.await(5L, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
return mFuture;
}
}
答案 0 :(得分:4)
如果您要连续(即少于60秒),请绝对使用foreground service而不是不的WorkManager,这是为了,按照the documentation:
可延迟的异步任务
不需要连续运行。
但是,如果您确实不正确地使用WorkManager,则需要牢记以下几点:
您的自定义doWork
方法在主线程上运行,因为按照setExecutor() documentation:
运行Worker的执行器
具体地说,只有ListenableWorker
的{{3}}子类在Executor
提供的后台线程上运行-不是您的ListenableWorker
实现。< / p>
按照Worker:
在主线程上调用此方法。
由于使用的是ListenableWorker
,因此,按预期在主线程上调用了startWork
方法。由于您在同一线程上调用自己的doWork()
方法,因此您仍将位于主线程上。
在您的情况下,您无需关心正在使用的线程,也不需要任何Executor
,因为您调用getLastLocation()
的线程都没有关系。
相反,您只需要在实际有结果(即在set
或ResolvableFuture
回调中)的onSuccess()
上调用onFailure
。这是WorkManager
的信号,说明您实际上已经完成工作:
public class LocationWorker extends ListenableWorker {
static final String UNIQUE_WORK_NAME = "LocationWorker";
static final String KEY_NEW_LOCATION = "new_location";
private static final String TAG = "LocationWorker";
private ResolvableFuture<Result> mFuture;
private LocationCallback mLocationCallback;
public LocationWorker(@NonNull final Context appContext, @NonNull WorkerParameters workerParams) {
super(appContext, workerParams);
}
@NonNull
@Override
public ListenableFuture<Result> startWork() {
Log.d(TAG, "Starting work " + getId());
mFuture = ResolvableFuture.create();
Utils.setRequestingLocationUpdates(getApplicationContext(), true);
mLocationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
LocationUtils.getInstance(getApplicationContext()).removeLocationUpdates(this);
Location location = locationResult.getLastLocation();
Log.d(TAG, "Work " + getId() + " returned: " + location);
// Rescheduling work
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(LocationWorker.class).setInitialDelay(10, TimeUnit.SECONDS).build();
WorkManager.getInstance().enqueueUniqueWork(LocationWorker.UNIQUE_WORK_NAME, ExistingWorkPolicy.KEEP, request);
Log.d(TAG, "Rescheduling work. New ID: " + request.getId());
// Always set the result as the last operation
mFuture.set(Result.success(Utils.getOutputData(location)));
}
};
LocationUtils.getInstance(getApplicationContext()).requestSingleUpdate(mLocationCallback, new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
LocationUtils.getInstance(getApplicationContext()).removeLocationUpdates(mLocationCallback);
Utils.setRequestingLocationUpdates(getApplicationContext(), false);
WorkManager.getInstance().cancelUniqueWork(UNIQUE_WORK_NAME);
mFuture.set(Result.failure());
}
});
return mFuture;
}
}
答案 1 :(得分:0)
好-与其他答案一样,如果您想频繁地工作(例如每60秒一次),则应该使用前台服务。
无论如何,我都将使用协程退出主线程。 喜欢:
runBlocking(Dispatchers.Default) {
//If you want to run blocking your code
}
或使用GlobalScope Launch
GlobalScope.launch {
//Work in a separated thread (running not blocking)
}
这是一个使用ListenableWorker获取位置并在主线程中设置侦听器的实际示例。