目录1、前言2、使用2.1、引用2.2 使用2.3 重要概念3、原理3.1 约束检测3.2 任务调度器3.3、任务流程4、总结1、前言 WorkManager 是适合用于持
WorkManager 是适合用于持久性工作的推荐解决方案。如果工作始终要通过应用重启和系统重新启动来调度,便是持久性的工作。由于大多数后台处理操作都是通过持久性工作完成的,因此 WorkManager 是适用于后台处理操作的主要推荐 api。 WorkManager 可处理三种类型的持久性工作:
implementation "Androidx.work:work-runtime:2.7.1" //基础使用
implementation "androidx.work:work-multiprocess:2.7.1" //跨进程时引用
执行一次性任务
Data data = new Data.Builder().putBoolean("is_test", false).build();
WorkManager.getInstance(this).enqueue(
new OneTimeWorkRequest.Builder(Test.class) // 执行任务一次性任务
.setInputMerger(NewInputMerge.class) // 输入数据合并策略,这里并没有用,链式处理时,多个上流执行结果合并,作为下流输入数据
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 5, TimeUnit.MINUTES) // 重试策略
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) // 加急处理
.addTag("test").addTag("huahua") // 标识
.setInputData(data) // 输入数据
.setInitialDelay(5, TimeUnit.SECONDS) // 执行延时时间
.setConstraints(new Constraints.Builder().setRequiresBatteryNotLow(true).build()) // 约束,部分约束只对高版本有效
.build()
);
执行周期性任务
WorkManager.getInstance(this).enqueue(
new PeriodicWorkRequest.Builder(Test.class, 2, TimeUnit.HOURS) // 执行周期性任务,周期2小时
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 5, TimeUnit.MINUTES)
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
.addTag("test").addTag("huahua")
.setInputData(data)
.setInitialDelay(5, TimeUnit.SECONDS) // 首次执行延时时间
.setConstraints(new Constraints.Builder().setRequiresBatteryNotLow(true).build())
.build()
);
拥有名字的任务
WorkManager.getInstance(this).enqueueUniqueWork("test", ExistingWorkPolicy.KEEP,
new OneTimeWorkRequest.Builder(Test.class).build()); // 此种方法会对重复名字的任务进行处理
监听状态
WorkManager.getInstance(this).getWorkInfosByTagLiveData("test").observe(this,
new Observer<List<WorkInfo>>() {
@Override
public void onChanged(List<WorkInfo> workInfos) {
}
});
定义Work代码
public class Test extends Worker {
public Test(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
Data input = getInputData(); // 获取输入数据
boolean isTest = input.getBoolean("is_test", true);
}
@NonNull
@Override
public Result doWork() {
return Result.success();
}
}
继承使Worker类,实现doWork()方法,此方法是实现任务的主体;doWork()返回的 Result会通知 WorkManager 服务工作是否成功,以及工作失败时是否应重试工作。
配置初始化 不同的版本初始化不同,但是都是通过Provider来进行的,2.6之前是WorkManagerInitializer, 2.6之后是InitializationProvider;这里按照2.7.1的版本来说,老版本有需要留言回复;有两种处理方案
按照提供初始化流程处理
1.首先注意一个字符串配置:这个很重要,不建议修改
<string name="androidx_startup" translatable="false">androidx.startup</string>
2.提供实现Initializer的类,这个类是被调用的关键
3.移除默认实现
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="androidx.work.WorkManagerInitializer"
android:value="androidx.startup"
tools:node="remove" /> // 这表示移除
</provider>
默认实现Initializer类WorkManagerInitializer,使用的默认configuration
public final class WorkManagerInitializer implements Initializer<WorkManager> {
private static final String TAG = Logger.tagWithPrefix("WrkMgrInitializer");
@NonNull
@Override
public WorkManager create(@NonNull Context context) {
Logger.get().debug(TAG, "Initializing WorkManager with default configuration.");
WorkManager.initialize(context, new Configuration.Builder().build()); // 这里提供Configuration
return WorkManager.getInstance(context);
}
@NonNull
@Override
public List<Class<? extends androidx.startup.Initializer<?>>> dependencies() {
return Collections.emptyList(); // 提供需要执行的其它Initializer
}
}
4.进行注册自定义实现; 在meta-data数据处理,key为你需要调用的初始化类,value必须为R.sting.androidx_startup这个字符串的值; 如下
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge" >
<meta-data
android:name="androidx.work.WorkManagerInitializer" // 这里为你的实现的Initializer
android:value="androidx.startup" /> // 这里必须与R.sting.androidx_startup保持一致
</provider>
任务标识
任务类型
任务链
使用 WorkManager 创建工作链并将其加入队列。工作链用于指定多个依存任务并定义这些任务的运行顺序。使用 WorkManager.beginWith(OneTimeWorkRequest 或 WorkManager.beginWith(List)返回WorkContinuation实例,然后通过其 then(OneTimeWorkRequest)或 then(List)添加任务链,最后使用WorkContinuation.enqueue()进行执行。
任务链先添加的任务为后续任务的先决条件, 也就是前面任务成功了后面任务才会执行
重试退避政策
针对worker执行返回Result.retry()时处理策略, 使用方法setBackoffCriteria设置, 有两个指标,策略和延时时间(允许的最小值,即 10 秒);情况有下面2种
输入合并器
针对一次性任务,且在工作链中,父级工作请求的输出将作为子级的输入传入的场景中使用。输入中会存在相同关键字,这时,输入就会存在冲突
WorkManager 提供两种不同类型的 InputMerger:
冲突解决政策
调度唯一工作时,发生冲突时要执行的操作。可以通过在将工作加入队列时传递一个枚举来实现此目的。
对于一次性工作,您需要提供一个 ExistingWorkPolicy,有4 个选项。
CANCELLED
或 FAILED
状态,新工作也会变为 CANCELLED
或 FAILED
。如果您希望无论现有工作的状态如何都运行新工作,请改用 APPEND_OR_REPLACE
。CANCELLED
或 FAILED
状态,新工作仍会运行。约束条件
任务执行的先决条件,使用Contraints.Builder()进行构建实例。有一下约束
还有以下约束,对于>=24的版本有效:
合理分为几个部分来说
这里有需要理解的一个技术点:SettableFuture,实现了ListenableFuture接口,并且增加了下面接口ListenableFuture。这个类在源码中频繁使用
ListenableFuture只有一个方法
void addListener(Runnable listener, Executor executor)
调用了这个方法,这个类才有使用意义;调用之后,表示SettableFuture若完成,则使用executor执行listener
另外一个让我觉得有意思的地方,或者说不同之前future的地方是, SettableFuture未实现Runnable接口,也就是其结果不是来源于自己,来源于调用下面3个方法
public boolean set(@Nullable V value) // 正常设置结果
public boolean setException(Throwable throwable) // 执行结果异常
public boolean setFuture(ListenableFuture<? extends V> future) // 设置执行结果来源,也就是ListenableFuture的执行结果
类图如下
结合源码分析,结论有:
实际检测技术点,也就是ConstraintTracker实现
3.1.1 StorageNotLowTracker类
存储空间的是否低,根据系统广播来处理
Intent.ACTION_DEVICE_STORAGE_OK // 存储空间够用
Intent.ACTION_DEVICE_STORAGE_LOW // 存储空间很低
初步检测状态:
@Override
public Boolean getInitialState() {
Intent intent = mAppContext.reGISterReceiver(null, getIntentFilter());
if (intent == null || intent.getAction() == null) {
return true;
} else {
switch (intent.getAction()) {
case Intent.ACTION_DEVICE_STORAGE_OK:
return true;
case Intent.ACTION_DEVICE_STORAGE_LOW:
return false;
default:
return null;
}
}
}
实时更新通过接收广播信息:
public void onBroadcastReceive(Context context, @NonNull Intent intent) {
if (intent.getAction() == null) {
return;
}
switch (intent.getAction()) {
case Intent.ACTION_DEVICE_STORAGE_OK:
setState(true);
break;
case Intent.ACTION_DEVICE_STORAGE_LOW:
setState(false);
break;
}
}
3.1.2 BatteryChargingTracker类
充电状态,同样通过广播来进行处理;
接收广播根据版本不同而不同如下:
public IntentFilter getIntentFilter() {
IntentFilter intentFilter = new IntentFilter();
if (Build.VERSION.SDK_INT >= 23) {
intentFilter.addAction(BatteryManager.ACTION_CHARGING);
intentFilter.addAction(BatteryManager.ACTION_DISCHARGING);
} else {
intentFilter.addAction(Intent.ACTION_POWER_CONNECTED);
intentFilter.addAction(Intent.ACTION_POWER_DISCONNECTED);
}
return intentFilter;
}
初始状态:
@Override
public Boolean getInitialState() {
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent intent = mAppContext.registerReceiver(null, intentFilter);
if (intent == null) {
Logger.get().error(TAG, "getInitialState - null intent received");
return null;
}
return isBatteryChangedIntentCharging(intent);
}
private boolean isBatteryChangedIntentCharging(Intent intent) {
boolean charging;
if (Build.VERSION.SDK_INT >= 23) {
int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
charging = (status == BatteryManager.BATTERY_STATUS_CHARGING
|| status == BatteryManager.BATTERY_STATUS_FULL);
} else {
int chargePlug = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
charging = (chargePlug != 0);
}
return charging;
}
实时状态:
@Override
public void onBroadcastReceive(Context context, @NonNull Intent intent) {
String action = intent.getAction();
if (action == null) {
return;
}
Logger.get().debug(TAG, String.fORMat("Received %s", action));
switch (action) {
case BatteryManager.ACTION_CHARGING:
setState(true);
break;
case BatteryManager.ACTION_DISCHARGING:
setState(false);
break;
case Intent.ACTION_POWER_CONNECTED:
setState(true);
break;
case Intent.ACTION_POWER_DISCONNECTED:
setState(false);
break;
}
}
3.1.3 BatteryNotLowTracker类
电量是否低,同样通过广播实时获取状态
Intent.ACTION_BATTERY_OKAY //电量正常
Intent.ACTION_BATTERY_LOW //电量低
初始状态:
@Override
public Boolean getInitialState() {
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent intent = mAppContext.registerReceiver(null, intentFilter);
if (intent == null) {
Logger.get().error(TAG, "getInitialState - null intent received");
return null;
}
int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
float batteryPercentage = level / (float) scale;
return (status == BatteryManager.BATTERY_STATUS_UNKNOWN
|| batteryPercentage > BATTERY_LOW_THRESHOLD); // 这里是小于15%算是低
}
实时更新:
@Override
public void onBroadcastReceive(Context context, @NonNull Intent intent) {
if (intent.getAction() == null) {
return;
}
Logger.get().debug(TAG, String.format("Received %s", intent.getAction()));
switch (intent.getAction()) {
case Intent.ACTION_BATTERY_OKAY:
setState(true);
break;
case Intent.ACTION_BATTERY_LOW:
setState(false);
break;
}
}
3.1.4 NetworkStateTracker类
网络状态,这个根据版本不同使用也不同
初始状态:
NetworkState getActiveNetworkState() {
NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
boolean isConnected = info != null && info.isConnected();
boolean isValidated = isActiveNetworkValidated();
boolean isMetered = ConnectivityManagerCompat.isActiveNetworkMetered(mConnectivityManager);
boolean isNotRoaming = info != null && !info.isRoaming();
return new NetworkState(isConnected, isValidated, isMetered, isNotRoaming);
}
实时状态: 在回调NetworkCallback或者接收到广播onReceive时,调用getActiveNetworkState得到
实现Scheduler接口,调度器有3个:
调度器的创建在WorkManagerImpl类
public List<Scheduler> createSchedulers(
@NonNull Context context,
@NonNull Configuration configuration,
@NonNull TaskExecutor taskExecutor) {
return Arrays.asList(
Schedulers.createBestAvailableBackgroundScheduler(context, this), // 可以被系统唤起调用手段
new GreedyScheduler(context, configuration, taskExecutor, this)); // 进程内调用手段
}
调度器被调用的逻辑在Schedulers
public static void schedule(Configuration configuration,WorkDatabase workDatabase,List<Scheduler> schedulers) {
if (schedulers == null || schedulers.size() == 0) {
return;
}
WorkSpecDao workSpecDao = workDatabase.workSpecDao();
List<WorkSpec> eligibleWorkSpecsForLimitedSlots;
List<WorkSpec> allEligibleWorkSpecs;
workDatabase.beginTransaction();
try {
eligibleWorkSpecsForLimitedSlots = workSpecDao.getEligibleWorkForScheduling(configuration.getMaxSchedulerLimit());
allEligibleWorkSpecs = workSpecDao.getAllEligibleWorkSpecsForScheduling(MAX_GREEDY_SCHEDULER_LIMIT);
if (eligibleWorkSpecsForLimitedSlots != null && eligibleWorkSpecsForLimitedSlots.size() > 0) {
long now = System.currentTimeMillis();
for (WorkSpec workSpec : eligibleWorkSpecsForLimitedSlots) {
workSpecDao.markWorkSpecScheduled(workSpec.id, now); // 进行插槽状态处理,非默认才会被GreedyScheduler进行调度
}
}
workDatabase.setTransactionSuccessful();
} finally {
workDatabase.endTransaction();
}
if (eligibleWorkSpecsForLimitedSlots != null && eligibleWorkSpecsForLimitedSlots.size() > 0) {
WorkSpec[] eligibleWorkSpecsArray =
new WorkSpec[eligibleWorkSpecsForLimitedSlots.size()];
eligibleWorkSpecsArray =
eligibleWorkSpecsForLimitedSlots.toArray(eligibleWorkSpecsArray);
for (Scheduler scheduler : schedulers) {
if (scheduler.hasLimitedSchedulingSlots()) {
scheduler.schedule(eligibleWorkSpecsArray);
}
}
}
if (allEligibleWorkSpecs != null && allEligibleWorkSpecs.size() > 0) {
WorkSpec[] enqueuedWorkSpecsArray = new WorkSpec[allEligibleWorkSpecs.size()];
enqueuedWorkSpecsArray = allEligibleWorkSpecs.toArray(enqueuedWorkSpecsArray);
for (Scheduler scheduler : schedulers) {
if (!scheduler.hasLimitedSchedulingSlots()) {
scheduler.schedule(enqueuedWorkSpecsArray);
}
}
}
}
GreedyScheduler调度器处理任务正在排队,任务最大限制是200条;其它调度器执行在排队且未被调度器处理的,且和现在调度器已经处理的总和不超过配置中的最大限制,默认是版本23是10,其它版本20
3.2.1 GreedyScheduler调度器
类图:
通过流程分析有以下结论:
3.2.2 SystemJobScheduler调度器
类图:
这个调度器比较简单,其实现主要通过了JobScheduler的实现,JobScheduler的实现原理这里不做介绍
3.2.3 SystemAlarmScheduler调度器
这个调度器实现就比较复杂了;类图:
任务流程涉及调度的具体过程,在以下流程图中就会直接从Schedulers方法直接异步执行启动任务;调度过程省略;
3.3.1 enqueue流程
WorkManager调用时,均是先包装成WorkContinuationImpl然后调用其方法enqueue执行;
WorkContinuationImpl: 包含了一个链表,指向了所有依赖的WorkContinuation对象;也记录了任务本身的一些信息和流程状态
流程图
3.3.2 cancel流程
取消时,可以通过任务的id、name、tag或者取消所有;不管是哪种调用,都是通过标识获取满足条件的任务id集合,并进行取消;其流程也只是在id集合查询时不同而已
流程图
上面只是大概介绍了常规使用,以及相关类图、流程,代码细节并没有展示。代码中的一些技术点需要至少会用
从代码架构上,也有需要去多思考的地方
以上就是Android WorkManager使用以及源码分析的详细内容,更多关于Android WorkManager的资料请关注编程网其它相关文章!
--结束END--
本文标题: AndroidWorkManager使用以及源码分析
本文链接: https://www.lsjlt.com/news/195815.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
下载Word文档到电脑,方便收藏和打印~
2024-01-21
2023-10-28
2023-10-28
2023-10-27
2023-10-27
2023-10-27
2023-10-27
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0