关于android:Android-后台任务队列管理-Android-Priority-Job-Queue和WorkManager

6次阅读

共计 9344 个字符,预计需要花费 24 分钟才能阅读完成。

Android 后台任务队列治理 Android Priority Job Queue 和 WorkManager

一:前言
有人说“Android 的开发,玩的就是多线程”从某个角度来说的确如此,当初的 App 被设计的越来越简单,置信很多开发人员都因大量而又简单的后台任务(background work)而焦头烂额:Async-Task 和 Activity 的生命周期太过于耦合,尽管实现简略然而对于重要的后台任务还是不靠谱
ndroid-Priority-Job-Queue 是一款专门为 Android 平台编写的,实现了 Job Queue 的后台任务队列类库,可能轻松的在后盾执行定时工作,并且进步了用户体验和利用的稳定性。
二:Android Priority Job Queue(后盾治理工作队列)
其应用框架也很简便间接:

  • 结构一个工作管理器 JobManager,为咱们治理工作;
  • 自定义 Job 类,来作为工作的载体;
  • 在须要时,将自定义的 Job 类实例退出到 JobManager 中;

这样就 OK 了,JobManager 会依据优先级、持久性、负载平衡、提早,网络管制、分组等因素来治理工作的执行。因为是独立于各个 Activity,JobManager 为 Job 的执行提供了一个很好的生命周期,
第一步:增加依赖

dependencies {implementation 'com.birbit:android-priority-jobqueue:3.0.0'}

第二步:配置 JobManager
JobManager 是整个框架的外围。作为一个重型的对象,倡议 Application 只构建一个 JobManager 实例供全局应用

public class MyApplication extends Application {
    private JobManager jobManager;// 工作队列的 Job 治理
    private static MyApplication instance;
    @Override
    public void onCreate() {super.onCreate();
        instance = this;//1. Application 的实例
        configureJobManager();//2. 配置 JobMananger}

   // 公有结构器
   private MyApplication (){instance=this;}
    public JobManager getJobManager() {return jobManager;}

    public static MyApplication getInstance() {return instance;}

    private void configureJobManager() {
        //3. JobManager 的配置器,利用 Builder 模式
        Configuration configuration = new Configuration.Builder(this)
                .customLogger(new CustomLogger() {
                    private static final String TAG = "JOBS";
                    @Override
                    public boolean isDebugEnabled() {return true;}

                    @Override
                    public void d(String text, Object... args) {Log.d(TAG, String.format(text, args));
                    }

                    @Override
                    public void e(Throwable t, String text, Object... args) {Log.e(TAG, String.format(text, args), t);
                    }

                    @Override
                    public void e(String text, Object... args) {Log.e(TAG, String.format(text, args));
                    }
                })
                .minConsumerCount(1)// 起码的沉闷线程(这里配置是 1).maxConsumerCount(3)// 最多的开启线程(这里配置是 3).loadFactor(3)// 一个 Thread 设置多 3 个工作(也能够 Integer.MAX_VALUE,int 最大值).consumerKeepAlive(120)// 设置线程在没有工作的状况下放弃存活的时长,以秒为单位
                .build();
        jobManager = new JobManager(configuration);
    }
}
  • CustomLogger:日志设置,便于用户查看工作队列的工作信息,在调试的过程中很有用,前面剖析 JobManager 的任务调度时就会用到;
  • minConsumerCount&maxConsumerCount: 起码消费者和最多消费者数量,所谓的消费者就是开启的线程,用来执行工作。工作队列实际上就是一个生产者和消费者问题,用户是生产者,提交工作(Job),开启的线程就是消费者来执行工作,工作被执行就是“生产”。这里所谓的起码和最大将会上面具体解释;
  • loadFactor(int):其意义是设置多少个工作为一组被调配个一个消费者(Thread),也就是一个 Thread 最多要“承包”几个工作来执行;
  • consumerKeepAlive : 设置消费者在没有工作的状况下放弃存活的时长,以秒为单位,如果过了这个时长还没有工作,消费者线程就会被回收
    第三步:Job
    自定义 Job 类须要继承 Android-Priority-Job-Queue 提供的 Job 类
public abstract class BaseJob extends Job {protected BaseJob(Params params) {super(params);
    }
    // 工作退出队列并被保留在硬盘上,定义此时要解决的逻辑
    
     @Override
    public void onAdded() {LogUtils.i("onAdded:" + getClass().getSimpleName());
    }
// 工作开始执执行,在此定义工作的主题逻辑,当执行结束后,工作将被从工作队列中删除
    @Override
    public void onRun() throws Throwable {}
    // 工作勾销的时候要执行的逻辑
      @Override
    protected void onCancel(int cancelReason, @Nullable Throwable throwable) {
    
    // 调用开释
     release();}
    // 当 onRun()办法中抛出异样时,就会调用该函数,该函数返回 Job 类在执行产生异样时的应答策略,是从新执行还是勾销,或者是肯定工夫之后再尝试。@Override
    protected RetryConstraint shouldReRunOnThrowable(@NonNull Throwable throwable, int runCount, int maxRunCount) { }
    
     /**
     * 开始做工作
     */
    public abstract void doJob() throws Throwable;

    /**
     * 重写这个办法进行资源回收
     */
    protected void release() {}
}

第四步:Params 类
在这里咱们要特地说一下 Params 类,通过该类能够配置 Job 类的各种信息,同样是采纳链式调用

// 我这里继承方才抽象类 BaseJob
public class StopAppJob extends BaseJob{public StopAppJob(String pkgName, boolean isFirst) {// 这里咱们次要看一下父类调用,这里咱们结构的 Params 类,new Params(7): 是默认结构器传入工作优先级 7;groupBy():设置组 ID;delayInMs()::设置延迟时间,ms 为单位
        super(new Params(7).addTags(TAG).groupBy(SCRIPT_JOB).delayInMs(ONE_SECOND));
        this.pkgName = pkgName;
        this.isFirst = isFirst;
    }
    
     @Override
    public void onRun() throws Throwable {super.onRun();
        doJob();}

    @Override
    public void doJob() throws Throwable {//doSomething}
}

1. 默认结构器传入的是 int 参数是该工作的优先级,优先级越高,越优先执行。

public Params(int priority) {this.priority = priority;}

2.requireNetwork(): 设置该工作要求拜访网络
3.groupBy(String groupId):设置组 ID,被设置雷同组 ID 的工作,将会依照程序执行
4.persist():设置工作为可长久化的,长久化要求 Job 类为序列化的,这一点并不意外,因为一个类的内容只有序列化之后能力变成字节模式保留在硬盘上
5.delayInMs(long delayMs):设置延迟时间,ms 为单位,在该工夫之后再放入工作队列中。

第五步:执行 Job 工作

MyApplication.getJobManager()
                        .addJobInBackground(new StopAppJob(PackageInfoConfig.TAOBAO_PACKAGE_NAME, true));

这个库的 github:https://github.com/yigit/andr…
然而:这个我的项目曾经不保护了:它是在一个没有 JobScheduler、RxJava 不风行、Kotlin 甚至没有公开诞生的世界中设计的。基于 Java 语言的
当初大部分应用的是 WorkManager,故咱们须要学习一下 WorkManager

三:WorkManager 应用
WorkManager 是 Android Jetpack 的一部分,是用于后盾工作的架构组件,须要兼顾机会和有保障的执行。机会性执行意味着 WorkManager 将尽快实现您的后盾工作。有保障的执行意味着即便在来到应用程序的状况下,WorkManager 也会兼顾各种状况下开始逻辑工作。
WorkManager 是一个简略但非常灵活的库,它具备许多其余长处:

  • 反对异步一次性和定期工作
  • 反对网络条件,存储空间和充电状态等束缚
  • 链接简单的工作申请,包含并行运行工作
  • 一个工作申请的输入用作下一个工作的输出
  • 将 API 级别的兼容性解决回 API 级别 14(请参阅正文)
  • 能够应用或不应用 Google Play 服务
  • 遵循零碎衰弱最佳实际
  • LiveData 反对可轻松在 UI 中显示工作申请状态

WorkManager 几个要害的类:
Worker: 工作的执行类,是一个抽象类须要集成它来实现要执行的工作
WorkRequest: 指定哪个 Worker 执行工作,能够向 WorkRequest 中增加细节,指定执行环境,执行程序,ID 等。WorkRequest 是个抽象类,在代码中应用其子类 OneTimeWorkRequest 或者 PeriodicWorkRequest
WorkManager: 对 WorkRequest 进行排队和治理
WorkStatus: 蕴含工作的状态和信息,以 LiveData 的模式提供给观察者
1. 第一步增加依赖
Java

// Java
    implementation "androidx.work:work-runtime:2.5.0"

Kotlin

    // kotlin  workmanager
    implementation "androidx.work:work-runtime-ktx:2.5.0"

2. 第二步:应用 Worker 类定义工作

public class SendLogsWorker extends Worker {public SendLogsWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {super(context, workerParams);
    }

    @NonNull
    @Override
    public Result doWork() {// 耗时工作执行在 doWork()中执行
        Log.d("aa","日志");
        return Result.success();}
}

源码:

public abstract class Worker extends ListenableWorker {

    // Package-private to avoid synthetic accessor.
    SettableFuture<Result> mFuture;

    @Keep
    @SuppressLint("BanKeepAnnotation")
    public Worker(@NonNull Context context, @NonNull WorkerParameters workerParams) {super(context, workerParams);
    }。。。。。}

doWork()有 3 种返回类型
执行胜利 Result.success()
执行失败 Result.failure()
从新执行 Result.retry()
3. 第三步:应用 WorkRequest 分配任务
WorkRequest 是一个抽象类,它的两种实现形式:OneTimeWorkRequest 和 PeriodicWorkRequest 别离对应的是一次性工作和周期性工作

OneTimeWorkRequesty 一次申请工作

public class TwelveActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_twelve);
        // 触发条件
       Constraints constraints=new Constraints.Builder()
                .setRequiresCharging(true)// 设置充电状态,默认是 flase
                .setRequiredNetworkType(NetworkType.UNMETERED)//Wifi
                .setRequiresStorageNotLow(true)// 内存低,不工作
                .setRequiresBatteryNotLow(true)// 电量低,不工作
                .build();
        // 一次性工作
        OneTimeWorkRequest oneTimeWorkRequest=new OneTimeWorkRequest.Builder(SendLogsWorker.class)
               .setConstraints(constraints)
                .setInitialDelay(10,TimeUnit.SECONDS)// 设置提早执行 10s
                .build();
        WorkManager.getInstance(this).enqueueUniqueWork("sendLogs", ExistingWorkPolicy.KEEP,oneTimeWorkRequest);
}

PeriodicWorkRequest 反复工作申请

 PeriodicWorkRequest sendLogsWorkRequest=new PeriodicWorkRequest.Builder(SendLogsWorker.class,5, TimeUnit.SECONDS)// 定义 5s 周期,是没有成果的
 //PeriodicWorkRequest 应用和 OneTimeWorkRequest 没有太大区别, 须要留神的是, 间隔时间不能少于 15 分钟.
                .setConstraints(new Constraints.Builder().setRequiresCharging(true).build())
                .build();
                
   // 后果日志,能够看出相差 15 分钟
2021-07-06 14:07:04.265 25585-25647/com.ruan.mygitignore D/aa: 日志
2021-07-06 14:22:04.398 25585-26000/com.ruan.mygitignore D/aa: 日志
2021-07-06 14:37:04.565 25585-26356/com.ruan.mygitignore D/aa: 日志


触发条件:

  • setRequiredNetworkType(NetworkType.UNMETERED) // 设置须要的网络条件 Wifi(UNMETERED(Wifi),CONNECTED(任何网络),NOT_REQUIRED(没有要求),NOT_ROAMING(连贯非漫游网络),METERED(连贯按流量计费的网络))
  • setRequiresStorageNotLow(true)// 如果设置为 true,那么当用户设施上的存储空间有余时,工作不会运行。
  • setRequiresBatteryNotLow(true)// 如果设置为 true,那么当用户设施上的电量有余时,工作不会运行。

WorkRequest

new OneTimeWorkRequest.Builder(SendLogsWorker.class)
               .setConstraints(constraints)
               .setBackoffCriteria(BackoffPolicy.LINEAR,OneTimeWorkRequest.MIN_BACKOFF_MILLIS,TimeUnit.MICROSECONDS)//10s 重试和退却政策
                .setInitialDelay(10,TimeUnit.SECONDS)// 提早执行
                .addTag("log")// 增加 Tag 标记
                .build();
  • setInitialDelay(10,TimeUnit.SECONDS)// 如果工作没有束缚,或者当工作退出队列时所有束缚都失去了满足,那么零碎可能会抉择立刻运行该工作。如果您不心愿工作立刻运行,能够将工作指定为在通过一段最短初始延迟时间后再启动
  • setBackoffCriteria(BackoffPolicy.LINEAR,OneTimeWorkRequest.MIN_BACKOFF_MILLIS,TimeUnit.MICROSECONDS)//10s 重试和退却政策, 最短退却延迟时间设置为容许的最小值,即 10 秒。因为政策为 LINEAR,每次尝试重试时,重试距离都会减少约 10 秒。例如,第一次运行以 Result.retry() 完结并在 10 秒后重试;而后,如果工作在后续尝试后持续返回 Result.retry(),那么接下来会在 20 秒、30 秒、40 秒后重试,以此类推。如果退却政策设置为 EXPONENTIAL,那么重试时长序列将靠近 20、40、80 秒
  • addTag(“log”)// 每个工作申请都有一个惟一标识符,该标识符可用于在当前标识该工作,以便勾销工作或察看其进度, 例如,WorkManager.cancelAllWorkByTag(String) 会勾销带有特定标记的所有工作申请,WorkManager.getWorkInfosByTag(String) 会返回一个 WorkInfo 对象列表,该列表可用于确定当前工作状态。
    工作状态
    一次性工作的状态

定期工作的状态

调度工作

// 一次性工作
 WorkManager.getInstance(this).enqueueUniqueWork("sendLogs", ExistingWorkPolicy.REPLACE,oneTimeWorkRequest);
 // 反复工作
  WorkManager.getInstance(this).enqueueUniquePeriodicWork("sendLogs", ExistingPeriodicWorkPolicy.KEEP,sendLogsWorkRequest);

WorkManager.enqueueUniqueWork()(用于一次性工作)
WorkManager.enqueueUniquePeriodicWork()(用于定期工作)
这两种办法都承受 3 个参数:

  • uniqueWorkName – 用于惟一标识工作申请的 String。
  • existingWorkPolicy – 此 enum 可告知 WorkManager:如果已有应用该名称且尚未实现的惟一工作链,应执行什么操作。如需理解详情,请参阅抵触解决政策。
  • work – 要调度的 WorkRequest。

抵触解决政策
调度惟一工作时,您必须告知 WorkManager 在发生冲突时要执行的操作。您能够通过在将工作退出队列时传递一个枚举来实现此目标。

对于一次性工作,您须要提供一个 ExistingWorkPolicy,它反对用于解决抵触的 4 个选项。

  • REPLACE:用新工作替换现有工作。此选项将勾销现有工作。
  • KEEP:保留现有工作,并疏忽新工作。
  • APPEND:将新工作附加到现有工作的开端。此政策将导致您的新工作链接到现有工作,在现有工作实现后运行。
  • 现有工作将成为新工作的先决条件。如果现有工作变为 CANCELLED 或 FAILED 状态,新工作也会变为 CANCELLED 或 FAILED。如果您心愿无论现有工作的状态如何都运行新工作,请改用 APPEND_OR_REPLACE。
  • APPEND_OR_REPLACE 函数相似于 APPEND,不过它并不依赖于先决条件工作状态。即便现有工作变为 CANCELLED 或 FAILED 状态,新工作仍会运行。
    对于定期工作,您须要提供一个 ExistingPeriodicWorkPolicy,它反对 REPLACE 和 KEEP 这两个选项。这些选项的性能与其对应的 ExistingWorkPolicy 性能雷同。

工作链
WorkManager 容许咱们依照肯定的程序执行工作,比方我想 A、B、C 三个工作按先后顺序执行:

WorkManager.getInstance()
        .beginWith(workA)
        .then(workB)
        .then(workC)
        .enqueue();

再更简单一点,我想 A 和 B 同时执行,它们都执行完之后,再执行 C:

WorkManager.getInstance()
        .beginWith(workA,workB)
        .then(workC)
        .enqueue();


这样就须要先把 A、B 和 C、D 别离组成一条工作链,再进行联结:

// 这里 Kolin 语言实现
val chain1 = WorkManager.getInstance()
        .beginWith(workA)
        .then(workB)
val chain2 = WorkManager.getInstance()
        .beginWith(workC)
        .then(workD)
val chain3 = WorkContinuation
        .combine(chain1, chain2)
        .then(workE)
chain3.enqueue()

结尾:所有美妙的事物,终将毁于工夫

正文完
 0