场景
- 确保后台任务执行如上传,下载,同步数据等(不可作为保活的手段)
- 内部有对电量做了优化
- 执行延时的任务
使用
public class MainWorker extends Worker {
public final static String TAG = MainWorker7.class.getSimpleName();
// 有构造函数
public MainWorker7(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
@SuppressLint("RestrictedApi")
@NonNull
@Override
public Result doWork() {
Log.d(TAG, "MainWorker doWork: 后台任务执行了 started");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return new Result.Success(); // 本地执行 doWork 任务时 成功 执行任务完毕
}
}
任务需要继承 Worker,并且是异步操作
// 约束条件
Constraints constraints = new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED) // 约束条件,必须是网络连接
.build();
// 构建Request
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(MainWorker7.class)
.setConstraints(constraints)
.build();
// 加入队列
WorkManager.getInstance(this).enqueue(request);
执行,app 存活的时候执行操作,下次打开任务一定执行
初始化
WorkManger 的初始化在打车apk 的时候动态生成了四大组件的provider
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:exported="false"
android:multiprocess="true"
android:authorities="com.derry.workmanagersimple.workmanager-init"
android:directBootAware="false" />
使用Android 最常用的构造者设计模式进行初始化
WorkManager.initialize(getContext(), new Configuration.Builder().build());
初始化了,配置,异步线程池,和数据库,数据库创建是非常重要是任务再次执行的关键TAG
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public WorkManagerImpl(
@NonNull Context context,
@NonNull Configuration configuration,
@NonNull TaskExecutor workTaskExecutor,
boolean useTestDatabase) {
this(context,
configuration,
workTaskExecutor,
WorkDatabase.create(
context.getApplicationContext(),
workTaskExecutor.getBackgroundExecutor(),
useTestDatabase)
);
}
执行任务
执行任务异步的EnqueueRunnable
WorkManager.getInstance(this).enqueue(request);
EnqueueRunnable runnable = new EnqueueRunnable(this);
mWorkManagerImpl.getWorkTaskExecutor().executeOnBackgroundThread(runnable);
调度器执行获取核心逻辑
-
addToDatabase() 添加到初始化完成的数据库内 获取builder 配置
-
scheduleWorkInBackground() 调度到后台工作
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));
}
}
- Schedulers.schedule 执行调度工作
@VisibleForTesting
public void scheduleWorkInBackground() {
WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();
Schedulers.schedule(
workManager.getConfiguration(),
workManager.getWorkDatabase(),
workManager.getSchedulers());
}
4. 调度器执行 GreedySchedule WorkManagerImp startWork(调度器模式/策略模式)
public void startWork(
@NonNull String workSpecId,
@Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
mWorkTaskExecutor
.executeOnBackgroundThread(
new StartWorkRunnable(this, workSpecId, runtimeExtras));
}
StartWorkRunnable--》
mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras);
5. workWrapper mWorker.startWork() 最终会调用到 Worker的doWork 我们使用篇继承的就是Worker 最终形成闭环执行,首尾呼应
Processor().startWork
WorkerWrapper workWrapper;
mWorkTaskExecutor.getBackgroundExecutor().execute(workWrapper);
workWrapper.runWorker()
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()
启动任务后退出app进程的再次启动执行
- 在使用篇的时候我们定义了网络约束,同时打包过程的时候系统帮助自动注册了网络广播接收者,这也是退出进程再次打开app任务再次执行的发起点
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>
- ConstraintProxy 启动服务 SystemAlarmService 系统服务
@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);
}
static Intent createConstraintsChangedIntent(@NonNull Context context) {
Intent intent = new Intent(context, SystemAlarmService.class);
intent.setAction(ACTION_CONSTRAINTS_CHANGED);
return intent;
}
3 分发器准备工作,更换TAG为ACTION_DELAY_MET 执行 handleDelayMet(intent, startId, dispatcher);
SystemAlarmService
mDispatcher.add(intent, startId);
SystemAlarmDispatcher
processCommand();
mCommandHandler.onHandleIntent(mCurrentIntent, startId,
SystemAlarmDispatcher.this);
CommandHandler
void onHandleIntent(
@NonNull Intent intent,
int startId,
@NonNull SystemAlarmDispatcher dispatcher) {
String action = intent.getAction();
if (*ACTION_CONSTRAINTS_CHANGED*.equals(action)) {
handleConstraintsChanged(intent, startId, dispatcher); // 更换 Tag 标记为 *ACTION_DELAY_MET*
} 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));
}
}
}
}
- 检查数据库的是否有有id,执行 startWork 回到执行任务的逻辑
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);
// 这里获取了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
mWorkConstraintsTracker.replace(Collections.*singletonList*(workSpec));
}
}
mDispatcher.getProcessor().startWork(mWorkSpecId);
mWorkTaskExecutor.getBackgroundExecutor().execute(workWrapper);
mDispatcher.getProcessor().startWork(mWorkSpecId);
mWorkTaskExecutor.getBackgroundExecutor().execute(workWrapper);
总结
存储任务相关配置,存储是永久性配置,也是一定会执行任务的关键点,不管是马上执行还是重新打开app重新执行,读取数据库信息再执行保证任务的准确性