Android Jetpack系列(九):WorkManager
WorkManager的原理
上一篇讲解了如何使用WorkManager;本篇我们就按照上一篇的使用步骤来分析源码
生成WorkRequest的源码
第一步生成一个待执行的request请求
val request = OneTimeWorkRequestBuilder<CountWorker>()
.setConstraints(constraints)
.addTag("tagCountWorker")
.setInputData(Data.Builder().putString("parameter1", "value of parameter1").build())
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 1, TimeUnit.HOURS)
.build()
我们看类WorkRequest的源码部分:
public abstract class WorkRequest {
private @NonNull UUID mId;
private @NonNull WorkSpec mWorkSpec;
private @NonNull Set<String> mTags;
public abstract static class Builder<B extends Builder<?, ?>, W extends WorkRequest> {
boolean mBackoffCriteriaSet = false;
UUID mId;
WorkSpec mWorkSpec;
Set<String> mTags = new HashSet<>();
Class<? extends ListenableWorker> mWorkerClass;
Builder(@NonNull Class<? extends ListenableWorker> workerClass) {
mId = UUID.randomUUID();
mWorkerClass = workerClass;
mWorkSpec = new WorkSpec(mId.toString(), workerClass.getName());
addTag(workerClass.getName());
}
public final @NonNull W build() {
W returnValue = buildInternal();
// Create a new id and WorkSpec so this WorkRequest.Builder can be used multiple times.
mId = UUID.randomUUID();
mWorkSpec = new WorkSpec(mWorkSpec);
mWorkSpec.id = mId.toString();
return returnValue;
}
abstract @NonNull W buildInternal();
abstract @NonNull B getThis();
}
}
- 它采用了建造者设计模式,内部成员变量
mWorkSpec
使用的是Room数据库,可以持久化存储,除非被clear data,所以能保证系统即使被重启,也可以确保任务得到执行。我们查看WorkSpec
的源码:
@Entity(
indices = {
@Index(value = {"schedule_requested_at"}),
@Index(value = {"period_start_time"})
}
)
public final class WorkSpec {
private static final String TAG = Logger.tagWithPrefix("WorkSpec");
public static final long SCHEDULE_NOT_REQUESTED_YET = -1;
@ColumnInfo(name = "id")
@PrimaryKey
@NonNull
public String id;
@ColumnInfo(name = "state")
@NonNull
public WorkInfo.State state = ENQUEUED;
@ColumnInfo(name = "worker_class_name")
@NonNull
public String workerClassName;
@ColumnInfo(name = "input_merger_class_name")
public String inputMergerClassName;
@ColumnInfo(name = "input")
@NonNull
public Data input = Data.EMPTY;
@ColumnInfo(name = "output")
@NonNull
public Data output = Data.EMPTY;
@ColumnInfo(name = "initial_delay")
public long initialDelay;
@ColumnInfo(name = "interval_duration")
public long intervalDuration;
@ColumnInfo(name = "flex_duration")
public long flexDuration;
@Embedded
@NonNull
public Constraints constraints = Constraints.NONE;
@ColumnInfo(name = "run_attempt_count")
@IntRange(from = 0)
public int runAttemptCount;
@ColumnInfo(name = "backoff_policy")
@NonNull
public BackoffPolicy backoffPolicy = BackoffPolicy.EXPONENTIAL;
@ColumnInfo(name = "backoff_delay_duration")
public long backoffDelayDuration = WorkRequest.DEFAULT_BACKOFF_DELAY_MILLIS;
@ColumnInfo(name = "period_start_time")
public long periodStartTime;
@ColumnInfo(name = "minimum_retention_duration")
public long minimumRetentionDuration;
@ColumnInfo(name = "schedule_requested_at")
public long scheduleRequestedAt = SCHEDULE_NOT_REQUESTED_YET;
/**
* This is {@code true} when the WorkSpec needs to be hosted by a foreground service.
*/
@ColumnInfo(name = "run_in_foreground")
public boolean runInForeground;
public WorkSpec(@NonNull String id, @NonNull String workerClassName) {
this.id = id;
this.workerClassName = workerClassName;
}
public WorkSpec(@NonNull WorkSpec other) {
id = other.id;
workerClassName = other.workerClassName;
state = other.state;
inputMergerClassName = other.inputMergerClassName;
input = new Data(other.input);
output = new Data(other.output);
initialDelay = other.initialDelay;
intervalDuration = other.intervalDuration;
flexDuration = other.flexDuration;
constraints = new Constraints(other.constraints);
runAttemptCount = other.runAttemptCount;
backoffPolicy = other.backoffPolicy;
backoffDelayDuration = other.backoffDelayDuration;
periodStartTime = other.periodStartTime;
minimumRetentionDuration = other.minimumRetentionDuration;
scheduleRequestedAt = other.scheduleRequestedAt;
runInForeground = other.runInForeground;
}
}
我们看到上面使用了Room的Entity注解来注解WorkSpec,说明这是一个持有化存储的类,会被存储在数据库中; 它里面存储了一个WorkRequest的几乎所有信息,包括唯一标识id,workerClassName,input,output,constraints等,这些信息在我们生成WorkRequest中都有涉及
WorkManager.getInstance(this)源码
WorkManager是一个抽象类,它的实现类是WorkManagerImpl
,采用单例模式返回WorkManagerImpl
对象; 下面注释1的地方调用getInstance
其实返回值已经不为空了,下面我们来分析:
public static @NonNull WorkManager getInstance(@NonNull Context context) {
return WorkManagerImpl.getInstance(context);
}
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static @NonNull WorkManagerImpl getInstance(@NonNull Context context) {
synchronized (sLock) {
//-----1-----
WorkManagerImpl instance = getInstance();
if (instance == null) {
Context appContext = context.getApplicationContext();
if (appContext instanceof Configuration.Provider) {
initialize(
appContext,
((Configuration.Provider) appContext).getWorkManagerConfiguration());
instance = getInstance(appContext);
} else {
throw new IllegalStateException("WorkManager is not initialized properly. You "
+ "have explicitly disabled WorkManagerInitializer in your manifest, "
+ "have not manually called WorkManager#initialize at this point, and "
+ "your Application does not implement Configuration.Provider.");
}
}
return instance;
}
}
getInstance
源码如下,我们看到有两个WorkManagerImpl
对象,这里sDelegatedInstance
已经不为空,它是在哪里赋值的呢?
private static WorkManagerImpl sDelegatedInstance = null;
private static WorkManagerImpl sDefaultInstance = null;
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static @Nullable WorkManagerImpl getInstance() {
synchronized (sLock) {
if (sDelegatedInstance != null) {
return sDelegatedInstance;
}
return sDefaultInstance;
}
}
在源码中有这么一个类WorkManagerInitializer
,它继承ContentProvider
,源码如下:
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class WorkManagerInitializer extends ContentProvider {
@Override
public boolean onCreate() {
// Initialize WorkManager with the default configuration.
WorkManager.initialize(getContext(), new Configuration.Builder().build());
return true;
}
}
我们都知道ContentProvider的创建时机是在程序的入口ActivityThread.main中,通过调用thread.attach最终回调到activityThread.handleBindApplication方法,在这个方法中ActivityThread会创建Application对象并加载ContentProvider,但是有一点要注意,就是会先加载ContentProvider然后在调用Application的onCreate方法
这样在程序启动前就会调用WorkManagerInitializer的onCreate方法,从而调用WorkManager的initialize方法,我们来看一下这个方法:
public boolean onCreate() {
// Initialize WorkManager with the default configuration.
//-----1-----
WorkManager.initialize(getContext(), new Configuration.Builder().build());
return true;
}
public static void initialize(@NonNull Context context, @NonNull Configuration configuration) {
WorkManagerImpl.initialize(context, configuration);
}
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static void initialize(@NonNull Context context, @NonNull Configuration configuration) {
synchronized (sLock) {
if (sDelegatedInstance != null && sDefaultInstance != null) {
throw new IllegalStateException("WorkManager is already initialized. Did you "
+ "try to initialize it manually without disabling "
+ "WorkManagerInitializer? See "
+ "WorkManager#initialize(Context, Configuration) or the class level "
+ "Javadoc for more information.");
}
if (sDelegatedInstance == null) {
context = context.getApplicationContext();
if (sDefaultInstance == null) {
//-----2-----
sDefaultInstance = new WorkManagerImpl(
context,
configuration,
new WorkManagerTaskExecutor(configuration.getTaskExecutor()));
}
sDelegatedInstance = sDefaultInstance;
}
}
}
在注释1的地方,传入了一个参数Configuration
, 下面我们先来看一下Configuration
的源码:
public final class Configuration {
public static final int MIN_SCHEDULER_LIMIT = 20;
final @NonNull Executor mExecutor;
final @NonNull Executor mTaskExecutor;
final @NonNull WorkerFactory mWorkerFactory;
final @NonNull InputMergerFactory mInputMergerFactory;
final int mLoggingLevel;
final int mMinJobSchedulerId;
final int mMaxJobSchedulerId;
final int mMaxSchedulerLimit;
private final boolean mIsUsingDefaultTaskExecutor;
Configuration(@NonNull Configuration.Builder builder) {
if (builder.mExecutor == null) {
mExecutor = createDefaultExecutor();
} else {
mExecutor = builder.mExecutor;
}
if (builder.mTaskExecutor == null) {
mIsUsingDefaultTaskExecutor = true;
mTaskExecutor = createDefaultExecutor();
} else {
mIsUsingDefaultTaskExecutor = false;
mTaskExecutor = builder.mTaskExecutor;
}
if (builder.mWorkerFactory == null) {
mWorkerFactory = WorkerFactory.getDefaultWorkerFactory();
} else {
mWorkerFactory = builder.mWorkerFactory;
}
if (builder.mInputMergerFactory == null) {
mInputMergerFactory = InputMergerFactory.getDefaultInputMergerFactory();
} else {
mInputMergerFactory = builder.mInputMergerFactory;
}
mLoggingLevel = builder.mLoggingLevel;
mMinJobSchedulerId = builder.mMinJobSchedulerId;
mMaxJobSchedulerId = builder.mMaxJobSchedulerId;
mMaxSchedulerLimit = builder.mMaxSchedulerLimit;
}
}
我们截取了Configuration部分重要的源码; 它是采用的建造者模式,从上面的源码看出,它是一个配置类,里面保存了很多配置信息,比如Executor,WorkerFactory,InputMergerFactory等,这里的Executor默认是一个采用Executors.newFixedThreadPool的线程池
然后我们接着看上面注释2的地方使用构造方法初始化了sDefaultInstance,并将其赋值给了sDelegatedInstance,所以程序在一开始就初始化了这两个对象,当我们调用的时候就不为空了
我们看内部实现:
sDefaultInstance = new WorkManagerImpl(
context,
configuration,
//-----1-----
new WorkManagerTaskExecutor(configuration.getTaskExecutor()));
public WorkManagerImpl(
@NonNull Context context,
@NonNull Configuration configuration,
@NonNull TaskExecutor workTaskExecutor) {
this(context,
configuration,
workTaskExecutor,
//-----2-----
context.getResources().getBoolean(R.bool.workmanager_test_configuration));
}
public WorkManagerImpl(
@NonNull Context context,
@NonNull Configuration configuration,
@NonNull TaskExecutor workTaskExecutor,
boolean useTestDatabase) {
this(context,
configuration,
workTaskExecutor,
//-----3-----
WorkDatabase.create(
context.getApplicationContext(),
workTaskExecutor.getBackgroundExecutor(),
useTestDatabase)
);
}
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()));
//-----4-----
List<Scheduler> schedulers = createSchedulers(applicationContext, workTaskExecutor);
/-----5-----
Processor processor = new Processor(
context,
configuration,
workTaskExecutor,
database,
schedulers);
//-----6-----
internalInit(context, configuration, workTaskExecutor, database, schedulers, processor);
}
-
注释1处提供了一个新的类WorkManagerTaskExecutor,它内部提供了主线程和子线程切换以及执行的操作
-
注释2配置了一个标志位,用来标记是否采用测试数据库,后面会使用到这个参数
-
注释3就开始创建数据库WorkDatabase,它的源码如下,我们看到如果useTestDatabase为true的话,就会采用Room.inMemoryDatabaseBuilder创建数据库,并且允许在主线程查询
@Database(entities = {
Dependency.class,
WorkSpec.class,
WorkTag.class,
SystemIdInfo.class,
WorkName.class,
WorkProgress.class,
Preference.class},
version = 10)
@TypeConverters(value = {Data.class, WorkTypeConverters.class})
public abstract class WorkDatabase extends RoomDatabase {
...
}
public static WorkDatabase create(
@NonNull final Context context,
@NonNull Executor queryExecutor,
boolean useTestDatabase) {
RoomDatabase.Builder<WorkDatabase> builder;
if (useTestDatabase) {
builder = Room.inMemoryDatabaseBuilder(context, WorkDatabase.class)
.allowMainThreadQueries();
} else {
String name = WorkDatabasePathHelper.getWorkDatabaseName();
builder = Room.databaseBuilder(context, WorkDatabase.class, name);
builder.openHelperFactory(new SupportSQLiteOpenHelper.Factory() {
@NonNull
@Override
public SupportSQLiteOpenHelper create(
@NonNull SupportSQLiteOpenHelper.Configuration configuration) {
SupportSQLiteOpenHelper.Configuration.Builder configBuilder =
SupportSQLiteOpenHelper.Configuration.builder(context);
configBuilder.name(configuration.name)
.callback(configuration.callback)
.noBackupDirectory(true);
FrameworkSQLiteOpenHelperFactory factory =
new FrameworkSQLiteOpenHelperFactory();
return factory.create(configBuilder.build());
}
});
}
}
- 注释4处调用了
createSchedulers
来创建一个Scheduler的List,后面会使用到
public List<Scheduler> createSchedulers(
@NonNull Context context,
@NonNull TaskExecutor taskExecutor) {
return Arrays.asList(
Schedulers.createBestAvailableBackgroundScheduler(context, this),
// Specify the task executor directly here as this happens before internalInit.
// GreedyScheduler creates ConstraintTrackers and controllers eagerly.
new GreedyScheduler(context, taskExecutor, this));
}
-
注释5处创建了一个Process对象,官方的解释是它可以根据需要智能地调度和执行工作,完美看到它几乎把所有的配置信息都保存在对象中,包括配置configuration,workTaskExecutor, 数据库database,调度schedulers.
-
注释6处就是将上面创建的对象赋值到WorkManagerImpl成员变量中,到此处WorkManager.getInstance(context)创建实例的过程就结束了
enqueue(request)源码分析
enqueue实际调用的是WorkContinuationImpl的enqueue方法,源码如下:
public @NonNull Operation enqueue() {
// Only enqueue if not already enqueued.
if (!mEnqueued) {
// The runnable walks the hierarchy of the continuations
// and marks them enqueued using the markEnqueued() method, parent first.
//-----1-----
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;
}
- 在注释1的地方生成了一个
EnqueueRunnable
,它继承Runnable,然后通过之前在Configuration
中创建的线程池,将EnqueueRunnable加入到线程池中执行,返回一个Operation
对象。我们关注一下EnqueueRunnable
的run方法:
public class EnqueueRunnable implements Runnable {
private static final String TAG = Logger.tagWithPrefix("EnqueueRunnable");
private final WorkContinuationImpl mWorkContinuation;
private final OperationImpl mOperation;
public EnqueueRunnable(@NonNull WorkContinuationImpl workContinuation) {
mWorkContinuation = workContinuation;
mOperation = new OperationImpl();
}
@Override
public void run() {
try {
if (mWorkContinuation.hasCycles()) {
throw new IllegalStateException(
String.format("WorkContinuation has cycles (%s)", mWorkContinuation));
}
boolean needsScheduling = addToDatabase();
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);
//-----1-----
scheduleWorkInBackground();
}
mOperation.setState(Operation.SUCCESS);
} catch (Throwable exception) {
mOperation.setState(new Operation.State.FAILURE(exception));
}
}
public Operation getOperation() {
return mOperation;
}
}
我们关注注释1处的scheduleWorkInBackground
方法:
public void scheduleWorkInBackground() {
WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();
Schedulers.schedule(
workManager.getConfiguration(),
workManager.getWorkDatabase(),
workManager.getSchedulers());
}
public static void schedule(
@NonNull Configuration configuration,
@NonNull WorkDatabase workDatabase,
List<Scheduler> schedulers) {
if (schedulers == null || schedulers.size() == 0) {
return;
}
WorkSpecDao workSpecDao = workDatabase.workSpecDao();
List<WorkSpec> eligibleWorkSpecs;
workDatabase.beginTransaction();
try {
eligibleWorkSpecs = workSpecDao.getEligibleWorkForScheduling(
configuration.getMaxSchedulerLimit());
if (eligibleWorkSpecs != null && eligibleWorkSpecs.size() > 0) {
long now = System.currentTimeMillis();
// Mark all the WorkSpecs as scheduled.
// Calls to Scheduler#schedule() could potentially result in more schedules
// on a separate thread. Therefore, this needs to be done first.
for (WorkSpec workSpec : eligibleWorkSpecs) {
workSpecDao.markWorkSpecScheduled(workSpec.id, now);
}
}
workDatabase.setTransactionSuccessful();
} finally {
workDatabase.endTransaction();
}
if (eligibleWorkSpecs != null && eligibleWorkSpecs.size() > 0) {
WorkSpec[] eligibleWorkSpecsArray = eligibleWorkSpecs.toArray(new WorkSpec[0]);
// Delegate to the underlying scheduler.
for (Scheduler scheduler : schedulers) {
//-----1-----
scheduler.schedule(eligibleWorkSpecsArray);
}
}
}
在注释1处调用了scheduler.schedule
方法,我们前面将GreedyScheduler
加入到队列中,我们这里主要分析该类:
public void schedule(@NonNull WorkSpec... workSpecs) {
if (mIsMainProcess == null) {
// The default process name is the package name.
mIsMainProcess = TextUtils.equals(mContext.getPackageName(), getProcessName());
}
if (!mIsMainProcess) {
Logger.get().info(TAG, "Ignoring schedule request in non-main process");
return;
}
registerExecutionListenerIfNeeded();
List<WorkSpec> constrainedWorkSpecs = new ArrayList<>();
List<String> constrainedWorkSpecIds = new ArrayList<>();
for (WorkSpec workSpec : workSpecs) {
//-----1-----
if (workSpec.state == WorkInfo.State.ENQUEUED
&& !workSpec.isPeriodic()
&& workSpec.initialDelay == 0L
&& !workSpec.isBackedOff()) {
if (workSpec.hasConstraints()) {
if (SDK_INT >= 23 && workSpec.constraints.requiresDeviceIdle()) {
// Ignore requests that have an idle mode constraint.
Logger.get().debug(TAG,
String.format("Ignoring WorkSpec %s, Requires device idle.",
workSpec));
} else if (SDK_INT >= 24 && workSpec.constraints.hasContentUriTriggers()) {
// Ignore requests that have content uri triggers.
Logger.get().debug(TAG,
String.format("Ignoring WorkSpec %s, Requires ContentUri triggers.",
workSpec));
} else {
constrainedWorkSpecs.add(workSpec);
constrainedWorkSpecIds.add(workSpec.id);
}
} else {
Logger.get().debug(TAG, String.format("Starting work for %s", workSpec.id));
//-----2-----
mWorkManagerImpl.startWork(workSpec.id);
}
}
}
synchronized (mLock) {
if (!constrainedWorkSpecs.isEmpty()) {
Logger.get().debug(TAG, String.format("Starting tracking for [%s]",
TextUtils.join(",", constrainedWorkSpecIds)));
mConstrainedWorkSpecs.addAll(constrainedWorkSpecs);
mWorkConstraintsTracker.replace(mConstrainedWorkSpecs);
}
}
}
首先在注释1处判断是否有约束条件,有的话将其任务和任务id加入到集合中,没有的话直接执行注释2处的startWork
方法。我们先关注没有约束条件时候的startWork
方法:
public void startWork(
@NonNull String workSpecId,
@Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
mWorkTaskExecutor
.executeOnBackgroundThread(
new StartWorkRunnable(this, workSpecId, runtimeExtras));
}
接着通过线程池调用了StartWorkRunnable
,它是一个Runnable:
public class StartWorkRunnable implements Runnable {
private WorkManagerImpl mWorkManagerImpl;
private String mWorkSpecId;
private WorkerParameters.RuntimeExtras mRuntimeExtras;
public StartWorkRunnable(
WorkManagerImpl workManagerImpl,
String workSpecId,
WorkerParameters.RuntimeExtras runtimeExtras) {
mWorkManagerImpl = workManagerImpl;
mWorkSpecId = workSpecId;
mRuntimeExtras = runtimeExtras;
}
@Override
public void run() {
//-----1-----
mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras);
}
}
接着上面注释1处的getProcessor().startWork
方法,Processor的startWorker源码如下:
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 (mEnqueuedWorkMap.containsKey(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);
}
//-----1-----
mWorkTaskExecutor.getBackgroundExecutor().execute(workWrapper);
Logger.get().debug(TAG, String.format("%s: processing %s", getClass().getSimpleName(), id));
return true;
}
它是一个work的包装类WorkWrapper,然后执行注释1处的方法, 我们看WorkWrapper
的源码:
public class WorkerWrapper implements Runnable {
public void run() {
mTags = mWorkTagDao.getTagsForWorkSpecId(mWorkSpecId);
mWorkDescription = createWorkDescription(mTags);
runWorker();
}
private void runWorker() {
...
if (mWorker == null) {
//-----1-----
mWorker = mConfiguration.getWorkerFactory().createWorkerWithDefaultFallback(
mAppContext,
mWorkSpec.workerClassName,
params);
}
mWorkTaskExecutor.getMainThreadExecutor()
.execute(new Runnable() {
@Override
public void run() {
try {
Logger.get().debug(TAG, String.format("Starting work for %s",
mWorkSpec.workerClassName));
//-----2-----
mInnerFuture = mWorker.startWork();
future.setFuture(mInnerFuture);
} catch (Throwable e) {
future.setException(e);
}
}
});
}
}
- 在注释1处通过类名,采用反射机制获取到ListenableWorker对象。其中Worker类继承自ListenableWorker类。
- 注释2调用ListenableWorker.startWork,它实际上是调用Worker类的startWork方法,Worker的源码如下:
public abstract class Worker extends ListenableWorker {
public abstract @NonNull Result doWork();
@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;
}
}
- 上面我们看到
startWork
方法实际上调用的是我们自己实现的doWork
方法,到这里终于调用了我们需要的逻辑。
有约束Constraints的任务是如何被执行的?
我反编译了我的apk,拿到了androidManifest.xml
:
<provider
//-----1-----
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="com.oman.forward.workmanager-init"
android:directBootAware="false"
android:exported="false"
android:multiprocess="true" />
<service
android:name="androidx.work.impl.background.systemalarm.SystemAlarmService"
android:directBootAware="false"
android:enabled="@bool/enable_system_alarm_service_default"
android:exported="false" />
<service
android:name="androidx.work.impl.background.systemjob.SystemJobService"
android:directBootAware="false"
android:enabled="@bool/enable_system_job_service_default"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" />
<service
android:name="androidx.work.impl.foreground.SystemForegroundService"
android:directBootAware="false"
android:enabled="@bool/enable_system_foreground_service_default"
android:exported="false" />
<receiver
android:name="androidx.work.impl.utils.ForceStopRunnable$BroadcastReceiver"
android:directBootAware="false"
android:enabled="true"
android:exported="false" />
<receiver
android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryChargingProxy"
android:directBootAware="false"
android:enabled="false"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
</intent-filter>
</receiver>
<receiver
android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryNotLowProxy"
android:directBootAware="false"
android:enabled="false"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BATTERY_OKAY" />
<action android:name="android.intent.action.BATTERY_LOW" />
</intent-filter>
</receiver>
<receiver
android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$StorageNotLowProxy"
android:directBootAware="false"
android:enabled="false"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.DEVICE_STORAGE_LOW" />
<action android:name="android.intent.action.DEVICE_STORAGE_OK" />
</intent-filter>
</receiver>
<receiver
//-----2-----
android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$NetworkStateProxy"
android:directBootAware="false"
android:enabled="false"
android:exported="false">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
<receiver
android:name="androidx.work.impl.background.systemalarm.RescheduleReceiver"
android:directBootAware="false"
android:enabled="false"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.TIME_SET" />
<action android:name="android.intent.action.TIMEZONE_CHANGED" />
</intent-filter>
</receiver>
<receiver
android:name="androidx.work.impl.background.systemalarm.ConstraintProxyUpdateReceiver"
android:directBootAware="false"
android:enabled="@bool/enable_system_alarm_service_default"
android:exported="false">
<intent-filter>
<action android:name="androidx.work.impl.background.systemalarm.UpdateProxies" />
</intent-filter>
</receiver>
<service
android:name="androidx.room.MultiInstanceInvalidationService"
android:exported="false" />
<provider
android:name="androidx.lifecycle.ProcessLifecycleOwnerInitializer"
android:authorities="com.oman.forward.lifecycle-process"
android:exported="false"
android:multiprocess="true" />
在最开始注释1的地方有一个provider,正是我们分析初始化workManager的时候的那个Provider。
而且我们还看到有很多的Receiver,我们以注释2处的NetworkStateProxy为例进行分析,它有一个action是CONNECTIVITY_CHANGE,所以当网络状态发生变化的时候会触发这个Receiver。我们看源码部分:
abstract class ConstraintProxy extends BroadcastReceiver {
private static final String TAG = Logger.tagWithPrefix("ConstraintProxy");
@Override
public void onReceive(Context context, Intent intent) {
Logger.get().debug(TAG, String.format("onReceive : %s", intent));
//-----1-----
Intent constraintChangedIntent = CommandHandler.createConstraintsChangedIntent(context);
context.startService(constraintChangedIntent);
}
/**
* Proxy for Battery Not Low constraint
*/
public static class BatteryNotLowProxy extends ConstraintProxy {
}
/**
* Proxy for Battery Charging constraint
*/
public static class BatteryChargingProxy extends ConstraintProxy {
}
/**
* Proxy for Storage Not Low constraint
*/
public static class StorageNotLowProxy extends ConstraintProxy {
}
/**
* Proxy for Network State constraints
*/
public static class NetworkStateProxy extends ConstraintProxy {
}
}
- 当网络状态发生变化的时候会执行上面注释1的
CommandHandler的createConstraintsChangedIntent(context)
方法,源码如下:
static final String ACTION_CONSTRAINTS_CHANGED = "ACTION_CONSTRAINTS_CHANGED";
static Intent createConstraintsChangedIntent(@NonNull Context context) {
Intent intent = new Intent(context, SystemAlarmService.class);
intent.setAction(ACTION_CONSTRAINTS_CHANGED);
return intent;
}
- 这里启动了一个
SystemAlarmService
,它是一个Service,我们关注它的onStartCommand
方法:
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
if (mIsShutdown) {
Logger.get().info(TAG,
"Re-initializing SystemAlarmDispatcher after a request to shut-down.");
// Destroy the old dispatcher to complete it's lifecycle.
mDispatcher.onDestroy();
// Create a new dispatcher to setup a new lifecycle.
initializeDispatcher();
// Set mIsShutdown to false, to correctly accept new commands.
mIsShutdown = false;
}
if (intent != null) {
mDispatcher.add(intent, startId);
}
// If the service were to crash, we want all unacknowledged Intents to get redelivered.
return Service.START_REDELIVER_INTENT;
}
- 调用了
mDispatcher.add(intent, startId)
方法:
public boolean add(@NonNull final Intent intent, final int startId) {
Logger.get().debug(TAG, String.format("Adding command %s (%s)", intent, startId));
assertMainThread();
String action = intent.getAction();
if (TextUtils.isEmpty(action)) {
Logger.get().warning(TAG, "Unknown command. Ignoring");
return false;
}
if (CommandHandler.ACTION_CONSTRAINTS_CHANGED.equals(action)
&& hasIntentWithAction(CommandHandler.ACTION_CONSTRAINTS_CHANGED)) {
return false;
}
intent.putExtra(KEY_START_ID, startId);
synchronized (mIntents) {
boolean hasCommands = !mIntents.isEmpty();
//-----1-----
mIntents.add(intent);
if (!hasCommands) {
// Only call processCommand if this is the first command.
// The call to dequeueAndCheckForCompletion will process the remaining commands
// in the order that they were added.
//-----2-----
processCommand();
}
}
return true;
}
- 注释1处将intent添加到了
mIntents
中,注释2处调用了processCommand
方法:
private void processCommand() {
assertMainThread();
PowerManager.WakeLock processCommandLock =
WakeLocks.newWakeLock(mContext, PROCESS_COMMAND_TAG);
try {
processCommandLock.acquire();
// Process commands on the background thread.
mWorkManager.getWorkTaskExecutor().executeOnBackgroundThread(new Runnable() {
@Override
public void run() {
synchronized (mIntents) {
//-----1-----
mCurrentIntent = mIntents.get(0);
}
if (mCurrentIntent != null) {
final String action = mCurrentIntent.getAction();
final int startId = mCurrentIntent.getIntExtra(KEY_START_ID,
DEFAULT_START_ID);
Logger.get().debug(TAG,
String.format("Processing command %s, %s", mCurrentIntent,
startId));
final PowerManager.WakeLock wakeLock = WakeLocks.newWakeLock(
mContext,
String.format("%s (%s)", action, startId));
try {
Logger.get().debug(TAG, String.format(
"Acquiring operation wake lock (%s) %s",
action,
wakeLock));
wakeLock.acquire();
//-----2-----
mCommandHandler.onHandleIntent(mCurrentIntent, startId,
SystemAlarmDispatcher.this);
} catch (Throwable throwable) {
Logger.get().error(
TAG,
"Unexpected error in onHandleIntent",
throwable);
} finally {
Logger.get().debug(
TAG,
String.format(
"Releasing operation wake lock (%s) %s",
action,
wakeLock));
wakeLock.release();
// Check if we have processed all commands
postOnMainThread(
new DequeueAndCheckForCompletion(SystemAlarmDispatcher.this));
}
}
}
});
} finally {
processCommandLock.release();
}
}
- 上面注释1得到了之前加入的Intent。在注释2处调用了
mCommandHandler.onHandleIntent
:
void onHandleIntent(
@NonNull Intent intent,
int startId,
@NonNull SystemAlarmDispatcher dispatcher) {
String action = intent.getAction();
if (ACTION_CONSTRAINTS_CHANGED.equals(action)) {
handleConstraintsChanged(intent, startId, dispatcher);
} else if (ACTION_RESCHEDULE.equals(action)) {
handleReschedule(intent, startId, dispatcher);
} else {
Bundle extras = intent.getExtras();
if (!hasKeys(extras, KEY_WORKSPEC_ID)) {
Logger.get().error(TAG,
String.format("Invalid request for %s, requires %s.",
action,
KEY_WORKSPEC_ID));
} else {
if (ACTION_SCHEDULE_WORK.equals(action)) {
handleScheduleWorkIntent(intent, startId, dispatcher);
} else if (ACTION_DELAY_MET.equals(action)) {
handleDelayMet(intent, startId, dispatcher);
} else if (ACTION_STOP_WORK.equals(action)) {
handleStopWork(intent, dispatcher);
} else if (ACTION_EXECUTION_COMPLETED.equals(action)) {
handleExecutionCompleted(intent, startId);
} else {
Logger.get().warning(TAG, String.format("Ignoring intent %s", intent));
}
}
}
}
- 在这个方法中会判断传入进来的action,进行相应的方法调用。因为这里的action是
ACTION_CONSTRAINTS_CHANGED
,所以会执行注释1处的handleConstraintsChanged
方法:
private void handleConstraintsChanged(
@NonNull Intent intent, int startId,
@NonNull SystemAlarmDispatcher dispatcher) {
Logger.get().debug(TAG, String.format("Handling constraints changed %s", intent));
// Constraints changed command handler is synchronous. No cleanup
// is necessary.
ConstraintsCommandHandler changedCommandHandler =
new ConstraintsCommandHandler(mContext, startId, dispatcher);
//-----1-----
changedCommandHandler.handleConstraintsChanged();
}
- 调用了上面注释1处的
changedCommandHandler.handleConstraintsChanged()
方法,源码如下:
void handleConstraintsChanged() {
List<WorkSpec> candidates = mDispatcher.getWorkManager().getWorkDatabase()
.workSpecDao()
.getScheduledWork();
// Update constraint proxy to potentially disable proxies for previously
// completed WorkSpecs.
ConstraintProxy.updateAll(mContext, candidates);
// This needs to be done to populate matching WorkSpec ids in every constraint controller.
mWorkConstraintsTracker.replace(candidates);
List<WorkSpec> eligibleWorkSpecs = new ArrayList<>(candidates.size());
// Filter candidates should have already been scheduled.
long now = System.currentTimeMillis();
for (WorkSpec workSpec : candidates) {
String workSpecId = workSpec.id;
long triggerAt = workSpec.calculateNextRunTime();
if (now >= triggerAt && (!workSpec.hasConstraints()
|| mWorkConstraintsTracker.areAllConstraintsMet(workSpecId))) {
eligibleWorkSpecs.add(workSpec);
}
}
for (WorkSpec workSpec : eligibleWorkSpecs) {
String workSpecId = workSpec.id;
//-----1-----
Intent intent = CommandHandler.createDelayMetIntent(mContext, workSpecId);
Logger.get().debug(TAG, String.format(
"Creating a delay_met command for workSpec with id (%s)", workSpecId));
mDispatcher.postOnMainThread(
//-----2-----
new SystemAlarmDispatcher.AddRunnable(mDispatcher, intent, mStartId));
}
mWorkConstraintsTracker.reset();
}
- 注释1处创建了一个action为
ACTION_DELAY_MET
的Intent。 - 注释2处将这个intent加入到一个Runnable中,将这个Runnable切换到主线程执行。
AddRunnable
的源码如下:
static class AddRunnable implements Runnable {
private final SystemAlarmDispatcher mDispatcher;
private final Intent mIntent;
private final int mStartId;
AddRunnable(@NonNull SystemAlarmDispatcher dispatcher,
@NonNull Intent intent,
int startId) {
mDispatcher = dispatcher;
mIntent = intent;
mStartId = startId;
}
@Override
public void run() {
mDispatcher.add(mIntent, mStartId);
}
}
我们看到又调用了mDispatcher.add方法,最终还是会 -> processCommand -> mCommandHandler.onHandleIntent , 但是这时候在onHandleIntent中的action就不是ACTION_CONSTRAINTS_CHANGED,而是ACTION_DELAY_MET了,所以要执行handleDelayMet方法:
private void handleDelayMet(
@NonNull Intent intent,
int startId,
@NonNull SystemAlarmDispatcher dispatcher) {
Bundle extras = intent.getExtras();
synchronized (mLock) {
String workSpecId = extras.getString(KEY_WORKSPEC_ID);
Logger.get().debug(TAG, String.format("Handing delay met for %s", workSpecId));
// Check to see if we are already handling an ACTION_DELAY_MET for the WorkSpec.
// If we are, then there is nothing for us to do.
if (!mPendingDelayMet.containsKey(workSpecId)) {
DelayMetCommandHandler delayMetCommandHandler =
new DelayMetCommandHandler(mContext, startId, workSpecId, dispatcher);
mPendingDelayMet.put(workSpecId, delayMetCommandHandler);
//-----1-----
delayMetCommandHandler.handleProcessWork();
} else {
Logger.get().debug(TAG,
String.format("WorkSpec %s is already being handled for ACTION_DELAY_MET",
workSpecId));
}
}
}
- 在注释1处调用了
delayMetCommandHandler.handleProcessWork()
方法,如下:
void handleProcessWork() {
mWakeLock = WakeLocks.newWakeLock(
mContext,
String.format("%s (%s)", mWorkSpecId, mStartId));
Logger.get().debug(TAG,
String.format("Acquiring wakelock %s for WorkSpec %s", mWakeLock, mWorkSpecId));
mWakeLock.acquire();
WorkSpec workSpec = mDispatcher.getWorkManager()
.getWorkDatabase()
.workSpecDao()
.getWorkSpec(mWorkSpecId);
// This should typically never happen. Cancelling work should remove alarms, but if an
// alarm has already fired, then fire a stop work request to remove the pending delay met
// command handler.
if (workSpec == null) {
stopWork();
return;
}
// Keep track of whether the WorkSpec had constraints. This is useful for updating the
// state of constraint proxies when onExecuted().
mHasConstraints = workSpec.hasConstraints();
if (!mHasConstraints) {
Logger.get().debug(TAG, String.format("No constraints for %s", mWorkSpecId));
onAllConstraintsMet(Collections.singletonList(mWorkSpecId));
} else {
// Allow tracker to report constraint changes
//-----1-----
mWorkConstraintsTracker.replace(Collections.singletonList(workSpec));
}
}
- 因为有约束条件,所以会进入注释1处的
replace
方法:
*
* @param workSpecs A list of {@link WorkSpec}s to monitor constraints for
*/
@SuppressWarnings("unchecked")
public void replace(@NonNull Iterable<WorkSpec> workSpecs) {
synchronized (mLock) {
for (ConstraintController<?> controller : mConstraintControllers) {
controller.setCallback(null);
}
for (ConstraintController<?> controller : mConstraintControllers) {
//-----1-----
controller.replace(workSpecs);
}
for (ConstraintController<?> controller : mConstraintControllers) {
controller.setCallback(this);
}
}
}
public void replace(@NonNull Iterable<WorkSpec> workSpecs) {
mMatchingWorkSpecIds.clear();
for (WorkSpec workSpec : workSpecs) {
if (hasConstraint(workSpec)) {
mMatchingWorkSpecIds.add(workSpec.id);
}
}
if (mMatchingWorkSpecIds.isEmpty()) {
mTracker.removeListener(this);
} else {
mTracker.addListener(this);
}
//-----2-----
updateCallback(mCallback, mCurrentValue);
}
- 紧接着调用了注释1处的
replace
到注释2处的updateCallback
:
private void updateCallback(
@Nullable OnConstraintUpdatedCallback callback,
@Nullable T currentValue) {
// We pass copies of references (callback, currentValue) to updateCallback because public
// APIs on ConstraintController may be called from any thread, and onConstraintChanged() is
// called from the main thread.
if (mMatchingWorkSpecIds.isEmpty() || callback == null) {
return;
}
if (currentValue == null || isConstrained(currentValue)) {
callback.onConstraintNotMet(mMatchingWorkSpecIds);
} else {
//-----1-----
callback.onConstraintMet(mMatchingWorkSpecIds);
}
}
- 接着调用到了注释1处的
onConstraintMet
方法。接着会执行到GreedyScheduler
的onAllConstraintsMet方法中:
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);
}
}
- 到这里,就会发现开始执行
mWorkManagerImpl.startWork
方法了:
public void startWork(
@NonNull String workSpecId,
@Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
mWorkTaskExecutor
.executeOnBackgroundThread(
new StartWorkRunnable(this, workSpecId, runtimeExtras));
}
这里的StartWorkRunnable就和前面分析的连接上了,最终会调用我们自定义的doWork方法
有需要文章中完整代码的同学 : 《现在点击此处传送门 即可免费获取》
现在点击链接还可以获取《更多 Android 源码解析+核心笔记+面试真题》
总结
WorkManager是一个很优秀的框架,使用起来很方便,只需要自定义Worker,创建请求,加入workManager队列等待执行任务即可,并且能保证任务肯定执行,至于其中的原理,需要好好研究一下源码
最后我想说:
学习没有捷径可言,我们要注意记学习,不仅要记,还要写心得体会,文字笔记、画图、总结等,方式很多,但是一定要自己认真去做,不要太相信自己的记忆,只有反复记忆,加深理解才行
同时,对于程序员而言,不单单是死记硬背,我们有更好的方式去学习,比如写demo去验证。复习知识点时,要及时跟你做过的项目结合起来,这样在面试时就知道怎么聊了,由项目讲到知识点,由一个知识点串联到另一个知识点。复习到一定阶段,可以尝试着去把这些东西串联起来,由点及面,形成知识体系
对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们
技术是无止境的,你需要对自己提交的每一行代码、使用的每一个工具负责,不断挖掘其底层原理,才能使自己的技术升华到更高的层面
Android 架构师之路还很漫长,与君共勉