我们有一个使用WorkManager处理后台同步工作的Android应用程序。我们的同步工作者是这样的:
工作由帮助类的专用方法安排:
当用户单击“同步”按钮或由另一个工作程序调用时,将调用该函数,该工作程序每20分钟运行一次并以此方式调用helper的函数。
public class SyncWorker extends Worker {
[...]
@NonNull
@Override
public Result doWork() {
if (canNotRetry(getRunAttemptCount())) {
// This could seem unreachable, consider removing... or not... because if stopped by the
// system, the work might be retried by design
CBlogger.INSTANCE.log([...]);
return Result.success();
}
boolean syncOk = false;
//Sync
try (Realm realm = Realm.getDefaultInstance()) {
// Doing sync related ops & network calls
// checking this.isStopped() between operations to quit
// sync activity when worker has to be stopped
syncOk = true;
} catch (Throwable throwable) {
CBlogger.INSTANCE.log([...]);
}
// On error, continue with following code to avoid any logic in catch
// This method must NOT throw any unhandled exception to avoid unique work to be marked as failed
try {
if (syncOk) {
return Result.success();
}
if (canNotRetry(getRunAttemptCount() + 1)) {
CBlogger.INSTANCE.log([...]);
return Result.success();
} else {
CBlogger.INSTANCE.log([...]);
return Result.retry();
}
} catch (Throwable e) {
CBlogger.INSTANCE.log([...]);
return Result.success();
}
}
private boolean canNotRetry(int tryNumber) {
// Check if the work has been retry too many times
if (tryNumber > MAX_SYNC_RETRY_COUNT) {
CBlogger.INSTANCE.log([...]);
return true;
} else {
return false;
}
}
@Override
public void onStopped() {
CBlogger.INSTANCE.log([...]);
}
}
工作由帮助类的专用方法安排:
public static void scheduleWorker(Context context, String syncPolicy, ExistingWorkPolicy existingWorkingPolicy){
Constraints constraints = new Constraints.Builder()
.setRequiresCharging(false)
.setRequiredNetworkType(NetworkType.CONNECTED)
.build();
Data.Builder data = new Data.Builder();
data.putString(context.getResources().getString(R.string.sync_worker_policy), syncPolicy);
Log.d(TAG, "Scheduling one-time sync request");
logger.info("Scheduling one-time sync request");
OneTimeWorkRequest oneTimeWorkRequest = new OneTimeWorkRequest.Builder
(SyncWorker.class)
.setInputData(data.build())
.setConstraints(constraints)
.setBackoffCriteria(
BackoffPolicy.LINEAR,
OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS)
.build();
WorkManager.getInstance(context).enqueueUniqueWork("OneTimeSyncWorker", existingWorkingPolicy, oneTimeWorkRequest);
}
当用户单击“同步”按钮或由另一个工作程序调用时,将调用该函数,该工作程序每20分钟运行一次并以此方式调用helper的函数。
SyncWorkerManager.scheduleWorker(context, context.getResources().getString(R.string.sync_worker_policy_full), ExistingWorkPolicy.KEEP);
为了避免重复等待或运行,只有在没有等待或正在运行时才会排队新的同步。请注意,同步工作策略强制要求连接到网络。
这种策略总体上效果很好,但有时我们发现日志中Worker的onStopped()
方法在SyncWorker启动后几秒钟(约10“)被调用。
我们知道,我们从外部不会编程停止特定的Worker,而且我们只会在注销过程中或新登录之前(也安排del周期性Worker)调用WorkManager.getInstance(context).cancelAllWork();
,那么系统何时决定停止Worker并调用其onStopped()
方法?
我知道这可能发生在以下情况下:
- 约束不再满足(网络连接中断)
- 工作程序超过了JobScheduler实现所施加的10分钟限制(我们的场景在Android 9设备上进行了测试)
- 使用相同名称和REPLACE策略排队新的唯一工作(我们从未在SyncWorker应用中使用此策略,仅在PeriodicSyncWorker中使用)
- 由于this bug而产生的虚假调用(我们使用“androidx.work:work-runtime:2.2.0”)
还有其他原因会导致工作程序停止吗?例如:
- Doze模式
- 应用待机桶
- 应用后台限制(设置-->应用-->我的应用-->电池-->允许后台运行)
- 应用电池优化(设置-->应用-->我的应用-->电池-->电池优化)
谢谢