前言

本章进行 WorkManager 的学习;
是什么?
WorkManger 是 Android Jetpack 提供执行后台任务管理的组件,它适用于需要保证系统即使应用程序退出也会运行的任务,WorkManager API 可以轻松指定可延迟的异步任务以及何时运行它们,这些 API 允许您创建任务并将其交给 WorkManager 立即运行或在适当的时间运行;
WorkManager 根据设备 API 级别和应用程序状态等因素选择适当的方式来运行任务。如果WorkManager在应用程序运行时执行您的任务之一,WorkManager 可以在您应用程序进程的新线程中运行您的任务。如果您的应用程序未运行, WorkManager 会选择一种合适的方式来安排后台任务-具体取决于设备 API 级别和包含的依赖项,WorkManager 可能会使用 JobScheduler, Firebase JobDispatcher 或 AlarmManager;
任务的管理,一定会被执行,即使进程杀掉,手机重启,主要是针对的是非及时任务;
有什么用?
- 确保重要的后台任务,一定会被执行,后台任务(例如:非及时性的(请求服务器及时性)上传,下载,同步数据等);
- 内部对电量进行了优化,不需要我们去处理电量优化了;
- API14 到最新版本,都可以使用 WorkManager 来管理你的后台任务;
- 注意:WorkManager 不能做保活操作;
- 调度,管理,执行的后台任务的场景,通常是是可延迟的后台任务;
使用篇
依赖
bash
def work_version = "xxx"
implementation "androidx.work:work-runtime:$work_version"
基础调用
使用起来还是不算复杂,我们实现一个类,继承 Worker
即可;
kotlin
// 最简单的 执行任务
class MainWorker1(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {
companion object { const val TAG = "Mars" }
// 后台任务并且异步的 (原理:线程池执行Runnable)
override fun doWork(): Result {
Log.d(TAG, "MainWorker1 doWork: run started ... ")
try {
Thread.sleep(8000) // 睡眠
} catch (e: InterruptedException) {
e.printStackTrace()
Result.failure(); // 本次任务失败
} finally {
Log.d(TAG, "MainWorker1 doWork: run end ... ")
}
return Result.success(); // 本次任务成功
}
}
然后 MainActivity 中使用这个 MainWorker1
kotlin
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
/**
* 最简单的 执行任务
* 测试后台任务 1
*
* @param view
*/
fun testBackgroundWork1(view: View) {
// OneTimeWorkRequest 单个一次的任务
val oneTimeWorkRequest = OneTimeWorkRequest.Builder(MainWorker1::class.java).build()
WorkManager.getInstance(this).enqueue(oneTimeWorkRequest)
}
}
这样我们就完成了最基础的使用;
数据交互
完成 Activity 和 WorkerManager 的数据传递;我们先来定一个 MainWorker2
kotlin
/**
* 数据互相传递
* 后台任务
*/
class MainWorker2(context: Context, private val workerParams: WorkerParameters) : Worker(context, workerParams) {
companion object { const val TAG = "Mars" }
// 后台任务并且异步的 (原理:线程池执行Runnable)
@SuppressLint("RestrictedApi")
override fun doWork(): Result {
// 开始执行了 ENQUEUED
Log.d(TAG, "MainWorker2 doWork: 后台任务执行了")
// 接收 MainActivity 传递过来的数据,通过 InputData
val dataString = workerParams.inputData.getString("NBA")
Log.d(TAG, "MainWorker2 doWork: 接收 MainActivity 传递过来的数据: $dataString")
return new Result.Success(); // 本地执行 doWork 任务时 成功 执行任务完毕
}
}
定义 MainWorker2 通过 InputData 来接收 Activity 传递过来的数据,接下来我们来看下 Activity 中发送数据,通过 Data 进行发送数据的封装
kotlin
/**
* 数据 互相传递
* 测试后台任务 2
*
* @param view
*/
fun testBackgroundWork2(view: View?) {
// 单一的任务 一次
val oneTimeWorkRequest1: OneTimeWorkRequest
// 数据
val sendData = Data.Builder().putString("NBA", "Kobe").build()
// 请求对象初始化
oneTimeWorkRequest1 = OneTimeWorkRequest.Builder(MainWorker2::class.java)
.setInputData(sendData) // 数据的携带发送,一般都是携带到 Request 里面去发送数据给 WorkManager2
.build()
// 启动
WorkManager.getInstance(this).enqueue(oneTimeWorkRequest1)
}
这样,我们就完成了 Activity 向 WorkManager 传递数据,以及在 WorkManager 中接收数据;
接下来,我们来看下 WorkManager 怎么向 Activity 回传数据,以及 Activity 中如何接收回传回来的数据;
MainWorker2 中通过 Data 进行回传数据的封装;
kotlin
class MainWorker2(context: Context, private val workerParams: WorkerParameters) : Worker(context, workerParams) {
companion object { const val TAG = "Mars" }
// 后台任务 并且 异步的 (原理:线程池执行Runnable)
@SuppressLint("RestrictedApi")
override fun doWork(): Result {
// 开始执行了 ENQUEUED
Log.d(TAG, "MainWorker2 doWork: 后台任务执行了")
// 接收 MainActivity 传递过来的数据,通过 InputData
val dataString = workerParams.inputData.getString("NBA")
Log.d(TAG, "MainWorker2 doWork: 接收 MainActivity 传递过来的数据: $dataString")
// 反馈数据 给 MainActivity
// 把任务中的数据回传到MainActivity中
val outputData = Data.Builder().putString("CBA", "Jordan").build()
return Result.Success(outputData); // 本地执行 doWork 任务时成功执行任务完毕
}
}
Success 标识成功,WorkManager 也提供了失败和重试的标识
Result.Failure() // 本地执行 doWork 任务时失败
Result.Retry(); // 本地执行 doWork 任务时重试一次,只重试一次,成不成不关心了
通过 Data 将回传数据封装起来,并通过 Result.Success 方法回传回去,我们来看下 Activity 中如何接收回传回来的数据;
接收 WorkManager 回传的数据,一般都是通过状态机来接收的,WorkManager 也天然的支持了 LiveData,并提供了 getWorkInfoByIdLiveData
方法来监听结果
kotlin
/**
* 数据 互相传递
* 测试后台任务 2
*
* @param view
*/
fun testBackgroundWork2(view: View?) {
// 单一的任务 一次
val oneTimeWorkRequest1: OneTimeWorkRequest
// 数据
val sendData = Data.Builder().putString("NBA", "Kobe").build()
// 请求对象初始化
oneTimeWorkRequest1 = OneTimeWorkRequest.Builder(MainWorker2::class.java)
.setInputData(sendData) // 数据的携带发送,一般都是携带到 Request 里面去 发送数据给 WorkManager2
.build()
// 接收 WorkManager 回传的数据
WorkManager.getInstance(this).getWorkInfoByIdLiveData(oneTimeWorkRequest1.id)
.observe(this, { workInfo ->
// ENQUEUED, RUNNING, SUCCEEED
Log.d(TAG, "状态:" + workInfo.state.name)
// ENQUEUED, RUNNING 都取不到 回馈的数据 都是 null
Log.d(TAG, "取到了任务回传的数据: " + workInfo.outputData.getString("CBA"))
if (workInfo.state.isFinished) {
// 判断成功 SUCCEEDED 状态
Log.d(TAG, "取到了任务回传的数据: " + workInfo.outputData.getString("CBA"))
}
})
// 启动
WorkManager.getInstance(this).enqueue(oneTimeWorkRequest1)
}
这样我们就完成了从 WorkManager 接收回传数据;另外,我们只有在状态机是SUCCEEDED状态的时候才能获取到回传的数据;ENQUEUED 和 RUNNING 是获取不到回传的数据;
多任务顺序执行
我们前面讲的都是单个任务,那么多个任务如何顺序执行呢?我们来探索下;
首先,我们来声明多个任务 MainWorker3、MainWorker4、MainWorker5、MainWorker6
kotlin
/**
* 后台任务3
*/
class MainWorker3 (context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {
companion object { const val TAG = "Mars" }
@SuppressLint("RestrictedApi")
override fun doWork(): Result {
Log.d(TAG, "MainWorker3 doWork: 后台任务执行了")
return Result.Success() // 本地执行 doWork 任务时 成功 执行任务完毕
}
}
MainWorker4、MainWorker5、MainWorker6 完全复制,这里就不展示了;我们接下来看下如何让它们顺序执行;
scss
/**
* 多个任务 顺序执行
* 测试后台任务 3
*
* @param view
*/
fun testBackgroundWork3(view: View) {
// 单一的任务 一次
val oneTimeWorkRequest3 = OneTimeWorkRequest.Builder(MainWorker3::class.java).build()
val oneTimeWorkRequest4 = OneTimeWorkRequest.Builder(MainWorker4::class.java).build()
val oneTimeWorkRequest5 = OneTimeWorkRequest.Builder(MainWorker5::class.java).build()
val oneTimeWorkRequest6 = OneTimeWorkRequest.Builder(MainWorker6::class.java).build()
// 顺序执行这四个任务,3、4、5、6
WorkManager.getInstance(this)
.beginWith(oneTimeWorkRequest3) // 做初始化检查的任务成功后
.then(oneTimeWorkRequest4) // 业务4 任务成功后
.then(oneTimeWorkRequest5) // 业务5 任务成功后
.then(oneTimeWorkRequest6) // 最后检查工作任务6
.enqueue()
// 也可以采用集合的方式来顺序执行
val oneTimeWorkRequests: MutableList<OneTimeWorkRequest> = ArrayList() // 集合方式
oneTimeWorkRequests.add(oneTimeWorkRequest3)
oneTimeWorkRequests.add(oneTimeWorkRequest4)
WorkManager.getInstance(this).beginWith(oneTimeWorkRequests)
.then(oneTimeWorkReques5)
.then(oneTimeWorkRequest6) // 最后再检查同步
.enqueue()
}
这样我们就完成了任务的顺序执行,接下来,我们来看下周期性任务如何实现
周期性任务执行
周期性任务就不能使用 OneTimeWorkRequest
,要使用 PeriodicWorkRequest
来实现了;
kotlin
/**
* 重复执行后台任务 非单个任务,多个任务
* 测试后台任务
*
* @param view
*/
fun testBackgroundWork4(view: View) {
//
val periodicWorkRequest = PeriodicWorkRequest.Builder(MainWorker3::class.java, 10, TimeUnit.SECONDS).build()
// 监听状态
WorkManager.getInstance(this).getWorkInfoByIdLiveData(periodicWorkRequest.id)
.observe(this, { workInfo ->
Log.d(TAG, "状态:" + workInfo.state.name) // ENQUEEN & RUNNING 循环反复
if (workInfo.state.isFinished) {
// 这里不会打印,因为一直在轮询任务,导致任务不会有完成了
Log.d(TAG, "状态:isFinished=true 同学们注意:后台任务已经完成了...")
}
})
WorkManager.getInstance(this).enqueue(periodicWorkRequest)
}
PS:这里有个点要注意下,虽然我们传入的是 10 秒,但是 Google 规定,这个值不能小于 15 分钟,否则会强制改成 15 分钟;
因为一直在轮询任务,导致任务不会有完成状态,只有 ENQUEEN & RUNNING 状态;
取消任务调用 cancelWorkById 方法
kotlin
WorkManager.getInstance(this).cancelWorkById(periodicWorkRequest.getId())
这就是周期性任务的实现,接下来,我们来看下约束条件任务如何执行,也就是我们限定了某些场景下才能执行的任务
约束任务执行
假设,我们限定必须是『网络连接中状态』、『充电中状态』、『空闲状态』下才能执行的任务
kotlin
/**
* 约束条件,约束后台任务执行
* 测试后台任务
*
* @param view
*/
@RequiresApi(api = Build.VERSION_CODES.M)
fun testBackgroundWork5(view: View?) {
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED) // 必须是联网中
.setRequiresCharging(true) // 必须是充电中
.setRequiresDeviceIdle(true) // 必须是空闲时
.build()
// 请求对象
val request = OneTimeWorkRequest.Builder(MainWorker3::class.java)
.setConstraints(constraints) // Request 关联约束条件
.build()
// 加入队列
WorkManager.getInstance(this).enqueue(request)
}
除了上面设置的约束外,WorkManger还提供了以下的约束作为Work执行的条件:
- setRequiredNetworkType:网络连接设置
- setRequiresBatteryNotLow:是否为低电量时运行 默认false
- setRequiresCharging:是否要插入设备(接入电源),默认false
- setRequiresDeviceIdle:设备是否为空闲,默认false
- setRequiresStorageNotLow:设备可用存储是否不低于临界阈值
进程杀死继续执行的任务
前面开头我们有讲到,WorkManager 本身支持在进程不存活的状态下执行任务,我们怎么来验证这个能力,可以通过启动任务之后,写入 SP 的方式来验证下,我们先来声明一个写入 SP 的 Worker;
kotlin
class MainWorker7(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {
// 三个静态常量 SP的标记
companion object {
const val TAG = "Mars"
const val SP_NAME = "spNAME" // SP name
const val SP_KEY = "spKEY" // SP Key
}
// 后台任务
@SuppressLint("RestrictedApi")
override fun doWork(): Result {
Log.d(TAG, "MainWorker7 doWork: 后台任务执行了 started")
// 睡眠八秒钟
try {
Thread.sleep(8000)
} catch (e: InterruptedException) {
e.printStackTrace()
}
// 获取SP
val sp = applicationContext.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE)
// 获取 sp 里面的值
var spIntValue = sp.getInt(SP_KEY, 0)
sp.edit().putInt(SP_KEY, ++spIntValue).apply() // 隔8秒钟 更新文件
Log.d(TAG, "MainWorker7 doWork: 后台任务执行了 end")
return Result.Success() // 本地执行 doWork 任务时 成功 执行任务完毕
}
}
接下来我们就常规启动这个任务;
kotlin
fun testBackgroundWork6(view: View?) {
// 约束条件
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED) // 约束条件,必须是网络连接
.build()
// 构建Request
val request = OneTimeWorkRequest.Builder(MainWorker7::class.java)
.setConstraints(constraints)
.build()
// 加入队列
WorkManager.getInstance(this).enqueue(request)
}
接下来,我们在 Activity 中监听 SP 的变化,通过实现SharedPreferences.OnSharedPreferenceChangeListener
接口,来实时监听 SP 内容的变化,我们来让 Activity 实现这个接口
kotlin
class MainActivity : AppCompatActivity() , SharedPreferences.OnSharedPreferenceChangeListener {
override fun onCreate(savedInstanceState: Bundle?) {
setContentView(R.layout.activity_main)
// 绑定 SP 变化监听
val sp = getSharedPreferences(MainWorker7.SP_NAME, MODE_PRIVATE)
sp.registerOnSharedPreferenceChangeListener(this)
updateToUI()
}
// 监听 SP 变化,并实时更新UI
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?)
= updateToUI()
// 从SP里面获取值,显示到界面
private fun updateToUI() {
val sp = getSharedPreferences(MainWorker7.SP_NAME, MODE_PRIVATE)
val resultValue = sp.getInt(MainWorker7.SP_KEY, 0)
bt6?.setText("测试后台任务六 -- $resultValue")
}
}
启动任务之后,我们将 APP 杀死,等到任务结束之后,我们来看下 sp 中是否写入了我们期望的内容;
原理篇
原理主要从:初始化、执行、状态变换三个方向来展开讲解
初始化
WorkManager 借助了强大的 Proivder 来进行的初始化,当我们在程序中依赖 WorkManager 之后,编译产出 apk 之后,可以直接在 AS 中打开分析 apk,在 AndroidManifest.xml 中可以看到 WorkManager 相关的 Provider
ini
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:exported="false"
android:multiprocess="true"
android:authorities="com.llc.workmanager.workmanager-init"
android:directBootAware="false" />
这个是 gradle 帮我们生成的 Provider,我们进入这个 WorkManagerInitializer 来看下:
scala
public class WorkManagerInitializer extends ContentProvider {
@Override
public boolean onCreate() {
// Initialize WorkManager with the default configuration.
WorkManager.initialize(getContext(), new Configuration.Builder().build());
return true;
}
... 省略部分代码
}
这里就是在 Provider 中执行了初始化的操作,我们进入这个 initialize
方法看下:
less
public static void initialize(@NonNull Context context, @NonNull Configuration configuration) {
synchronized (sLock) {
if (sDelegatedInstance == null) {
context = context.getApplicationContext();
if (sDefaultInstance == null) {
sDefaultInstance = new WorkManagerImpl(
context,
configuration,
new WorkManagerTaskExecutor(configuration.getTaskExecutor()));
}
sDelegatedInstance = sDefaultInstance;
}
}
}
很简单的单例初始化,第一次的初始化是放在 Provider 中执行的;我们进入这个 WorkManagerImpl
的构造方法中看下:
less
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()));
// 核心逻辑 1
List<Scheduler> schedulers = createSchedulers(applicationContext, workTaskExecutor);
Processor processor = new Processor(
context,
configuration,
workTaskExecutor,
database,
schedulers);
// 核心逻辑 2
internalInit(context, configuration, workTaskExecutor, database, schedulers, processor);
}
可以看到,入参中使用了WorkDatabase
这个就是 Room 中的数据库,所以说 WorkManager 中采用了 Room 来管理相关任务来保证即使手机重启,App 进程被杀掉也依然能够执行我们的任务;
这里也创建了一个调度器,通过 createSchedulers
方法,我们进入这个方法看下:
less
public List<Scheduler> createSchedulers(
@NonNull Context context,
@NonNull TaskExecutor taskExecutor) {
return Arrays.asList(
Schedulers.createBestAvailableBackgroundScheduler(context, this),
new GreedyScheduler(context, taskExecutor, this));
}
最终 new 了一个 GreedyScheduler 贪婪调度器;
创建这个调度器之后,交给了 Processor 来管理这个调度器
ini
Processor processor = new Processor(
context,
configuration,
workTaskExecutor,
database,
schedulers);
最终调用 internalInit
方法来执行真正的初始化
less
private void internalInit(@NonNull Context context,
@NonNull Configuration configuration,
@NonNull TaskExecutor workTaskExecutor,
@NonNull WorkDatabase workDatabase,
@NonNull List<Scheduler> schedulers,
@NonNull Processor processor) {
context = context.getApplicationContext();
mContext = context;
mConfiguration = configuration;
mWorkTaskExecutor = workTaskExecutor;
mWorkDatabase = workDatabase;
mSchedulers = schedulers;
mProcessor = processor;
mPreferenceUtils = new PreferenceUtils(workDatabase);
mForceStopRunnableCompleted = false;
// 这里的 【检查应用程序强制停止】 例如:正在执行任务的时候,手机关机了,或者发生了意外,这里就会重试之前失败的任务哦
mWorkTaskExecutor.executeOnBackgroundThread(new ForceStopRunnable(context, this));
}
初始化中,启动了一个 ForceStopRunnable 这样的一个『Runnable』来进行初始化的操作,也就是说 WorkManager 的初始化是在子线程中执行的;
我们进入这个 Runnable 的 run 方法看下:
scss
public void run() {
WorkDatabasePathHelper.migrateDatabase(mContext);
try {
boolean needsScheduling = cleanUp();
if (shouldRescheduleWorkers()) {
mWorkManager.rescheduleEligibleWork();
mWorkManager.getPreferenceUtils().setNeedsReschedule(false);
} else if (isForceStopped()) {
mWorkManager.rescheduleEligibleWork();
} else if (needsScheduling) {
Schedulers.schedule(
mWorkManager.getConfiguration(),
mWorkManager.getWorkDatabase(),
mWorkManager.getSchedulers());
}
mWorkManager.onForceStopRunnableCompleted();
} catch (SQLiteCantOpenDatabaseException
| SQLiteDatabaseCorruptException
| SQLiteAccessPermException exception) {
... 省略部分代码
}
}
这个 run
的作用主要是:发现了未完成的任务,重新执行任务;
WorkManager的初始化总结
① WorkManager 的初始化是由 WorkManagerInitializer 这个 ContentProvider 执行的
② 会初始化 Configuration,WorkManagerTaskExecutor,WorkDatabase,Schedulers,Processor
③ GreedyScheduler
④ 发现了未完成的,需要重新执行的任务(之前意外中断的继续执行)
执行
任务执行又分为 约束任务执行 和 非约束任务执行
kotlin
WorkManager.getInstance(this).enqueue(request)
通过 enqueue
来启动任务,我们进入这个方法看下:
less
public Operation enqueue(
@NonNull List<? extends WorkRequest> workRequests) {
... 省略部分代码
return new WorkContinuationImpl(this, workRequests).enqueue();
}
这里直接创建 WorkContinuationImpl 并执行 enqueue 方法,我们进入这个方法看下:
java
public @NonNull Operation enqueue() {
if (!mEnqueued) {
EnqueueRunnable runnable = new EnqueueRunnable(this);
// 核心逻辑
mWorkManagerImpl.getWorkTaskExecutor().executeOnBackgroundThread(runnable);
mOperation = runnable.getOperation();
} else {
... 省略部分代码
}
return mOperation;
}
WorkManager 的 TaskExecutor 执行了 EnqueueRunnable ,那么我们来看下 EnqueueRunnable 的 run
函数
scss
public void run() {
try {
... 省略部分代码
// 核心逻辑 1
boolean needsScheduling = addToDatabase();
if (needsScheduling) {
final Context context =
mWorkContinuation.getWorkManagerImpl().getApplicationContext();
PackageManagerHelper.setComponentEnabled(context, RescheduleReceiver.class, true);
// 核心逻辑 2
scheduleWorkInBackground();
}
mOperation.setState(Operation.SUCCESS);
} catch (Throwable exception) {
... 省略部分代码
}
}
这里是两块核心的地方,一个是 addToDatabase
这个方法是用来构建数据库的,一个是 scheduleWorkInBackground
这个方法用来执行异步任务;
我们先来看主流程 scheduleWorkInBackground
scss
public void scheduleWorkInBackground() {
WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();
Schedulers.schedule(
workManager.getConfiguration(),
workManager.getWorkDatabase(),
workManager.getSchedulers());
}
这里通过 Schedulers.schedule 来执行调度,我们进入这个 schedule
方法看下:
less
public static void schedule(
@NonNull Configuration configuration,
@NonNull WorkDatabase workDatabase,
List<Scheduler> schedulers) {
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();
for (WorkSpec workSpec : eligibleWorkSpecs) {
workSpecDao.markWorkSpecScheduled(workSpec.id, now);
}
}
// 核心逻辑1
workDatabase.setTransactionSuccessful();
} finally {
workDatabase.endTransaction();
}
if (eligibleWorkSpecs != null && eligibleWorkSpecs.size() > 0) {
WorkSpec[] eligibleWorkSpecsArray = eligibleWorkSpecs.toArray(new WorkSpec[0]);
for (Scheduler scheduler : schedulers) {
// 核心逻辑在这里
scheduler.schedule(eligibleWorkSpecsArray);
}
}
}
这里两个逻辑,核心逻辑1是:任务保存到数据,另一个就是执行调度;
最终,for 循环所有的调度器,并执行 schedule 方法,这里一共有三个调度器实现类:

我们前面在初始化的时候创建了一个 GreedyScheduler 调度器,所以这里一定是执行这个调度器的 schedule
方法,我们进入这个方法看下:
scss
public void schedule(@NonNull WorkSpec... workSpecs) {
if (mIsMainProcess == null) {
mIsMainProcess = TextUtils.equals(mContext.getPackageName(), getProcessName());
}
registerExecutionListenerIfNeeded();
List<WorkSpec> constrainedWorkSpecs = new ArrayList<>();
List<String> constrainedWorkSpecIds = new ArrayList<>();
for (WorkSpec workSpec : workSpecs) {
if (workSpec.state == WorkInfo.State.ENQUEUED
&& !workSpec.isPeriodic()
&& workSpec.initialDelay == 0L
&& !workSpec.isBackedOff()) {
// 这里区分约束条件任务 和 非约束条件任务
if (workSpec.hasConstraints()) {
if (SDK_INT >= 23 && workSpec.constraints.requiresDeviceIdle()) {
//
} else if (SDK_INT >= 24 && workSpec.constraints.hasContentUriTriggers()) {
//
} else {
constrainedWorkSpecs.add(workSpec);
constrainedWorkSpecIds.add(workSpec.id);
}
} else {
// 核心逻辑
mWorkManagerImpl.startWork(workSpec.id);
}
}
}
synchronized (mLock) {
if (!constrainedWorkSpecs.isEmpty()) {
mConstrainedWorkSpecs.addAll(constrainedWorkSpecs);
mWorkConstraintsTracker.replace(mConstrainedWorkSpecs);
}
}
}
我们先来看非约束条件任务,mWorkManagerImpl.startWork(workSpec.id);
less
public void startWork(
@NonNull String workSpecId,
@Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
mWorkTaskExecutor
.executeOnBackgroundThread(
new StartWorkRunnable(this, workSpecId, runtimeExtras));
}
这里又启动了一个 StartWorkRunnable 来执行任务,我们进入这个 Runnable 的 run
方法看下:
csharp
public void run() {
mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras);
}
我们进入这个 startWork
看下:
less
public boolean startWork(
@NonNull String id,
@Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
WorkerWrapper workWrapper;
synchronized (mLock) {
.... 省略部分代码
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);
return true;
}
这里又启动了一个 WorkerWrapper 来执行任务,我们进入这个 Runnable 的 run
方法看下:
scss
public void run() {
mTags = mWorkTagDao.getTagsForWorkSpecId(mWorkSpecId);
mWorkDescription = createWorkDescription(mTags);
// 核心逻辑
runWorker();
}
这里直接调用了 runWorker
方法,我们进入这个方法看下:
scss
private void runWorker() {
.... 省略部分代码
if (trySetRunning()) {
final SettableFuture<ListenableWorker.Result> future = SettableFuture.create();
mWorkTaskExecutor.getMainThreadExecutor()
.execute(new Runnable() {
@Override
public void run() {
try {
// 核心逻辑在这里
mInnerFuture = mWorker.startWork();
future.setFuture(mInnerFuture);
} catch (Throwable e) {
future.setException(e);
}
}
});
}, mWorkTaskExecutor.getBackgroundExecutor());
}
.... 省略部分代码
}
最终执行的是 startWork
方法,这个方法也有两个实现,我们进入这个方法看下:

分为有约束Work 和无约束 Work,我们先看无约束 Work
java
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;
}
执行的是 doWork
方法是一个抽象方法,最终执行的就是我们自定的 Worker 的 doWork 方法;

这样,WorkManager 工作的主流程就打通了,我们接下来看下状态变换,就是当 网络发生变化的时候,它为什么可以继续执行任务;
状态变换
以网络切换为例子,通过注册监听网络变化的广播 ConstraintProxy 来监听网络变化,网络发生变化回调onReceive 方法;
我们通过 AndroidManifest.xml 可以看到,WorkManager 帮我们注册了很多的系统服务以及广播;
ini
<service
android:name="androidx.work.impl.background.systemalarm.SystemAlarmService"
android:enabled="@ref/0x7f040003"
android:exported="false"
android:directBootAware="false" />
<service
android:name="androidx.work.impl.background.systemjob.SystemJobService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:enabled="@ref/0x7f040005"
android:exported="true"
android:directBootAware="false" />
<service
android:name="androidx.work.impl.foreground.SystemForegroundService"
android:enabled="@ref/0x7f040004"
android:exported="false"
android:directBootAware="false" />
<receiver
android:name="androidx.work.impl.utils.ForceStopRunnable$BroadcastReceiver"
android:enabled="true"
android:exported="false"
android:directBootAware="false" />
<receiver
android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryChargingProxy"
android:enabled="false"
android:exported="false"
android:directBootAware="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:enabled="false"
android:exported="false"
android:directBootAware="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:enabled="false"
android:exported="false"
android:directBootAware="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
android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$NetworkStateProxy"
android:enabled="false"
android:exported="false"
android:directBootAware="false">
<intent-filter>
<action
android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
<receiver
android:name="androidx.work.impl.background.systemalarm.RescheduleReceiver"
android:enabled="false"
android:exported="false"
android:directBootAware="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:enabled="@ref/0x7f040003"
android:exported="false"
android:directBootAware="false">
<intent-filter>
<action
android:name="androidx.work.impl.background.systemalarm.UpdateProxies" />
</intent-filter>
</receiver>
<service
android:name="androidx.room.MultiInstanceInvalidationService"
android:exported="false" />
我们就以其中的网络变换的 Receiver 为切入点,来看下:
scala
public static class NetworkStateProxy extends ConstraintProxy {
}
可以看到,NetworkStateProxy 是继承了 ConstraintProxy,我们来看下 ConstraintProxy 这个类;
scala
abstract class ConstraintProxy extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Logger.get().debug(TAG, String.format("onReceive : %s", intent));
Intent constraintChangedIntent = CommandHandler.createConstraintsChangedIntent(context);
context.startService(constraintChangedIntent);
}
}
这里我们重点看下 onReceive
方法,通过 createConstraintsChangedIntent
来创建目标 Intent
less
static Intent createConstraintsChangedIntent(@NonNull Context context) {
Intent intent = new Intent(context, SystemAlarmService.class);
intent.setAction(ACTION_CONSTRAINTS_CHANGED);
return intent;
}
Intent 中启动了一个 SystemAlarmService 服务;这是一个系统级别的服务,非 App 级别的;
我们进入这个 Service 的 onStartCommand
方法看下:
scss
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
if (mIsShutdown) {
mDispatcher.onDestroy();
initializeDispatcher();
mIsShutdown = false;
}
if (intent != null) {
// 核心逻辑在这里
mDispatcher.add(intent, startId);
}
return Service.START_REDELIVER_INTENT;
}
启动服务之后的核心逻辑在 add
方法,我们进入这个方法看下:
java
public boolean add(@NonNull final Intent intent, final int startId) {
.... 省略部分代码
intent.putExtra(KEY_START_ID, startId);
synchronized (mIntents) {
boolean hasCommands = !mIntents.isEmpty();
mIntents.add(intent);
if (!hasCommands) {
// 核心逻辑在这里
processCommand();
}
}
return true;
}
这里直接调用 processCommand
方法来执行状态变化;
csharp
private void processCommand() {
try {
.... 省略部分代码
mWorkManager.getWorkTaskExecutor().executeOnBackgroundThread(new Runnable() {
@Override
public void run() {
synchronized (mIntents) {
mCurrentIntent = mIntents.get(0);
}
if (mCurrentIntent != null) {
final int startId = mCurrentIntent.getIntExtra(KEY_START_ID,
DEFAULT_START_ID);
.... 省略部分代码
try {
.... 省略部分代码
// 核心逻辑在这里
mCommandHandler.onHandleIntent(mCurrentIntent, startId,
SystemAlarmDispatcher.this);
} catch (Throwable throwable) {
.... 省略部分代码
} finally {
.... 省略部分代码
}
}
}
});
} finally {
.... 省略部分代码
}
}
核心逻辑在这个 onHandleIntent
方法中,我们进入这个方法看下:
less
void onHandleIntent(
@NonNull Intent intent,
int startId,
@NonNull SystemAlarmDispatcher dispatcher) {
String action = intent.getAction();
if (ACTION_CONSTRAINTS_CHANGED.equals(action)) {
// 核心逻辑 1
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));
}
}
}
}
核心逻辑 1 就是我们前面创建 Service 的时候传入的 ACTION_CONSTRAINTS_CHANGED 所以,一定会执行 handleConstraintsChanged
方法,每次执行的时候都同步检测约束条件;
less
private void handleConstraintsChanged(
@NonNull Intent intent, int startId,
@NonNull SystemAlarmDispatcher dispatcher) {
ConstraintsCommandHandler changedCommandHandler =
new ConstraintsCommandHandler(mContext, startId, dispatcher);
// 核心逻辑
changedCommandHandler.handleConstraintsChanged();
}
这里直接调用 handleConstraintsChanged
方法;
ini
void handleConstraintsChanged() {
List<WorkSpec> candidates = mDispatcher.getWorkManager().getWorkDatabase()
.workSpecDao()
.getScheduledWork();
.... 省略部分代码
for (WorkSpec workSpec : eligibleWorkSpecs) {
String workSpecId = workSpec.id;
// 核心逻辑
Intent intent = CommandHandler.createDelayMetIntent(mContext, workSpecId);
mDispatcher.postOnMainThread(
new SystemAlarmDispatcher.AddRunnable(mDispatcher, intent, mStartId));
}
mWorkConstraintsTracker.reset();
}
这里又创建另一个 Intent,通过 createDelayMetIntent
方法,我们进入这个方法看下;
less
static Intent createDelayMetIntent(@NonNull Context context, @NonNull String workSpecId) {
Intent intent = new Intent(context, SystemAlarmService.class);
intent.setAction(ACTION_DELAY_MET);
intent.putExtra(KEY_WORKSPEC_ID, workSpecId);
return intent;
}
这里依然是启动 SystemAlarmService ,只不过 startId,替换成了 ACTION_DELAY_MET 我们回到这个 Service 的 onStartCommand 方法看下,最终按照前面 ACTION_CONSTRAINTS_CHANGED 的流程,进入到 CommandHandler 的 onHandleIntent
方法中的节点:
scss
if (ACTION_DELAY_MET.equals(action)) {
handleDelayMet(intent, startId, dispatcher);
}
进入这个方法看下:
less
private void handleDelayMet(
@NonNull Intent intent,
int startId,
@NonNull SystemAlarmDispatcher dispatcher) {
Bundle extras = intent.getExtras();
synchronized (mLock) {
String workSpecId = extras.getString(KEY_WORKSPEC_ID);
if (!mPendingDelayMet.containsKey(workSpecId)) {
DelayMetCommandHandler delayMetCommandHandler =
new DelayMetCommandHandler(mContext, startId, workSpecId, dispatcher);
mPendingDelayMet.put(workSpecId, delayMetCommandHandler);
// 核心逻辑
delayMetCommandHandler.handleProcessWork();
} else {
.... 省略部分代码
}
}
}
我们进入这个 handleProcessWork
看下:
scss
void handleProcessWork() {
.... 省略部分代码
WorkSpec workSpec = mDispatcher.getWorkManager()
.getWorkDatabase()
.workSpecDao()
.getWorkSpec(mWorkSpecId);
.... 省略部分代码
mHasConstraints = workSpec.hasConstraints();
if (!mHasConstraints) {
// 核心逻辑
onAllConstraintsMet(Collections.singletonList(mWorkSpecId));
} else {
// Allow tracker to report constraint changes
mWorkConstraintsTracker.replace(Collections.singletonList(workSpec));
}
}
核心逻辑在这个 onAllConstraintsMet
方法;
scss
public void onAllConstraintsMet(@NonNull List<String> workSpecIds) {
if (!workSpecIds.contains(mWorkSpecId)) {
return;
}
synchronized (mLock) {
if (mCurrentState == STATE_INITIAL) {
mCurrentState = STATE_START_REQUESTED;
// 核心逻辑
boolean isEnqueued = mDispatcher.getProcessor().startWork(mWorkSpecId);
if (isEnqueued) {
mDispatcher.getWorkTimer()
.startTimer(mWorkSpecId, WORK_PROCESSING_TIME_IN_MS, this);
} else {、
cleanUp();
}
} else {
.... 省略部分代码
}
}
}
最终调用到了 startWork,就又回到了我们前面看到的 startWork 的逻辑,开始启动我们创建的任务;
到此,约束任务下,状态变换的时候,也就能执行到我们创建的任务了;
好了,WorkManager 就拆解到这里吧~
总结 借助 Room 和系统服务 SystemAlarmService 来完成我们的任务即使进程被杀死的情况下依然能够执行的能力;
欢迎三连
来都来了,点个关注,点个赞吧,你的支持是我前进的最大动力~