WorkManage

简单学习

最新库的版本以及更新日志
https://developer.android.google.cn/jetpack/androidx/releases/work?hl=zh-cn
首先导入库
下边的库看自己需求添加,并非所有的都需要,比如java和kotlin两种

    dependencies {
      def work_version = "2.2.0"

        // (Java only)
        implementation "androidx.work:work-runtime:$work_version"

        // Kotlin + coroutines
        implementation "androidx.work:work-runtime-ktx:$work_version"

        // optional - RxJava2 support
        implementation "androidx.work:work-rxjava2:$work_version"

        // optional - GCMNetworkManager support
        implementation "androidx.work:work-gcm:$work_version"

        // optional - Test helpers
        androidTestImplementation "androidx.work:work-testing:$work_version"
      }
    

创建任务

继承Worker类,实现doWork方法,doWork() 方法在 WorkManager 提供的后台线程上同步运行

    public class UploadWorker extends Worker {

        public UploadWorker(
            @NonNull Context context,
            @NonNull WorkerParameters params) {
            super(context, params);
        }

        @Override
        public Result doWork() {
          // Do the work here--in this case, upload the images.

          uploadImages()

          // Indicate whether the task finished successfully with the Result
          return Result.success()
        }
    }

已成功完成:Result.success()
已失败:Result.failure()
需要稍后重试:Result.retry()
成功和失败,还可以传个Data数据,给下一个worker或者自己接收

        val data = Data.Builder().apply {
//可以put一些基础数据类型
            putString("key", "from My Worker");
        }.build()
简单介绍下几种任务
  1. Worker
public abstract class Worker extends ListenableWorker
  1. CoroutineWorker [kotlin下用的]
abstract class CoroutineWorker(
    appContext: Context,
    params: WorkerParameters
) : ListenableWorker(appContext, params)

配置运行任务的方式和时间

worker弄好了,我们还要确定任务执行的方式,是一次性的,还是重复性的,系统也提供了对应的类

    OneTimeWorkRequest uploadWorkRequest = new OneTimeWorkRequest.Builder(UploadWorker.class)
            .build()
    

代码简单的写了这两种任务的实例

//任务运行的一些条件设置
        var constraints=Constraints.Builder()
                .setRequiredNetworkType(NetworkType.CONNECTED)
                .setRequiresBatteryNotLow(false)
                .setRequiresCharging(false)
                .setRequiresDeviceIdle(false)
                .setTriggerContentMaxDelay(10,TimeUnit.MINUTES)
                .setTriggerContentUpdateDelay(10,TimeUnit.SECONDS)
                .build()
//这个数据在Worker里可以通过getInputData()方法拿到
        var input=Data.Builder()
                .putString("url","www.xxx.xxx")
                .putInt("retry",3)
                .build()
        var request= OneTimeWorkRequest.Builder(MyWoker::class.java)
                .addTag("xxx")
                .setConstraints(constraints)
                .setInitialDelay(10,TimeUnit.SECONDS)
                .setInputData(input)
                .build()

        var request2=PeriodicWorkRequest.Builder(MyWoker::class.java, 10,TimeUnit.MINUTES)
                .setConstraints(constraints)
                .setInitialDelay(10,TimeUnit.SECONDS)
                .setInputData(input)
                .build()

kotlin版本的

        request= OneTimeWorkRequestBuilder()
                .setConstraints(constraints)
                .setInitialDelay(10,TimeUnit.SECONDS)
                .setInputData(input)
                .build()

        request2= PeriodicWorkRequestBuilder(10,TimeUnit.MINUTES)
                .setConstraints(constraints)
                .setInitialDelay(10,TimeUnit.SECONDS)
                .setInputData(input)
                .build()

其他:
PeriodicWorkRequest的Builder还有一种构造方法,可以传入flex时间,不传的默认和repeat interval时间一样

        public Builder(
                @NonNull Class workerClass,
                long repeatInterval,
                @NonNull TimeUnit repeatIntervalTimeUnit,
                long flexInterval,
                @NonNull TimeUnit flexIntervalTimeUnit)

这个时间有啥用,看下文档
就是说你repeat时间是15分钟,flex时间是5分钟,那么其实就相当于每次延迟5分钟执行任务,当然不是5分钟以后立马执行,worker这东西时间不是确定的,它是在这个时间段执行

 Note that flex  intervals are ignored for certain OS versions (in particular, API 23).
//23的设备,这个字段无效

         * 

         * [     before flex     |     flex     ][     before flex     |     flex     ]...
         * [   cannot run work   | can run work ][   cannot run work   | can run work ]...
         * \____________________________________/\____________________________________/...
         *                interval 1                            interval 2             ...(repeat)

间隔任务时间是有限制的,你设置比这个小没用,它会自动改成这个最小值

    /**
     * The minimum interval duration for {@link PeriodicWorkRequest} (in milliseconds).
     */
    public static final long MIN_PERIODIC_INTERVAL_MILLIS = 15 * 60 * 1000L; // 15 minutes.
    /**
     * The minimum flex duration for {@link PeriodicWorkRequest} (in milliseconds).
     */
    public static final long MIN_PERIODIC_FLEX_MILLIS = 5 * 60 * 1000L; // 5 minutes.

开始执行任务

上边request也有了,worker也有了,那么现在让request开始处理worker吧
首先获取WorkManager的实例,以前没有context参数,还得手动init,现在不用了,传个context,会自己判断有没有init来生成对应的实例

WorkManager.getInstance(applicationContext)

执行,有beginWith,then,enqueue操作

 WorkManager.getInstance(applicationContext).beginWith(request).then(request3).then(request).enqueue()
        WorkManager.getInstance(applicationContext).enqueue(arrayListOf(request,request3,request2))

beginWith,then只能执行一次性的任务
enqueue啥任务都行
参数可以是单个任务,也可以是多个任务【集合】

public final @NonNull WorkContinuation beginWith(@NonNull OneTimeWorkRequest work)
public abstract @NonNull WorkContinuation beginWith(@NonNull List work)

public final Operation enqueue(@NonNull WorkRequest workRequest)
public abstract Operation enqueue(@NonNull List requests)

public final @NonNull WorkContinuation then(@NonNull OneTimeWorkRequest work)
public abstract @NonNull WorkContinuation then(@NonNull List work)
public abstract @NonNull Operation enqueue();

上边的这些还有另外一种UniqueWork
顾名思义,就是对于uniqueWorkName一样的任务只能有一个,那已经有咋办,由第二个参数来决定

    public abstract @NonNull WorkContinuation beginUniqueWork(
            @NonNull String uniqueWorkName,
            @NonNull ExistingWorkPolicy existingWorkPolicy,
            @NonNull List work)

3个看名字大概就知道啥意思了,如果没有存在的同名任务,那么添加一个新的,这时候这个参数无意义,大家都一样
replace:有老的,把老的取消删除
keep:有老的,就继续老的,新的其实就等于没用到
append:就是把现有的任务 添加到老的任务下边,您不能将 APPEND 与 PeriodicWorkRequest 一起使用。

public enum ExistingWorkPolicy {

    /**
     * If there is existing pending (uncompleted) work with the same unique name, cancel and delete
     * it.  Then, insert the newly-specified work.
     */
    REPLACE,

    /**
     * If there is existing pending (uncompleted) work with the same unique name, do nothing.
     * Otherwise, insert the newly-specified work.
     */
    KEEP,

    /**
     * If there is existing pending (uncompleted) work with the same unique name, append the
     * newly-specified work as a child of all the leaves of that work sequence.  Otherwise, insert
     * the newly-specified work as the start of a new sequence.
     */
    APPEND
}

取消任务

tag 可以在workerRequest里设置,上边有
uuid 可以通过workerRequest ,getId拿到
至于uniqueWork对应上边的uniqueWork
image.png

查看任务的状态

下边是方法,先拿到你要找的任务

WorkManager.getInstance(applicationContext).getWorkInfosByTag("xxx").get()[0].state
image.png
    public enum State {

        /**
         * Used to indicate that the {@link WorkRequest} is enqueued and eligible to run when its
         * {@link Constraints} are met and resources are available.
         */
        ENQUEUED,

        /**
         * Used to indicate that the {@link WorkRequest} is currently being executed.
         */
        RUNNING,

        /**
         * Used to indicate that the {@link WorkRequest} has completed in a successful state.  Note
         * that {@link PeriodicWorkRequest}s will never enter this state (they will simply go back
         * to {@link #ENQUEUED} and be eligible to run again).
         */
        SUCCEEDED,

        /**
         * Used to indicate that the {@link WorkRequest} has completed in a failure state.  All
         * dependent work will also be marked as {@code #FAILED} and will never run.
         */
        FAILED,

        /**
         * Used to indicate that the {@link WorkRequest} is currently blocked because its
         * prerequisites haven't finished successfully.
         */
        BLOCKED,

        /**
         * Used to indicate that the {@link WorkRequest} has been cancelled and will not execute.
         * All dependent work will also be marked as {@code #CANCELLED} and will not run.
         */
        CANCELLED;

        /**
         * Returns {@code true} if this State is considered finished.
         *
         * @return {@code true} for {@link #SUCCEEDED}, {@link #FAILED}, and * {@link #CANCELLED}
         *         states
         */
        public boolean isFinished() {
            return (this == SUCCEEDED || this == FAILED || this == CANCELLED);
        }
    }

删除完成的任务

包括,success,failed,cancelled

    • Is finished (succeeded, failed, or cancelled)
    • Has zero unfinished dependents

这些任务其实是写在用room库处理的数据库里的,就算worker执行完,任务信息也不会立马删除,还是在数据库了,只是状态变了而已,当然系统过一段时间会清理掉这些完成的任务的,具体多久不清楚
我们也可以手动删除已完成的任务,

workManager.pruneWork()

点击参考 源码研究

worker state 查看

进度设置,WorkManager 2.3.0-alpha01以上才有的

链接工作

比如,先有3个任务filter1,2,3,都一起做完了以后把结果告诉下个工作compress,现在看下compress咋获取到数据

    WorkManager.getInstance(myContext)
        // Candidates to run in parallel
        .beginWith(Arrays.asList(filter1, filter2, filter3))
        // Dependent work (only runs after all previous work in chain)
        .then(compress)
        .then(upload)
        // Don't forget to enqueue()
        .enqueue();

首先我们知道一个worker做完,可以在结果了插入数据

val output=Data.Builder().putString("path","out put data").build()
        return Result.success(output)

如果filter1,2,3是同一个worker,output里的key都一样,那咋拿到这些value?
这里可以用到InputMerger,对output里的数据进行处理

WorkManager 提供两种不同类型的 InputMerger
  • OverwritingInputMerger 会尝试将所有输入中的所有键添加到输出中。如果发生冲突,它会覆盖先前设置的键。
    举个例子:filter 返回的key都是path,那么compress里最后收到的只有一个path,就是最后执行的那个filter传出来的
  • ArrayCreatingInputMerger 会尝试合并输入,并在必要时创建数组
    这种把value封装成一个集合了,可以去看下源码,比较简单,比如你是string,设置这个以后,在compress任务里拿到的value就是个stringArray了
OneTimeWorkRequestBuilder().setInputMerger(ArrayCreatingInputMerger::class.java).build()

//设置了inputMerger为ArrayCreatingInputMerger以后,数据就封装成集合了
val input=inputData;
val path=input.getStringArray("path")

默认不设置的话,就是OverwritingInputMerger,也就是key一样的,后边的数据会覆盖前边的

创建 OneTimeWorkRequest 链时,需要注意以下几点:

从属 OneTimeWorkRequest 仅在其所有父级 OneTimeWorkRequest 都成功完成(即返回 Result.success())时才会被解除阻塞(变为 ENQUEUED 状态)。

如果有任何父级 OneTimeWorkRequest 失败(返回 Result.failure()),则所有从属 OneTimeWorkRequest 也会被标记为 FAILED。

如果有任何父级 OneTimeWorkRequest 被取消,则所有从属 OneTimeWorkRequest 也会被标记为 CANCELLED

image.png

自定义 WorkManager 配置和初始化

前边讲过,新版库默认init了workerManager

public class WorkManagerInitializer extends ContentProvider {
    @Override
    public boolean onCreate() {
        // Initialize WorkManager with the default configuration.
        WorkManager.initialize(getContext(), new Configuration.Builder().build());
        return true;
    }

如果不满意,可以删掉,然后自己重写
先删掉这个ContentProvider


然后在application里继承Configuration.Provider,写自己的Configuration

    class MyApplication extends Application implements Configuration.Provider {
        @Override
        public Configuration getWorkManagerConfiguration() {
            return Configuration.Builder()
                    .setMinimumLoggingLevel(android.util.Log.INFO)
                    .build();
        }
    }
//下边是Configuration的属性,都可以通过builder里修改
    public static final class Builder {

        Executor mExecutor;
        WorkerFactory mWorkerFactory;
        Executor mTaskExecutor;

        int mLoggingLevel = Log.INFO;
        int mMinJobSchedulerId = IdGenerator.INITIAL_ID;
        int mMaxJobSchedulerId = Integer.MAX_VALUE;
        int mMaxSchedulerLimit = MIN_SCHEDULER_LIMIT;

然后看下下图,我们取消自动初始化以后,在application里实现Provider,这里就用到了


image.png

补充

  1. 如何停止worker
    ,比如下边你在执行一个循环任务的时候,可以加个条件,判断是否isStopped来决定任务是否继续下去
        public Result doWork() {
            for (int i = 0; i < 100; ++i) {
                if (isStopped()) {
                    break;
                }

                try {
                    downloadSynchronously("https://www.google.com");
                } catch (IOException e) {
                    return Result.failure();
                }
            }

            return Result.success();
        }
  1. kotlin下的CoroutineWorker 取消任务
    CoroutineWorker 通过取消协程并传播取消信号来自动处理停工情况。您无需执行任何特殊操作来处理停工任务
    class CoroutineDownloadWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {

        override val coroutineContext = Dispatchers.IO

        override suspend fun doWork(): Result = coroutineScope {
            val jobs = (0 until 100).map {
                async {
                    downloadSynchronously("https://www.google.com")
                }
            }

            // awaitAll will throw an exception if a download fails, which CoroutineWorker will treat as a failure
            jobs.awaitAll()
            Result.success()
        }
    }
    
  1. Constraints.Builder().setRequiresCharging(false) 这个boolean值的作用
    ,如果设置为true,那么必须插上电源这个任务才会运行。 这里插上电源,我试了下,必须是交流电。用数据线连接到电脑上的不算。
    begin(a).then(b)的效果,a执行完,并且dowork()返回WorkerResult.SUCCESS ,b才会执行。
    如果a返回了FAILED,那么b不会执行的,如果a返回retry,那么a就会等待时机再次尝试,直到返回成功,b才会执行。

测试中发现。点开start以后,我就打开应用程序,点了强制停止,然后发现过了一会它又成了运行状态了【强制停止不是灰色的,又可以点击了】
而且studio的日志里看到下边的东西

22:44:17.247 : Failed execv(/system/bin/dex2oat --runtime-arg -classpath --runtime-arg  --debuggable --instruction-set=arm --instruction-set-features=smp,-div,-atomic_ldrd_strd --runtime-arg -Xrelocate --boot-image=/system/framework/boot.art --runtime-arg -Xms64m --runtime-arg -Xmx512m --instruction-set-variant=cortex-a53 --instruction-set-features=default --dex-file=/data/app/com.charliesong.demo0327-1/split_lib_dependencies_apk.apk --oat-file=/data/dalvik-cache/arm/data@[email protected]@[email protected]) because non-0 exit status

22:44:18.887 D/PackageManagerHelper: androidx.work.impl.background.systemjob.SystemJobService enabled
22:44:18.887  D/Schedulers: Created SystemJobScheduler and enabled SystemJobService
22:44:18.897  D/PackageManagerHelper: androidx.work.impl.background.firebase.FirebaseJobService could not be disabled
    java.lang.IllegalArgumentException: Component class androidx.work.impl.background.firebase.FirebaseJobService does not exist in com.charliesong.demo0327

22:44:18.897  D/PackageManagerHelper: androidx.work.impl.background.systemalarm.SystemAlarmService disabled
22:44:18.907  I/InstantRun: starting instant run server: is main process
22:44:18.907  D/ForceStopRunnable: Application was force-stopped, rescheduling.
22:44:18.987 D/SystemJobService: onStartJob for c4d275cb-10af-4c08-bc9b-0985f196cbd9
22:44:18.987  D/SystemJobScheduler: Scheduling work ID c4d275cb-10af-4c08-bc9b-0985f196cbd9 Job ID 40
22:44:18.987  D/SystemJobService: Job is already being executed by SystemJobService: c4d275cb-10af-4c08-bc9b-0985f196cbd9
22:44:18.997  D/SystemJobScheduler: Scheduling work ID c4d275cb-10af-4c08-bc9b-0985f196cbd9 Job ID 41
22:44:18.997  D/SystemJobScheduler: Scheduling work ID ce1865ad-3d30-4e0d-8474-f6d64307a973 Job ID 42
22:44:18.997 /SystemJobScheduler: Scheduling work ID ce1865ad-3d30-4e0d-8474-f6d64307a973 Job ID 43
22:44:18.997  D/Processor: Processor: processing c4d275cb-10af-4c08-bc9b-0985f196cbd9
22:44:19.017 D/WorkerWrapper: Worker result SUCCESS for c4d275cb-10af-4c08-bc9b-0985f196cbd9
22:44:19.027  D/Processor: Processor c4d275cb-10af-4c08-bc9b-0985f196cbd9 executed; isSuccessful = true, reschedule = false
22:44:19.027 D/SystemJobService: c4d275cb-10af-4c08-bc9b-0985f196cbd9 executed on JobScheduler
22:44:19.027 /SystemJobScheduler: Scheduling work ID ce1865ad-3d30-4e0d-8474-f6d64307a973 Job ID 44
22:44:19.037 /SystemJobScheduler: Scheduling work ID ce1865ad-3d30-4e0d-8474-f6d64307a973 Job ID 45
22:44:39.017 /SystemJobService: onStartJob for c4d275cb-10af-4c08-bc9b-0985f196cbd9
22:44:39.017 /SystemJobService: Job is already being executed by SystemJobService: c4d275cb-10af-4c08-bc9b-0985f196cbd9
22:44:39.017 /Processor: Processor: processing c4d275cb-10af-4c08-bc9b-0985f196cbd9
22:44:39.027 com.charliesong.demo0327 E/WorkerWrapper: Status for c4d275cb-10af-4c08-bc9b-0985f196cbd9 is SUCCEEDED; not doing any work
22:44:39.027 /Processor: Processor c4d275cb-10af-4c08-bc9b-0985f196cbd9 executed; isSuccessful = false, reschedule = false
22:44:39.027 /SystemJobService: c4d275cb-10af-4c08-bc9b-0985f196cbd9 executed on JobScheduler
22:44:48.087 /SystemJobService: onStartJob for ce1865ad-3d30-4e0d-8474-f6d64307a973
22:44:48.087 /Processor: Processor: processing ce1865ad-3d30-4e0d-8474-f6d64307a973
22:44:48.087 /SystemJobService: Job is already being executed by SystemJobService: ce1865ad-3d30-4e0d-8474-f6d64307a973
22:44:48.107 /WorkerWrapper: Worker result SUCCESS for ce1865ad-3d30-4e0d-8474-f6d64307a973
22:44:48.117 /WorkerWrapper: Setting status to enqueued for 95339398-f24f-439f-aab0-3dfbbe787d45
22:44:48.117 /Processor: Processor ce1865ad-3d30-4e0d-8474-f6d64307a973 executed; isSuccessful = true, reschedule = false
22:44:48.117 /SystemJobService: ce1865ad-3d30-4e0d-8474-f6d64307a973 executed on JobScheduler
22:44:48.127 /SystemJobScheduler: Scheduling work ID 95339398-f24f-439f-aab0-3dfbbe787d45 Job ID 46
22:44:48.127 /SystemJobScheduler: Scheduling work ID 95339398-f24f-439f-aab0-3dfbbe787d45 Job ID 47
22:45:09.017 /SystemJobService: onStartJob for ce1865ad-3d30-4e0d-8474-f6d64307a973
22:45:09.017 /Processor: Processor: processing ce1865ad-3d30-4e0d-8474-f6d64307a973
22:45:09.017 /SystemJobService: Job is already being executed by SystemJobService: ce1865ad-3d30-4e0d-8474-f6d64307a973
22:45:09.027 com.charliesong.demo0327 E/WorkerWrapper: Status for ce1865ad-3d30-4e0d-8474-f6d64307a973 is SUCCEEDED; not doing any work
22:45:09.027 /Processor: Processor ce1865ad-3d30-4e0d-8474-f6d64307a973 executed; isSuccessful = false, reschedule = false
22:45:09.027 /SystemJobService: ce1865ad-3d30-4e0d-8474-f6d64307a973 executed on JobScheduler
22:45:10.017 /SystemJobService: onStartJob for ce1865ad-3d30-4e0d-8474-f6d64307a973
22:45:10.017 /Processor: Processor: processing ce1865ad-3d30-4e0d-8474-f6d64307a973
22:45:10.017 /SystemJobService: Job is already being executed by SystemJobService: ce1865ad-3d30-4e0d-8474-f6d64307a973
22:45:10.027 com.charliesong.demo0327 E/WorkerWrapper: Status for ce1865ad-3d30-4e0d-8474-f6d64307a973 is SUCCEEDED; not doing any work
22:45:10.027 /Processor: Processor ce1865ad-3d30-4e0d-8474-f6d64307a973 executed; isSuccessful = false, reschedule = false
22:45:10.027 /SystemJobService: ce1865ad-3d30-4e0d-8474-f6d64307a973 executed on JobScheduler
22:45:58.907 /SystemJobService: onStartJob for 95339398-f24f-439f-aab0-3dfbbe787d45
22:45:58.907 /Processor: Processor: processing 95339398-f24f-439f-aab0-3dfbbe787d45
22:45:58.917 /SystemJobService: Job is already being executed by SystemJobService: 95339398-f24f-439f-aab0-3dfbbe787d45
22:45:58.927 /WorkerWrapper: Worker result SUCCESS for 95339398-f24f-439f-aab0-3dfbbe787d45
22:45:58.927 /Processor: Processor 95339398-f24f-439f-aab0-3dfbbe787d45 executed; isSuccessful = true, reschedule = false
22:45:58.927 /SystemJobService: 95339398-f24f-439f-aab0-3dfbbe787d45 executed on JobScheduler

另外当start执行了N次以后,可以看到get到的也是N次的信息,除非卸载重装,否则start了n次tag为xxx的work,到时候getbytag就能获取N次

06-10 22:53:50.047 com.charliesong.demo0327 I/System.out: 0==============2140871e-f14a-498e-bc41-e6728b91a7ae====SUCCEEDED//true==[work1]
06-10 22:53:50.047 com.charliesong.demo0327 I/System.out: 1==============85ed6feb-b5d2-4c5b-a3c6-c005ea60054e====SUCCEEDED//true==[work1]
06-10 22:53:50.047 com.charliesong.demo0327 I/System.out: 2==============87b3b5d9-000d-4ef2-ba7e-2c005fc29da8====SUCCEEDED//true==[work1]
06-10 22:53:50.047 com.charliesong.demo0327 I/System.out: 3==============c4d275cb-10af-4c08-bc9b-0985f196cbd9====SUCCEEDED//true==[work1]

不过有点奇怪的是,我start里就有3个任务,可每次都有4条日志,前2条的work ID是一样的,就是job ID 不一样,不懂。

06-10 22:59:49.737 xxx D/SystemJobScheduler: Scheduling work ID bbef65fb-aca0-4b8f-96a1-50da7234e5c8 Job ID 8
06-10 22:59:49.737 xxx D/SystemJobScheduler: Scheduling work ID bbef65fb-aca0-4b8f-96a1-50da7234e5c8 Job ID 9
06-10 22:59:49.737 xxxx D/SystemJobScheduler: Scheduling work ID cb3f7b83-9b8b-4e8d-aeb3-915d2498ff3f Job ID 10
06-10 22:59:49.737 xxx D/SystemJobScheduler: Scheduling work ID cb3f7b83-9b8b-4e8d-aeb3-915d2498ff3f Job ID 11

测试

卸载重装app,然后开启一个任务

        val oneTimeRequest= OneTimeWorkRequestBuilder().setConstraints(constraints).setInputData(data)
                .addTag("worker2")
                .build()

        WorkManager.getInstance().beginWith(oneTimeRequest).enqueue()

     class Worker2:Worker() {
        override fun doWork(): WorkerResult {
            println("start ${javaClass.simpleName}==================")
            Thread.sleep(3000)
            println("end ${javaClass.simpleName}================")
            val date=SimpleDateFormat("yyyyMMDD_HHmmss", Locale.getDefault()).format(Date())
            UtilFile.saveStringToTempFile("$date ${javaClass.simpleName}  ${inputData.getString("title","")} \r\n")
            return  WorkerResult.SUCCESS
        }
    }

日志

21:18:45.793  D/SystemJobScheduler: Scheduling work ID 17d724d0-757e-44e3-8814-1b7a933a3f46 Job ID 0
21:18:45.793  D/SystemJobScheduler: Scheduling work ID 17d724d0-757e-44e3-8814-1b7a933a3f46 Job ID 1
21:18:45.793  D/GreedyScheduler: Starting tracking for 17d724d0-757e-44e3-8814-1b7a933a3f46
D/ConstraintTracker: NetworkStateTracker: initial state = [ Connected=true Validated=true Metered=false NotRoaming=true ]
21:18:45.803  D/NetworkStateTracker: Registering broadcast receiver
21:18:45.803  D/WorkConstraintsTracker: Constraints met for 17d724d0-757e-44e3-8814-1b7a933a3f46
21:18:45.803  D/GreedyScheduler: Constraints met: Scheduling work ID 17d724d0-757e-44e3-8814-1b7a933a3f46
21:18:45.803  D/WorkConstraintsTracker: Constraints met for 17d724d0-757e-44e3-8814-1b7a933a3f46
21:18:45.803  D/GreedyScheduler: Constraints met: Scheduling work ID 17d724d0-757e-44e3-8814-1b7a933a3f46
21:18:45.813  D/Processor: Processor: processing 17d724d0-757e-44e3-8814-1b7a933a3f46
21:18:45.813  D/Processor: Work 17d724d0-757e-44e3-8814-1b7a933a3f46 is already enqueued for processing
I/System.out: start Worker2==================
21:18:45.873  D/ViewRootImpl: MSG_RESIZED_REPORT: ci=Rect(0, 24 - 0, 0) vi=Rect(0, 0 - 0, 0) or=2
21:18:45.883  D/NetworkStateTracker: Network broadcast received
21:18:45.923  I/Timeline: Timeline: Activity_idle id: android.os.BinderProxy@caf4359 time:15813324
21:18:46.803  D/SystemJobService: onStartJob for 17d724d0-757e-44e3-8814-1b7a933a3f46
21:18:46.803  D/Processor: Work 17d724d0-757e-44e3-8814-1b7a933a3f46 is already enqueued for processing
21:18:46.803  D/SystemJobService: Job is already being executed by SystemJobService: 17d724d0-757e-44e3-8814-1b7a933a3f46
21:18:48.843  I/System.out: end Worker2================
21:18:48.843  D/WorkerWrapper: Worker result SUCCESS for 17d724d0-757e-44e3-8814-1b7a933a3f46
21:18:48.853  D/Processor: Processor 17d724d0-757e-44e3-8814-1b7a933a3f46 executed; isSuccessful = true, reschedule = false
21:18:48.853  D/SystemJobService: 17d724d0-757e-44e3-8814-1b7a933a3f46 executed on JobScheduler

间隔时间重复执行某个work

看文章发现有个PeriodicWorkRequest可以执行重复的任务,试了下结果发现没重复啊,后来点击源码才看到,这玩意默认的最小间隔是15分钟。

    fun startRepeateWork(){
        var request=PeriodicWorkRequest.Builder(MyWorker::class.java,1,TimeUnit.MINUTES).addTag("hello").build()
        WorkManager.getInstance().enqueue(request)
        UtilNormal.saveUUID(this,request.id.toString())
    }

    fun getRepeatWorkStatus(){
        var str=UtilNormal.getUUID(this)
        if(TextUtils.isEmpty(str)){
            return
        }
        WorkManager.getInstance().getStatusById(UUID.fromString(str))?.apply {
            this.removeObserver(observerOnly)
            this.observeForever(observerOnly)
        }
    }

    val observerOnly=object:Observer{
        override fun onChanged(t: WorkStatus?) {
            t?.apply {
                println("onChanged==============${t.id}====${t.state}//${t.state.isFinished}==${t.tags}")
            }
        }
    }

今天在一个新项目里添加这个room库的时候,编译的时候挂了,说有重复的库
Error: Program type already present: android.support.v4.app.INotificationSideChannel

解决办法
在gradle.properties文件下添加

android.useAndroidX=true
android.enableJetifier=true

然后点击下边按钮,迁移为androidX库,完事就没问题了。


image.png

然后写了个任务,等到执行的时候总是挂

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

        @NonNull
        @Override
        public Result doWork() {
//xxxx
            return Result.success();
        }

异常如下

WM-WorkerFactory: Could not instantiate com.mitac.lockdown.util.ScheduleTaskManager$AutoPlayWork
    java.lang.NoSuchMethodException:  [class android.content.Context, class androidx.work.WorkerParameters]
        at java.lang.Class.getConstructor(Class.java:528)
        at java.lang.Class.getDeclaredConstructor(Class.java:507)
        at androidx.work.WorkerFactory.createWorkerWithDefaultFallback(WorkerFactory.java:91)

点进去看下,也就是newInstance通过构造方法实例化一个对象失败了,无语

        try {
            Constructor constructor =
                    clazz.getDeclaredConstructor(Context.class, WorkerParameters.class);
            worker = constructor.newInstance(
                    appContext,
                    workerParameters);
            return worker;
        } catch (Exception e) {
            Logger.get().error(TAG, "Could not instantiate " + workerClassName, e);
        }

解决办法:
首先出错原因,这个work类我是写在工具里里的,也即是说是个内部类,实例化的时候需要外部类的对象才行的,这个肯定没有的啊。
所以把这个类设置成静态的,或者把这个类拉到外边单独写

强制关闭前台其他应用

需要系统权限,我们的app就是系统级应用,所以可用,否则就需要root了,然后执行adb命令,这个网上有

     //需要权限:
android:sharedUserId="android.uid.system"




ActivityManager activityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
activityManager.forceStopPackage("com.android.scan");

注意

对于这种重复的任务,这种任务你如果不手动取消的话,那么任务会一直重复下去,即使app被杀死,下次启动任务还在的。
你如果每次打开app都执行下边的代码,那么你的app里就会有多个这样的重复任务了。
很明显不合理,所以
你需要每次执行前,先cancel掉旧的worker,
或者这个执行完做个本地标记,下次执行前先判断下有没有执行过

      var request=PeriodicWorkRequest.Builder(MyWorker::class.java,1,TimeUnit.MINUTES).addTag("hello").build()
        WorkManager.getInstance().enqueue(request)

可以通过worker的tag来找到对应的任务取消

getWorkInfosByTag(MyWorker::class.java.name)

WorkManager.getInstance(this).cancelAllWorkByTag(MyWorker::class.java.name)

你可能感兴趣的:(WorkManage)