【JetPack】WorkManager
转载自享学课堂-derry,非常感谢derry的讲解
WorkManager有什么用:
一:确保重要的后台任务,一定会被执行,后台任务(例如:非及时性的 (请求服务器 及时性) 上传,下载,同步数据 等)
二:内部对电量进行了优化,不需要我们去处理电量优化了
三:API 14 到 最新版本,都可以使用WorkManager来管你你的后台任务
四:注意:WorkManager不能做保活操作
五:调度,管理,执行的后台任务的场景,通常是是可延迟的后台任务
WorkManager概述
你的任务不可能总是在前台,但是还要确保你的那些重要任务执行,你们就可以放置 在后台执行,那么WorkManager就能够展现万丈光芒了
WorkManger是Android Jetpack提供执行后台任务管理的组件,它适用于需要保证 系统即使应用程序退出也会运行的任务,WorkManager API可以轻松指定可延迟的 异步任务以及何时运行它们,这些API允许您创建任务并将其交给WorkManager立 即运行或在适当的时间运行。
WorkManager根据设备API级别和应用程序状态等因素选择适当的方式来运行任务。如果WorkManager在应用程序运行时执行您的任务之一,WorkManager可以 在您应用程序进程的新线程中运行您的任务。如果您的应用程序未运行, WorkManager会选择一种合适的方式来安排后台任务 - 具体取决于设备API级别和包含的依赖项,WorkManager可能会使用 JobScheduler,Firebase JobDispatcher或 AlarmManager
WorkManager的各个角色
Worker:可以这样理解,指定需要执行的任务,可以成为Workder类的子类,在实现的方法中,就可以执行任务逻辑了
WorkRequest:可以这样理解,执行一项单一的任务
第一点:必须知道 WorkRequest对象必须指定Work执行的任务
第二点:需要知道 WorkRequest都有一个自动生成的唯一ID,可以使用ID执行取消 排队任务 或 获取任务状态等操作
第三点:需要知道 WorkRequest是一个抽象的类;系统默认实现子类 OneTimeWorkRequest或PeriodicWorkRequest
第四点:需要知道 WorkRequest.Builder创建WorkRequest对象;相应的子类:OneTimeWorkRequest.Builder或PeriodicWorkRequest.Builder
第五点:需要知道 Constraints:指定对任务运行时间的限制(任务约束);使用 Constraints.Builder创建Constraints对象 ,并传递给WorkRequest.Builder
WorkManager的简述:
WorkManager:排队和管理工作请求;将WorkRequest 对象传递WorkManager的 任务队列(注意:如果未指定任何约束, WorkManager立即运行任务)
WorkStatus的简述:
WorkStatus:包含有关特定任务的信息;可以使用LiveData保存 WorkStatus对象,监听任务状态;如LiveData
分享使用文章
WorkManage源码分析中
为什么workmanager可以在app杀死之后还可以继续工作,那是因为,这是由android系统级别的服务开启的,数据存储在room数据库里,由系统轮询,反射执行代码块,权限很大
1.初始化工作:
WorkManager.getInstance(this) // 各种初始化的工作
2.加入队列,触发后台任务工作:
.enqueue(oneTimeWorkRequest2); // 加入队列,触发后台任务工作
一、进入getInstance 初始化
WorkManagerImpl
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static @NonNull WorkManagerImpl getInstance(@NonNull Context context) {
synchronized (sLock) {
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;
}
}
第一个if不会进来,为什么呢,因为这是第二次执行,APK清单文件里面(第一次)执行 ,此时我们打开apk查看清单文件发现
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:exported="false"
android:multiprocess="true"
android:authorities="com.derry.workmanagersimple.workmanager-init"
android:directBootAware="false" />
在这里实例化,同样走到了上面初始化的方法,初始化的时候,最终返回workmanagerImpl的实现类
@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;
}
}
现在返回到初始化的代码的第二个if里面,new WorkManagerImpl
最终会走到一个方法,这里初始化了room的数据库,作为持久性保存的地方,以及其他配置信息
@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()));
List<Scheduler> schedulers = createSchedulers(applicationContext, workTaskExecutor);
Processor processor = new Processor(
context,
configuration,
workTaskExecutor,
database,
schedulers);
internalInit(context, configuration, workTaskExecutor, database, schedulers, processor);
}
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;
// Checks for app force stops.
mWorkTaskExecutor.executeOnBackgroundThread(new ForceStopRunnable(context, this));
}
其中有一个贪婪执行器,埋下伏笔
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@NonNull
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));
}
返回来,在代码里getInstance的时候,是第二次初始化,就只是获取了对应的workmanagerImpl类,单例的
初始化环节结束
二、执行流程
首先上图,整体的类uml关系图
首先进入enque
workmanagerImpl
@Override
@NonNull
public Operation enqueue(
@NonNull List<? extends WorkRequest> workRequests) {
// This error is not being propagated as part of the Operation, as we want the
// app to crash during development. Having no workRequests is always a developer error.
if (workRequests.isEmpty()) {
throw new IllegalArgumentException(
"enqueue needs at least one WorkRequest.");
}
return new WorkContinuationImpl(this, workRequests).enqueue();
}
WorkContinuationImpl
@Override
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.
//这里开启了线程,使用线程池
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;
}
EnqueueRunnable 来看一下做了什么
@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);
scheduleWorkInBackground();
}
mOperation.setState(Operation.SUCCESS);
} catch (Throwable exception) {
mOperation.setState(new Operation.State.FAILURE(exception));
}
}
@VisibleForTesting
public void scheduleWorkInBackground() {
WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();
Schedulers.schedule(
//获取配置的信息,开启执行调度
workManager.getConfiguration(),
workManager.getWorkDatabase(),
workManager.getSchedulers());
}
来看一下schedule,保存了所有的manager,使用了贪婪执行器GreedyScheduler,添加
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) {
//这里对应之前的贪婪执行器GreedyScheduler
scheduler.schedule(eligibleWorkSpecsArray);
}
}
}
GreedyScheduler
@Override
public void schedule(@NonNull WorkSpec... workSpecs) {
...
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()) {
// 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));
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);
}
}
}
startwork在impl里
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public void startWork(
@NonNull String workSpecId,
@Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
//拿到线程池,将任务丢给线程池执行
mWorkTaskExecutor
.executeOnBackgroundThread(
new StartWorkRunnable(this, workSpecId, runtimeExtras));
}
具体看一下这个线程
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
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() {
mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras);
}
}
Processor
public boolean startWork(
@NonNull String id,
@Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
WorkerWrapper workWrapper;
...
//将任务包裹起来,交给线程池
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;
}
WorkerWrapper
@WorkerThread
@Override
public void run() {
mTags = mWorkTagDao.getTagsForWorkSpecId(mWorkSpecId);
mWorkDescription = createWorkDescription(mTags);
runWorker();
}
private void runWorker() {
...
// Call mWorker.startWork() on the main thread.
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);
}
}
});
...
}
Worker
doWork就是我们实现的work类的实现方法,因为步骤都是异步操作,所以可以执行睡眠等操作
@Override
public final @NonNull ListenableFuture<Result> startWork() {
mFuture = SettableFuture.create();
getBackgroundExecutor().execute(new Runnable() {
@Override
public void run() {
try {
Result result = doWork();
//doWork
mFuture.set(result);
} catch (Throwable throwable) {
mFuture.setException(throwable);
}
}
});
return mFuture;
}
@WorkerThread
public abstract @NonNull Result doWork();
至此,主线流程打通
三、一些疑问
1、为什么飞行模式到网络连接打开就能收到work的启动
应用杀掉以后,系统服务还在。会有‘轮询机’会读取room数据库,apk的清单文件,已经把所有的信息保存进去了
当网络打开的时候,接收信息的是谁呢,
ConstraintProxy
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));
Intent constraintChangedIntent = CommandHandler.createConstraintsChangedIntent(context);
context.startService(constraintChangedIntent);
}
}
CommandHandler
static Intent createConstraintsChangedIntent(@NonNull Context context) {
Intent intent = new Intent(context, SystemAlarmService.class);
intent.setAction(ACTION_CONSTRAINTS_CHANGED);
return intent;
}
SystemAlarmService系统闹钟分发器
intent == createConstraintsChangedIntent返回
startId == ACTION_CONSTRAINTS_CHANGED
mDispatcher.add(intent, startId);
@Override
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;
}
SystemAlarmDispatcher
processCommand(); 执行此命令
@MainThread
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 we have a constraints changed intent in the queue don't add a second one. We are
// treating this intent as special because every time a worker with constraints is complete
// it kicks off an update for constraint proxies.
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();
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.
processCommand();
}
}
return true;
}
@MainThread
@SuppressWarnings("FutureReturnValueIgnored")
private void processCommand() {
assertMainThread();
...
//电池优化
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();
//注意这里
mCommandHandler.onHandleIntent(mCurrentIntent, startId,
.....
} finally {
processCommandLock.release();
}
}
注意这里走到之前定义的字段ACTION_CONSTRAINTS_CHANGED
目的是更换标记将ACTION_CONSTRAINTS_CHANGED 换标记 ACTION_DELAY_MET
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));
}
}
}
}
接下来就会执行,目的是为了做执行操作,会重新添加add,所以上面的if会执行好几次
else if (ACTION_DELAY_MET.equals(action)) {
handleDelayMet(intent, startId, dispatcher);
}
这个执行操作一点一点往里点,最终会点到startwork,调用到我们自己的work实现类里,完成唤醒操作
public void onAllConstraintsMet(@NonNull List<String> workSpecIds) {
......
synchronized (mLock) {
if (mCurrentState == STATE_INITIAL) {
mCurrentState = STATE_START_REQUESTED;
Logger.get().debug(TAG, String.format("onAllConstraintsMet for %s", mWorkSpecId));
// Constraints met, schedule execution
// Not using WorkManagerImpl#startWork() here because we need to know if the
// processor actually enqueued the work here.
//衔接到了之前的starrkwork
boolean isEnqueued = mDispatcher.getProcessor().startWork(mWorkSpecId);
if (isEnqueued) {
// setup timers to enforce quotas on workers that have
// been enqueued
mDispatcher.getWorkTimer()
.startTimer(mWorkSpecId, WORK_PROCESSING_TIME_IN_MS, this);
......
}
}
WorkManager是干嘛用的?
答:处理非及时任务,举例子:每天同步一次数据到服务器,这种类似的需求,不是及时执行,但是又保证会执行的非及时任务。
WorkManager是怎么保证,当我把APP杀掉后呢?
答:记录用户的所有信息并全部保存到数据库,而并非保存在内存中,这样做的好处,就是持久性保存记录,所有APP被杀掉后 依然可以获取所有任务信息。
你研究过WorKManager源码,任务是怎么保证一定执行的呀?
答:Android操作系统会在系统级别服务中,来判断用户的约束条件,当约束条件满足时就会执行任务,但是触发检测是采用广播的形式处理的,例如:网络连接成功 就触发...。