前言
Jetpack 提供了 WorkManager来执行一些任务,既然执行任务,就涉及到任务的创建、任务的属性、任务的执行条件、如何执行任务等等。本文通过任务属性、任务约束及如何执行任务来揭开workmanager的神秘面纱
一、任务创建(任务属性)
//任务定义
class UploadWorker(conText: Context, params: WorkerParameters) : Worker(conText, params) {
override fun doWork(): Result {
var name = inputData.getString("name")
Log.d("UploadWorker ==" ,"dowork 执行了 "+name)
//Toast.makeText(applicationContext, "执行requestManager", Toast.LENGTH_LONG).show()
return Result.retry() //Result.failure() Result.retry()
}
}
复制代码
可以看到 UploadWorker 继承了Worker,而Worker 又继承了 ListenableWorker,传入参数有两个 一个是上下文,一个是 WorkerParameters
public abstract class Worker extends ListenableWorker {
// Package-private to avoid synthetic accessor.
SettableFuture<Result> mFuture;
@Keep
@SuppressLint("BanKeepAnnotation")
public Worker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
@WorkerThread
public abstract @NonNull Result doWork();
//startWork方法 在线程池中调用执行 doWork 方法,然后将doWork方法的返回值设置到Future中返回
@Override
public final @NonNull ListenableFuture<Result> startWork() {
mFuture = SettableFuture.create();
getBackgroundExecutor().execute(new Runnable() {
@Override
public void run() {
try {
Result result = doWork();
mFuture.set(result);
} catch (Throwable throwable) {
mFuture.setException(throwable);
}
}
});
return mFuture;
}
}
复制代码
Worker 的代码也很简单,暴露了 doWork()方法让子类实现,然后 提供了 startWork 方法,用线程池执行dowork,然后将返回值放到SettableFuture 中。
//ListenableWorker
public abstract class ListenableWorker {
//上下文
private @NonNull Context mAppContext;
//传入参数
private @NonNull WorkerParameters mWorkerParams;
//是否暂停
private volatile boolean mStopped;
//是否被使用
private boolean mUsed;
//是否运行在前台
private boolean mRunInForeground;
//构造函数 context 和 WorkerParameters 都不能为空,为空时会抛异常
public ListenableWorker(@NonNull Context appContext, @NonNull WorkerParameters workerParams) {
// Actually make sure we don't get nulls.
if (appContext == null) {
throw new IllegalArgumentException("Application Context is null");
}
if (workerParams == null) {
throw new IllegalArgumentException("WorkerParameters is null");
}
mAppContext = appContext;
mWorkerParams = workerParams;
}
public final @NonNull Context getApplicationContext() {
return mAppContext;
}
public final @NonNull UUID getId() {
return mWorkerParams.getId();
}
public final @NonNull Data getInputData() {
return mWorkerParams.getInputData();
}
public final @NonNull Set<String> getTags() {
return mWorkerParams.getTags();
}
@RequiresApi(24)
public final @NonNull List<Uri> getTriggeredContentUris() {
return mWorkerParams.getTriggeredContentUris();
}
@RequiresApi(24)
public final @NonNull List<String> getTriggeredContentAuthorities() {
return mWorkerParams.getTriggeredContentAuthorities();
}
@RequiresApi(28)
public final @Nullable Network getNetwork() {
return mWorkerParams.getNetwork();
}
@IntRange(from = 0)
public final int getRunAttemptCount() {
return mWorkerParams.getRunAttemptCount();
}
//startWork方法 Woker类实现,这里标注了 调用需要是mainThread
@MainThread
public abstract @NonNull ListenableFuture<Result> startWork();
//更新执行进度
@NonNull
public final ListenableFuture<Void> setProgressAsync(@NonNull Data data) {
return mWorkerParams.getProgressUpdater()
.updateProgress(getApplicationContext(), getId(), data);
}
@NonNull
public final ListenableFuture<Void> setForegroundAsync(@NonNull ForegroundInfo foregroundInfo) {
mRunInForeground = true;
return mWorkerParams.getForegroundUpdater()
.setForegroundAsync(getApplicationContext(), getId(), foregroundInfo);
}
//暂停相关操作
public final boolean isStopped() {
return mStopped;
}
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public final void stop() {
mStopped = true;
onStopped();
}
public void onStopped() {
// Do nothing by default.
}
//是否已经被使用
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public final boolean isUsed() {
return mUsed;
}
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public final void setUsed() {
mUsed = true;
}
//是否在前台运行
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public boolean isRunInForeground() {
return mRunInForeground;
}
/**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public @NonNull Executor getBackgroundExecutor() {
return mWorkerParams.getBackgroundExecutor();
}
/**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public @NonNull TaskExecutor getTaskExecutor() {
return mWorkerParams.getTaskExecutor();
}
/**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public @NonNull WorkerFactory getWorkerFactory() {
return mWorkerParams.getWorkerFactory();
}
//执行结果类 result/Success/Failure/Retry
public abstract static class Result {
@NonNull
public static Result success() {
return new Success();
}
@NonNull
public static Result success(@NonNull Data outputData) {
return new Success(outputData);
}
@NonNull
public static Result retry() {
return new Retry();
}
@NonNull
public static Result failure() {
return new Failure();
}
@NonNull
public static Result failure(@NonNull Data outputData) {
return new Failure(outputData);
}
/**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
Result() {
// Restricting access to the constructor, to give Result a sealed class
// like behavior.
}
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static final class Success extends Result {
private final Data mOutputData;
public Success() {
this(Data.EMPTY);
}
public Success(@NonNull Data outputData) {
super();
mOutputData = outputData;
}
/**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public @NonNull Data getOutputData() {
return mOutputData;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Success success = (Success) o;
return mOutputData.equals(success.mOutputData);
}
@Override
public int hashCode() {
String name = Success.class.getName();
return 31 * name.hashCode() + mOutputData.hashCode();
}
@Override
public String toString() {
return "Success {" + "mOutputData=" + mOutputData + '}';
}
}
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static final class Failure extends Result {
private final Data mOutputData;
public Failure() {
this(Data.EMPTY);
}
public Failure(@NonNull Data outputData) {
super();
mOutputData = outputData;
}
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public @NonNull Data getOutputData() {
return mOutputData;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Failure failure = (Failure) o;
return mOutputData.equals(failure.mOutputData);
}
@Override
public int hashCode() {
String name = Failure.class.getName();
return 31 * name.hashCode() + mOutputData.hashCode();
}
@Override
public String toString() {
return "Failure {" + "mOutputData=" + mOutputData + '}';
}
}
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static final class Retry extends Result {
public Retry() {
super();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
// We are treating all instances of Retry as equivalent.
return o != null && getClass() == o.getClass();
}
@Override
public int hashCode() {
String name = Retry.class.getName();
return name.hashCode();
}
@Override
public String toString() {
return "Retry";
}
}
}
}
//任务相关参数
public final class WorkerParameters {
private @NonNull UUID mId; //随机序列
private @NonNull Data mInputData;//输入参数
private @NonNull Set<String> mTags;//任务TAG
private @NonNull RuntimeExtras mRuntimeExtras; //执行时参数
private int mRunAttemptCount; //执行重试次数
private @NonNull Executor mBackgroundExecutor; //后台任务执行线程池
private @NonNull TaskExecutor mWorkTaskExecutor; //任务执行线程池
private @NonNull WorkerFactory mWorkerFactory; //任务工厂
private @NonNull ProgressUpdater mProgressUpdater;//进度更新者
private @NonNull ForegroundUpdater mForegroundUpdater;//前台更新者
}
复制代码
小结:
任务参数:WorkerParameters 定义了一系列任务相关参数,包括执行任务的入参mInputData,任务标识TAG,任务执行线程池,任务进度更新,任务工厂等
任务结果:(Result/Success/Failure/Retry)
二、任务执行条件(任务约束 )
我们执行任务有一系列约束条件,并且字段都带上了 @ColumnInfo注释,意味着会写进数据库
a.网络条件约束有以下几种:
public enum NetworkType {
//任务对网络无要求
NOT_REQUIRED,
//只要有网络连接 就可以执行任务
CONNECTED,
//非计量网络链接 可以执行任务
UNMETERED,
//非漫游网络连接 可以执行任务
NOT_ROAMING,
//计量网络连接 可以执行任务
METERED
}
//网络条件判断
public class NetworkStateTracker extends ConstraintTracker<NetworkState> {
// Synthetic Accessor
static final String TAG = Logger.tagWithPrefix("NetworkStateTracker");
private final ConnectivityManager mConnectivityManager;
@RequiresApi(24)
private NetworkStateCallback mNetworkCallback;
private NetworkStateBroadcastReceiver mBroadcastReceiver;
public NetworkStateTracker(@NonNull Context context, @NonNull TaskExecutor taskExecutor) {
super(context, taskExecutor);
mConnectivityManager =
(ConnectivityManager) mAppContext.getSystemService(Context.CONNECTIVITY_SERVICE);
//判断如果支持NetworkCallback就用NetWorkCallback 监听网络连接
//否则用广播
if (isNetworkCallbackSupported()) {
mNetworkCallback = new NetworkStateCallback();
} else {
mBroadcastReceiver = new NetworkStateBroadcastReceiver();
}
}
@Override
public NetworkState getInitialState() {
return getActiveNetworkState();
}
@Override
public void startTracking() {
if (isNetworkCallbackSupported()) {
try {
Logger.get().debug(TAG, "Registering network callback");
mConnectivityManager.registerDefaultNetworkCallback(mNetworkCallback);
} catch (IllegalArgumentException | SecurityException e) {
}
} else {
Logger.get().debug(TAG, "Registering broadcast receiver");
mAppContext.registerReceiver(mBroadcastReceiver,
new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
}
}
@Override
public void stopTracking() {
if (isNetworkCallbackSupported()) {
try {
Logger.get().debug(TAG, "Unregistering network callback");
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
} catch (IllegalArgumentException | SecurityException e) {
}
} else {
Logger.get().debug(TAG, "Unregistering broadcast receiver");
mAppContext.unregisterReceiver(mBroadcastReceiver);
}
}
//判断是否支持 NetworkCallback API 版本 >=24
private static boolean isNetworkCallbackSupported() {
// Based on requiring ConnectivityManager#registerDefaultNetworkCallback - added in API 24.
return Build.VERSION.SDK_INT >= 24;
}
@SuppressWarnings("WeakerAccess") /* synthetic access */
NetworkState getActiveNetworkState() {
// Use getActiveNetworkInfo() instead of getNetworkInfo(network) because it can detect VPNs.
NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
boolean isConnected = info != null && info.isConnected();
boolean isValidated = isActiveNetworkValidated();
boolean isMetered = ConnectivityManagerCompat.isActiveNetworkMetered(mConnectivityManager);
boolean isNotRoaming = info != null && !info.isRoaming();
return new NetworkState(isConnected, isValidated, isMetered, isNotRoaming);
}
//判断网络是否合法
private boolean isActiveNetworkValidated() {
if (Build.VERSION.SDK_INT < 23) {
return false; // NET_CAPABILITY_VALIDATED not available until API 23. Used on API 26+.
}
Network network = mConnectivityManager.getActiveNetwork();
NetworkCapabilities capabilities = mConnectivityManager.getNetworkCapabilities(network);
return capabilities != null
&& capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
}
@RequiresApi(24)
private class NetworkStateCallback extends NetworkCallback {
NetworkStateCallback() {
}
@Override
public void onCapabilitiesChanged(
@NonNull Network network, @NonNull NetworkCapabilities capabilities) {
setState(getActiveNetworkState());
}
@Override
public void onLost(@NonNull Network network) {
setState(getActiveNetworkState());
}
}
private class NetworkStateBroadcastReceiver extends BroadcastReceiver {
NetworkStateBroadcastReceiver() {
}
@Override
public void onReceive(Context context, Intent intent) {
if (intent == null || intent.getAction() == null) {
return;
}
if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
Logger.get().debug(TAG, "Network broadcast received");
setState(getActiveNetworkState());
}
}
}
}
复制代码
网络连接的判断在API>=24 时采用 NetworkStateCallback 来监听网络连接状态的变更,在api < 24时采用BroadcastReceiver 来监听系统广播CONNECTIVITY_ACTION 来监听网络连接状态的变更,变更后会同步更新网络的计量、漫游、连接状态等
b.充电约束
public class BatteryChargingTracker extends BroadcastReceiverConstraintTracker<Boolean> {
private static final String TAG = Logger.tagWithPrefix("BatteryChrgTracker");
/**
* Create an instance of {@link BatteryChargingTracker}.
* @param context The application {@link Context}
* @param taskExecutor The internal {@link TaskExecutor} being used by WorkManager.
*/
public BatteryChargingTracker(@NonNull Context context, @NonNull TaskExecutor taskExecutor) {
super(context, taskExecutor);
}
//获取初始状态是否在充电
@Override
public Boolean getInitialState() {
// {@link ACTION_CHARGING} and {@link ACTION_DISCHARGING} are not sticky broadcasts, so
// we use {@link ACTION_BATTERY_CHANGED} on all APIs to get the initial state.
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent intent = mAppContext.registerReceiver(null, intentFilter);
if (intent == null) {
Logger.get().error(TAG, "getInitialState - null intent received");
return null;
}
return isBatteryChangedIntentCharging(intent);
}
@Override
public IntentFilter getIntentFilter() {
IntentFilter intentFilter = new IntentFilter();
//23 版本及以上监听 ACTION_CHARGING 和 ACTION_DISCHARGING
//23 版本以下监听 ACTION_POWER_CONNECTED 和 ACTION_POWER_DISCONNECTED
if (Build.VERSION.SDK_INT >= 23) {
intentFilter.addAction(BatteryManager.ACTION_CHARGING);
intentFilter.addAction(BatteryManager.ACTION_DISCHARGING);
} else {
intentFilter.addAction(Intent.ACTION_POWER_CONNECTED);
intentFilter.addAction(Intent.ACTION_POWER_DISCONNECTED);
}
return intentFilter;
}
//监听系统广播主要是 充电和断电
@Override
public void onBroadcastReceive(Context context, @NonNull Intent intent) {
String action = intent.getAction();
if (action == null) {
return;
}
Logger.get().debug(TAG, String.format("Received %s", action));
switch (action) {
case BatteryManager.ACTION_CHARGING:
setState(true);
break;
case BatteryManager.ACTION_DISCHARGING:
setState(false);
break;
case Intent.ACTION_POWER_CONNECTED:
setState(true);
break;
case Intent.ACTION_POWER_DISCONNECTED:
setState(false);
break;
}
}
//判断是否在充电
private boolean isBatteryChangedIntentCharging(Intent intent) {
boolean charging;
//23版本及以上获取 BatteryManager.EXTRA_STATUS 来判断
//23以下获取BatteryManager.EXTRA_PLUGGED 来判断
if (Build.VERSION.SDK_INT >= 23) {
int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
charging = (status == BatteryManager.BATTERY_STATUS_CHARGING
|| status == BatteryManager.BATTERY_STATUS_FULL);
} else {
int chargePlug = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
charging = (chargePlug != 0);
}
return charging;
}
}
复制代码
设备是否在充电状态的判断以api23 为界限,23 以下通过监听广播ACTION_POWER_CONNECTED 和 ACTION_POWER_DISCONNECTED 来判断充电和断电,23及以上通过监听 ACTION_CHARGING 和 ACTION_DISCHARGING 来判断充电状态
c.设备是否空闲约束
值的注意的是 这个约束条件并没有找到监听的方案,并且有诸多限制,比如API>=23;前台任务不能设置这个条件;设置过setBackoffCriteria 的任务不能使用此条件
d.设备内存约束
public class StorageNotLowTracker extends BroadcastReceiverConstraintTracker<Boolean> {
private static final String TAG = Logger.tagWithPrefix("StorageNotLowTracker");
public StorageNotLowTracker(@NonNull Context context, @NonNull TaskExecutor taskExecutor) {
super(context, taskExecutor);
}
@Override
public Boolean getInitialState() {
Intent intent = mAppContext.registerReceiver(null, getIntentFilter());
if (intent == null || intent.getAction() == null) {
return true;
} else {
switch (intent.getAction()) {
case Intent.ACTION_DEVICE_STORAGE_OK:
return true;
case Intent.ACTION_DEVICE_STORAGE_LOW:
return false;
default:
return null;
}
}
}
@Override
public IntentFilter getIntentFilter() {
// In API 26+, DEVICE_STORAGE_OK/LOW are deprecated and are no longer sent to
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_LOW);
return intentFilter;
}
@Override
public void onBroadcastReceive(Context context, @NonNull Intent intent) {
if (intent.getAction() == null) {
return; // Should never happen since the IntentFilter was configured.
}
Logger.get().debug(TAG, String.format("Received %s", intent.getAction()));
switch (intent.getAction()) {
case Intent.ACTION_DEVICE_STORAGE_OK:
setState(true);
break;
case Intent.ACTION_DEVICE_STORAGE_LOW:
setState(false);
break;
}
}
}
复制代码
可以看到代码逻辑也很简单,也是通过监听广播ACTION_DEVICE_STORAGE_OK 和 ACTION_DEVICE_STORAGE_LOW 来决定设备存储这个约束条件;但是在 >api26 版本这两个广播已经废弃了,且未来可能不再发送这两个广播,所以这个条件使用的话要谨慎
e.电量约束
public class BatteryNotLowTracker extends BroadcastReceiverConstraintTracker<Boolean> {
private static final String TAG = Logger.tagWithPrefix("BatteryNotLowTracker");
//电量低的阈值 <=15%
static final float BATTERY_LOW_THRESHOLD = 0.15f;
public BatteryNotLowTracker(@NonNull Context context, @NonNull TaskExecutor taskExecutor) {
super(context, taskExecutor);
}
//获取初始状态
@Override
public Boolean getInitialState() {
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent intent = mAppContext.registerReceiver(null, intentFilter);
if (intent == null) {
Logger.get().error(TAG, "getInitialState - null intent received");
return null;
}
int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
float batteryPercentage = level / (float) scale;
return (status == BatteryManager.BATTERY_STATUS_UNKNOWN
|| batteryPercentage > BATTERY_LOW_THRESHOLD);
}
@Override
public IntentFilter getIntentFilter() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_BATTERY_OKAY);
intentFilter.addAction(Intent.ACTION_BATTERY_LOW);
return intentFilter;
}
//监听电量变化
@Override
public void onBroadcastReceive(Context context, @NonNull Intent intent) {
if (intent.getAction() == null) {
return;
}
Logger.get().debug(TAG, String.format("Received %s", intent.getAction()));
switch (intent.getAction()) {
case Intent.ACTION_BATTERY_OKAY:
setState(true);
break;
case Intent.ACTION_BATTERY_LOW:
setState(false);
break;
}
}
}
复制代码
代码也相对简单,电量是否低的阈值的 15%,通过监听广播 ACTION_BATTERY_OKAY 和 ACTION_BATTERY_LOW 来决定该约束条件是否满足,这里有个问题 是 阈值是定义的15%,如果各厂商定义了发送广播的阈值,这里会导致不一致,也就是触发条件可能会不一致。
三、任务信息
public final class WorkInfo {
//任务的ID
private @NonNull UUID mId;
//任务状态
private @NonNull State mState
//任务输出数据
private @NonNull Data mOutputData;
//任务标识 tag
private @NonNull Set<String> mTags;
//任务进度
private @NonNull Data mProgress;
//任务尝试次数
private int mRunAttemptCount;
//任务状态
public enum State {
//入队
ENQUEUED,
//执行中
RUNNING,
//执行成功
SUCCEEDED,
//执行失败
FAILED,
//任务阻塞
BLOCKED,
//任务取消
CANCELLED;
//任务是否处于完成状态 (成功、失败、取消)
public boolean isFinished() {
return (this == SUCCEEDED || this == FAILED || this == CANCELLED);
}
}
}
复制代码
任务的信息包含了一系列状态,及任务执行过程中的一些数据,如进度、状态等,WorkSpec主要是写入数据库相关的任务信息的数据
四、任务执行
有了任务创建、任务约束、任务约束,我们来看下任务是怎么执行的。
单次任务:
//上层调用 调用了enqueue
WorkManager.getInstance(this).enqueue(workRequest)
//getInstance 返回了 实现类 WorkManagerImpl
public static @NonNull WorkManager getInstance(@NonNull Context context) {
return WorkManagerImpl.getInstance(context);
}
//WorkManager.java 将workRequest 转换成列表形式然后再次调用enqueue
@NonNull
public final Operation enqueue(@NonNull WorkRequest workRequest) {
return enqueue(Collections.singletonList(workRequest));
}
//WorkManagerImpl.java
//enqueue 是接口方法 而WorkManagerImpl 是 WorkManager 的实现,我们来看下而WorkManagerImpl的实现
@Override
@NonNull
public Operation enqueue(@NonNull List<? extends WorkRequest> workRequests) {
//检查入参是否为空
if (workRequests.isEmpty()) {
throw new IllegalArgumentException(
"enqueue needs at least one WorkRequest.");
}
//new WorkContinuationImpl,然后调用WorkContinuationImpl的 enqueue
return new WorkContinuationImpl(this, workRequests).enqueue();
}
//WorkContinuationImpl.java
WorkContinuationImpl(
@NonNull WorkManagerImpl workManagerImpl,
@NonNull List<? extends WorkRequest> work) {
this(
workManagerImpl,
null,
ExistingWorkPolicy.KEEP,
work,
null);
}
//equeue 方法
@Override
public @NonNull Operation enqueue() {
//校验是否在队列里
if (!mEnqueued) {
//包装成runable 然后再线程池执行该runabl人,runable 持有 WorkContinuationImpl
EnqueueRunnable runnable = new EnqueueRunnable(this);
mWorkManagerImpl.getWorkTaskExecutor().executeOnBackgroundThread(runnable);
mOperation = runnable.getOperation();
} else {
Logger.get().warning(TAG,
String.format("Already enqueued work ids (%s)", TextUtils.join(", ", mIds)));
}
return mOperation;
}
//上述方法调用线程池执行runable,那么我们看下 runable 的run方法
@Override
public void run() {
try {
//检查是否有循环
if (mWorkContinuation.hasCycles()) {
throw new IllegalStateException(
String.format("WorkContinuation has cycles (%s)", mWorkContinuation));
}
//添加到数据库
boolean needsScheduling = addToDatabase();
//如果返回为真 则 启动RescheduleReceiver 然后调用scheduleWorkInBackground()
if (needsScheduling) {
// Enable RescheduleReceiver, only when there are Worker's that need scheduling.
final Context context =
mWorkContinuation.getWorkManagerImpl().getApplicationContext();
PackageManagerHelper.setComponentEnabled(context, RescheduleReceiver.class, true); //后台执行
scheduleWorkInBackground();
}
mOperation.setState(Operation.SUCCESS);
} catch (Throwable exception) {
mOperation.setState(new Operation.State.FAILURE(exception));
}
}
//我们先看下addToDatabase
//获取数据库 然后调用了 processContinuation
@VisibleForTesting
public boolean addToDatabase() {
WorkManagerImpl workManagerImpl = mWorkContinuation.getWorkManagerImpl();
WorkDatabase workDatabase = workManagerImpl.getWorkDatabase();
workDatabase.beginTransaction();
try {
boolean needsScheduling = processContinuation(mWorkContinuation);
workDatabase.setTransactionSuccessful();
return needsScheduling;
} finally {
workDatabase.endTransaction();
}
}
private static boolean processContinuation(@NonNull WorkContinuationImpl workContinuation) {
boolean needsScheduling = false;
List<WorkContinuationImpl> parents = workContinuation.getParents();
if (parents != null) {
for (WorkContinuationImpl parent : parents) {
if (!parent.isEnqueued()) {
needsScheduling |= processContinuation(parent);
} else {
Logger.get().warning(TAG, String.format("Already enqueued work ids (%s).",
TextUtils.join(", ", parent.getIds())));
}
}
}
//加入 parents 为空,会调用enqueueContinuation
needsScheduling |= enqueueContinuation(workContinuation);
return needsScheduling;
}
//
private static boolean enqueueContinuation(@NonNull WorkContinuationImpl workContinuation) {
Set<String> prerequisiteIds = WorkContinuationImpl.prerequisitesFor(workContinuation);
boolean needsScheduling = enqueueWorkWithPrerequisites(
workContinuation.getWorkManagerImpl(),
workContinuation.getWork(),
prerequisiteIds.toArray(new String[0]),
workContinuation.getName(),
workContinuation.getExistingWorkPolicy());
workContinuation.markEnqueued();
return needsScheduling;
}
private static boolean enqueueWorkWithPrerequisites(
WorkManagerImpl workManagerImpl,
@NonNull List<? extends WorkRequest> workList,
String[] prerequisiteIds,
String name,
ExistingWorkPolicy existingWorkPolicy) {
boolean needsScheduling = false;
long currentTimeMillis = System.currentTimeMillis();
WorkDatabase workDatabase = workManagerImpl.getWorkDatabase();
boolean hasPrerequisite = (prerequisiteIds != null && prerequisiteIds.length > 0);
boolean hasCompletedAllPrerequisites = true;
boolean hasFailedPrerequisites = false;
boolean hasCancelledPrerequisites = false;
if (hasPrerequisite) {
for (String id : prerequisiteIds) {
WorkSpec prerequisiteWorkSpec = workDatabase.workSpecDao().getWorkSpec(id);
if (prerequisiteWorkSpec == null) {
Logger.get().error(TAG,
String.format("Prerequisite %s doesn't exist; not enqueuing", id));
return false;
}
WorkInfo.State prerequisiteState = prerequisiteWorkSpec.state;
hasCompletedAllPrerequisites &= (prerequisiteState == SUCCEEDED);
if (prerequisiteState == FAILED) {
hasFailedPrerequisites = true;
} else if (prerequisiteState == CANCELLED) {
hasCancelledPrerequisites = true;
}
}
}
boolean isNamed = !TextUtils.isEmpty(name);
boolean shouldApplyExistingWorkPolicy = isNamed && !hasPrerequisite;
if (shouldApplyExistingWorkPolicy) {
List<WorkSpec.IdAndState> existingWorkSpecIdAndStates =
workDatabase.workSpecDao().getWorkSpecIdAndStatesForName(name);
if (!existingWorkSpecIdAndStates.isEmpty()) {
// 策略处理
if (existingWorkPolicy == APPEND || existingWorkPolicy == APPEND_OR_REPLACE) {
DependencyDao dependencyDao = workDatabase.dependencyDao();
List<String> newPrerequisiteIds = new ArrayList<>();
for (WorkSpec.IdAndState idAndState : existingWorkSpecIdAndStates) {
if (!dependencyDao.hasDependents(idAndState.id)) {
hasCompletedAllPrerequisites &= (idAndState.state == SUCCEEDED);
if (idAndState.state == FAILED) {
hasFailedPrerequisites = true;
} else if (idAndState.state == CANCELLED) {
hasCancelledPrerequisites = true;
}
newPrerequisiteIds.add(idAndState.id);
}
}
if (existingWorkPolicy == APPEND_OR_REPLACE) {
if (hasCancelledPrerequisites || hasFailedPrerequisites) {
// Delete all WorkSpecs with this name
WorkSpecDao workSpecDao = workDatabase.workSpecDao();
List<WorkSpec.IdAndState> idAndStates =
workSpecDao.getWorkSpecIdAndStatesForName(name);
for (WorkSpec.IdAndState idAndState : idAndStates) {
workSpecDao.delete(idAndState.id);
}
// Treat this as a new chain of work.
newPrerequisiteIds = Collections.emptyList();
hasCancelledPrerequisites = false;
hasFailedPrerequisites = false;
}
}
prerequisiteIds = newPrerequisiteIds.toArray(prerequisiteIds);
hasPrerequisite = (prerequisiteIds.length > 0);
} else {
if (existingWorkPolicy == KEEP) {
for (WorkSpec.IdAndState idAndState : existingWorkSpecIdAndStates) {
if (idAndState.state == ENQUEUED || idAndState.state == RUNNING) {
return false;
}
}
}
CancelWorkRunnable.forName(name, workManagerImpl, false).run();
needsScheduling = true;
WorkSpecDao workSpecDao = workDatabase.workSpecDao();
for (WorkSpec.IdAndState idAndState : existingWorkSpecIdAndStates) {
workSpecDao.delete(idAndState.id);
}
}
}
}
//遍历workRequest
for (WorkRequest work : workList) {
//得到work 参数
WorkSpec workSpec = work.getWorkSpec();
if (hasPrerequisite && !hasCompletedAllPrerequisites) {
if (hasFailedPrerequisites) {
workSpec.state = FAILED;
} else if (hasCancelledPrerequisites) {
workSpec.state = CANCELLED;
} else {
workSpec.state = BLOCKED;
}
} else {
//如果是周期任务则记录当前的开始时间
if (!workSpec.isPeriodic()) {
workSpec.periodStartTime = currentTimeMillis;
} else {
//否则开始时间记为0
workSpec.periodStartTime = 0L;
}
}
//API 23 =< <=25 tryDelegateConstrainedWorkSpec
//api <=22且usesScheduler 也调用tryDelegateConstrainedWorkSpec
if (Build.VERSION.SDK_INT >= WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL
&& Build.VERSION.SDK_INT <= 25) {
tryDelegateConstrainedWorkSpec(workSpec);
} else if (Build.VERSION.SDK_INT <= WorkManagerImpl.MAX_PRE_JOB_SCHEDULER_API_LEVEL
&& usesScheduler(workManagerImpl, Schedulers.GCM_SCHEDULER)) {
tryDelegateConstrainedWorkSpec(workSpec);
}
if (workSpec.state == ENQUEUED) {
needsScheduling = true;
}
//放入数据库
workDatabase.workSpecDao().insertWorkSpec(workSpec);
if (hasPrerequisite) {
for (String prerequisiteId : prerequisiteIds) {
Dependency dep = new Dependency(work.getStringId(), prerequisiteId);
workDatabase.dependencyDao().insertDependency(dep);
}
}
for (String tag : work.getTags()) {
workDatabase.workTagDao().insert(new WorkTag(tag, work.getStringId()));
}
if (isNamed) {
workDatabase.workNameDao().insert(new WorkName(name, work.getStringId()));
}
}
return needsScheduling;
}
//电量依赖和 空间依赖 23-25版本ConstraintTrackingWorker 而 26 以上依赖JobScheduler
private static void tryDelegateConstrainedWorkSpec(WorkSpec workSpec) {
// requiresBatteryNotLow and requiresStorageNotLow require API 26 for JobScheduler.
// Delegate to ConstraintTrackingWorker between API 23-25.
Constraints constraints = workSpec.constraints;
if (constraints.requiresBatteryNotLow() || constraints.requiresStorageNotLow()) {
String workerClassName = workSpec.workerClassName;
Data.Builder builder = new Data.Builder();
// Copy all arguments
builder.putAll(workSpec.input)
.putString(ARGUMENT_CLASS_NAME, workerClassName);
workSpec.workerClassName = ConstraintTrackingWorker.class.getName();
workSpec.input = builder.build();
}
}
/**
* Schedules work on the background scheduler.
*/
@VisibleForTesting
public void scheduleWorkInBackground() {
WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();
Schedulers.schedule(
workManager.getConfiguration(),
workManager.getWorkDatabase(),
workManager.getSchedulers());
}
复制代码
最后会调到 Schedulers.schedule,将配置、数据库,及scheluer列表传入
我们先看下 schedulers 列表,是workManagerImpl的参数
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public WorkManagerImpl(
@NonNull Context context,
@NonNull Configuration configuration,
@NonNull TaskExecutor workTaskExecutor,
@NonNull WorkDatabase database) {
Context applicationContext = context.getApplicationContext();
Logger.setLogger(new Logger.LogcatLogger(configuration.getMinimumLoggingLevel()));
//生成schedulers 调用了 createSchedulers
List<Scheduler> schedulers =
createSchedulers(applicationContext, configuration, workTaskExecutor);
Processor processor = new Processor(
context,
configuration,
workTaskExecutor,
database,
schedulers);
internalInit(context, configuration, workTaskExecutor, database, schedulers, processor);
}
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@NonNull
public List<Scheduler> createSchedulers(
@NonNull Context context,
@NonNull Configuration configuration,
@NonNull TaskExecutor taskExecutor) {
//列表项第一个是 createBestAvailableBackgroundScheduler
//列表项第二个是 GreedyScheduler
return Arrays.asList(
Schedulers.createBestAvailableBackgroundScheduler(context, this),
new GreedyScheduler(context, configuration, taskExecutor, this));
}
//Shedulers.java
@NonNull
static Scheduler createBestAvailableBackgroundScheduler(
@NonNull Context context,
@NonNull WorkManagerImpl workManager) {
Scheduler scheduler;
//23及以上 采用SystemJobScheduler,同时依赖SystemJobService
if (Build.VERSION.SDK_INT >= WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL) {
scheduler = new SystemJobScheduler(context, workManager);
setComponentEnabled(context, SystemJobService.class, true);
Logger.get().debug(TAG, "Created SystemJobScheduler and enabled SystemJobService");
} else {
//23以下 采用反射获取GcmScheduler,如果获取不到,采用SystemAlarmScheduler,并用SystemAlarmService
scheduler = tryCreateGcmBasedScheduler(context);
if (scheduler == null) {
scheduler = new SystemAlarmScheduler(context);
setComponentEnabled(context, SystemAlarmService.class, true);
Logger.get().debug(TAG, "Created SystemAlarmScheduler");
}
}
return scheduler;
}
@Nullable
private static Scheduler tryCreateGcmBasedScheduler(@NonNull Context context) {
try {
Class<?> klass = Class.forName(GCM_SCHEDULER);
Scheduler scheduler =
(Scheduler) klass.getConstructor(Context.class).newInstance(context);
Logger.get().debug(TAG, String.format("Created %s", GCM_SCHEDULER));
return scheduler;
} catch (Throwable throwable) {
Logger.get().debug(TAG, "Unable to create GCM Scheduler", throwable);
return null;
}
}
复制代码
约束是如何生效的?我们知道最后调用是 Sheduler.shedule,我们看下这个方法:
@Override
public void schedule(@NonNull WorkSpec... workSpecs) {
if (mIsMainProcess == null) {
mIsMainProcess = TextUtils.equals(mContext.getPackageName(), getProcessName());
}
if (!mIsMainProcess) {
Logger.get().info(TAG, "Ignoring schedule request in non-main process");
return;
}
registerExecutionListenerIfNeeded();
//有约束的work参数集合
Set<WorkSpec> constrainedWorkSpecs = new HashSet<>();
//有约束的workId集合
Set<String> constrainedWorkSpecIds = new HashSet<>();
for (WorkSpec workSpec : workSpecs) {
long nextRunTime = workSpec.calculateNextRunTime();
long now = System.currentTimeMillis();
if (workSpec.state == WorkInfo.State.ENQUEUED) {
if (now < nextRunTime) {
// Future work
if (mDelayedWorkTracker != null) {
mDelayedWorkTracker.schedule(workSpec);
}
} else if (workSpec.hasConstraints()) {
if (SDK_INT >= 23 && workSpec.constraints.requiresDeviceIdle()) {
//23以上忽略设备空闲的约束
Logger.get().debug(TAG,
String.format("Ignoring WorkSpec %s, Requires device idle.",
workSpec));
} else if (SDK_INT >= 24 && workSpec.constraints.hasContentUriTriggers()) {
//24以上忽略contentUri 约束
Logger.get().debug(TAG,
String.format("Ignoring WorkSpec %s, Requires ContentUri triggers.",
workSpec));
} else {
//将约束添加到集合
constrainedWorkSpecs.add(workSpec);
//将任务id 添加到集合
constrainedWorkSpecIds.add(workSpec.id);
}
} else {
//没有约束 直接调用startWork
Logger.get().debug(TAG, String.format("Starting work for %s", workSpec.id));
mWorkManagerImpl.startWork(workSpec.id);
}
}
}
// onExecuted() which is called on the main thread also modifies the list of mConstrained
// WorkSpecs. Therefore we need to lock here.
synchronized (mLock) {
if (!constrainedWorkSpecs.isEmpty()) {
Logger.get().debug(TAG, String.format("Starting tracking for [%s]",
TextUtils.join(",", constrainedWorkSpecIds)));
mConstrainedWorkSpecs.addAll(constrainedWorkSpecs);
mWorkConstraintsTracker.replace(mConstrainedWorkSpecs);
}
}
}
复制代码
有约束的情况下并没有执行startwork,我们先假设没有约束,看下startwork
/**
* @param workSpecId The {@link WorkSpec} id to start
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public void startWork(@NonNull String workSpecId) {
startWork(workSpecId, null);
}
/**
* @param workSpecId The {@link WorkSpec} id to start
* @param runtimeExtras The {@link WorkerParameters.RuntimeExtras} associated with this work
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public void startWork(
@NonNull String workSpecId,
@Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
//调用后台线程 执行封装成的runable
mWorkTaskExecutor
.executeOnBackgroundThread(
new StartWorkRunnable(this, workSpecId, runtimeExtras));
}
//StartWorkRunnable.java 调用了 getProcessor.startWork
@Override
public void run() {
mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras);
}
//Processor.java 包装成workWrapper 然后调用线程池执行
public boolean startWork(
@NonNull String id,
@Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
WorkerWrapper workWrapper;
synchronized (mLock) {
// Work may get triggered multiple times if they have passing constraints
// and new work with those constraints are added.
if (isEnqueued(id)) {
Logger.get().debug(
TAG,
String.format("Work %s is already enqueued for processing", id));
return false;
}
workWrapper =
new WorkerWrapper.Builder(
mAppContext,
mConfiguration,
mWorkTaskExecutor,
this,
mWorkDatabase,
id)
.withSchedulers(mSchedulers)
.withRuntimeExtras(runtimeExtras)
.build();
ListenableFuture<Boolean> future = workWrapper.getFuture();
future.addListener(
new FutureListener(this, id, future),
mWorkTaskExecutor.getMainThreadExecutor());
mEnqueuedWorkMap.put(id, workWrapper);
}
mWorkTaskExecutor.getBackgroundExecutor().execute(workWrapper);
Logger.get().debug(TAG, String.format("%s: processing %s", getClass().getSimpleName(), id));
return true;
}
//WorkRapper.java run 方法
@WorkerThread
@Override
public void run() {
mTags = mWorkTagDao.getTagsForWorkSpecId(mWorkSpecId);
mWorkDescription = createWorkDescription(mTags);
//调用runworker
runWorker();
}
private void runWorker() {
if (tryCheckForInterruptionAndResolve()) {
return;
}
mWorkDatabase.beginTransaction();
try {
//获取任务参数
mWorkSpec = mWorkSpecDao.getWorkSpec(mWorkSpecId);
if (mWorkSpec == null) {
Logger.get().error(
TAG,
String.format("Didn't find WorkSpec for id %s", mWorkSpecId));
resolve(false);
return;
}
if (mWorkSpec.state != ENQUEUED) {
resolveIncorrectStatus();
mWorkDatabase.setTransactionSuccessful();
Logger.get().debug(TAG,
String.format("%s is not in ENQUEUED state. Nothing more to do.",
mWorkSpec.workerClassName));
return;
}
//周期性任务 和 延时任务处理
if (mWorkSpec.isPeriodic() || mWorkSpec.isBackedOff()) {
long now = System.currentTimeMillis();
//是不是第一次执行 并计算下次要执行任务的时间
boolean isFirstRun = mWorkSpec.periodStartTime == 0;
if (!isFirstRun && now < mWorkSpec.calculateNextRunTime()) {
Logger.get().debug(TAG,
String.format(
"Delaying execution for %s because it is being executed "
+ "before schedule.",
mWorkSpec.workerClassName));
resolve(true);
return;
}
}
mWorkDatabase.setTransactionSuccessful();
} finally {
mWorkDatabase.endTransaction();
}
//携带的参数处理
Data input;
if (mWorkSpec.isPeriodic()) {
input = mWorkSpec.input;
} else {
InputMergerFactory inputMergerFactory = mConfiguration.getInputMergerFactory();
String inputMergerClassName = mWorkSpec.inputMergerClassName;
InputMerger inputMerger =
inputMergerFactory.createInputMergerWithDefaultFallback(inputMergerClassName);
if (inputMerger == null) {
Logger.get().error(TAG, String.format("Could not create Input Merger %s",
mWorkSpec.inputMergerClassName));
setFailedAndResolve();
return;
}
List<Data> inputs = new ArrayList<>();
inputs.add(mWorkSpec.input);
inputs.addAll(mWorkSpecDao.getInputsFromPrerequisites(mWorkSpecId));
input = inputMerger.merge(inputs);
}
WorkerParameters params = new WorkerParameters(
UUID.fromString(mWorkSpecId),
input,
mTags,
mRuntimeExtras,
mWorkSpec.runAttemptCount,
mConfiguration.getExecutor(),
mWorkTaskExecutor,
mConfiguration.getWorkerFactory(),
new WorkProgressUpdater(mWorkDatabase, mWorkTaskExecutor),
new WorkForegroundUpdater(mWorkDatabase, mForegroundProcessor, mWorkTaskExecutor));
//执行任务前的判断 是否为空 是否正在使用
if (mWorker == null) {
mWorker = mConfiguration.getWorkerFactory().createWorkerWithDefaultFallback(
mAppContext,
mWorkSpec.workerClassName,
params);
}
if (mWorker == null) {
Logger.get().error(TAG,
String.format("Could not create Worker %s", mWorkSpec.workerClassName));
setFailedAndResolve();
return;
}
if (mWorker.isUsed()) {
Logger.get().error(TAG,
String.format("Received an already-used Worker %s; WorkerFactory should return "
+ "new instances",
mWorkSpec.workerClassName));
setFailedAndResolve();
return;
}
mWorker.setUsed();
//尝试运行任务
if (trySetRunning()) {
if (tryCheckForInterruptionAndResolve()) {
return;
}
final SettableFuture<ListenableWorker.Result> future = SettableFuture.create();
//调用startwork 方法 运行在主线程
mWorkTaskExecutor.getMainThreadExecutor()
.execute(new Runnable() {
@Override
public void run() {
try {
Logger.get().debug(TAG, String.format("Starting work for %s",
mWorkSpec.workerClassName));
mInnerFuture = mWorker.startWork();
future.setFuture(mInnerFuture);
} catch (Throwable e) {
future.setException(e);
}
}
});
//运行结果监听
final String workDescription = mWorkDescription;
future.addListener(new Runnable() {
@Override
@SuppressLint("SyntheticAccessor")
public void run() {
try {
ListenableWorker.Result result = future.get();
if (result == null) {
Logger.get().error(TAG, String.format(
"%s returned a null result. Treating it as a failure.",
mWorkSpec.workerClassName));
} else {
Logger.get().debug(TAG, String.format("%s returned a %s result.",
mWorkSpec.workerClassName, result));
mResult = result;
}
} catch (CancellationException exception) {
// Cancellations need to be treated with care here because innerFuture
// cancellations will bubble up, and we need to gracefully handle that.
Logger.get().info(TAG, String.format("%s was cancelled", workDescription),
exception);
} catch (InterruptedException | ExecutionException exception) {
Logger.get().error(TAG,
String.format("%s failed because it threw an exception/error",
workDescription), exception);
} finally {
onWorkFinished();
}
}
}, mWorkTaskExecutor.getBackgroundExecutor());
} else {
resolveIncorrectStatus();
}
}
复制代码
任务约束如何生效的?
上文我们看到GreedyScheduler 在有约束的情况下并没有调用startwork,那什么时候会调用
//精简过省略部分代码,实现了 WorkConstraintsCallback 接口
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class GreedyScheduler implements Scheduler, WorkConstraintsCallback{
//约束的总处理
private final WorkConstraintsTracker mWorkConstraintsTracker;
//约束的条件
private final Set<WorkSpec> mConstrainedWorkSpecs = new HashSet<>();
public GreedyScheduler(
@NonNull Context context,
@NonNull Configuration configuration,
@NonNull TaskExecutor taskExecutor,
@NonNull WorkManagerImpl workManagerImpl) {
//构造函数初始初始化约束总处理
mWorkConstraintsTracker = new WorkConstraintsTracker(context, taskExecutor, this);
}
//实现了接口所有约束都符合的回调 会调用stratwork
@Override
public void onAllConstraintsMet(@NonNull List<String> workSpecIds) {
for (String workSpecId : workSpecIds) {
Logger.get().debug(
TAG,
String.format("Constraints met: Scheduling work ID %s", workSpecId));
mWorkManagerImpl.startWork(workSpecId);
}
}
//实现了接口所有约束都不符合的回调 会调用stopwork
@Override
public void onAllConstraintsNotMet(@NonNull List<String> workSpecIds) {
for (String workSpecId : workSpecIds) {
Logger.get().debug(TAG,
String.format("Constraints not met: Cancelling work ID %s", workSpecId));
mWorkManagerImpl.stopWork(workSpecId);
}
}
}
复制代码
GreedyScheduler 实现了 WorkConstraintsCallback 接口,在所有约束满足的时候会回调onAllConstraintsMet,然后调用startwork