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<MyWoker>()
                .setConstraints(constraints)
                .setInitialDelay(10,TimeUnit.SECONDS)
                .setInputData(input)
                .build()

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

其他:
PeriodicWorkRequest的Builder還有一種構造方法,可以傳入flex時間,不傳的默認和repeat interval時間一樣

        public Builder(
                @NonNull Class<? extends ListenableWorker> 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的設備,這個字段無效

         * <p><pre>
         * [     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<OneTimeWorkRequest> work)

public final Operation enqueue(@NonNull WorkRequest workRequest)
public abstract Operation enqueue(@NonNull List<? extends WorkRequest> requests)

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

上邊的這些還有另外一種UniqueWork
顧名思義,就是對于uniqueWorkName一樣的任務只能有一個,那已經有咋辦,由第二個參數來決定

    public abstract @NonNull WorkContinuation beginUniqueWork(
            @NonNull String uniqueWorkName,
            @NonNull ExistingWorkPolicy existingWorkPolicy,
            @NonNull List<OneTimeWorkRequest> 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<CompressWorker>().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

<provider
        android:name="androidx.work.impl.WorkManagerInitializer"
        android:authorities="${applicationId}.workmanager-init"
        tools:node="remove" />

然后在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@app@com.charliesong.demo0327-1@split_lib_dependencies_apk.apk@classes.dex) 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<Worker2>().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<WorkStatus>{
        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: <init> [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<? extends ListenableWorker> 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"

<uses-permission android:name="android.permission.FOCE_STOP_PACKAGES" />


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)
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,156評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,401評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,069評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,873評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,635評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,128評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,203評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,365評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,881評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,733評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,935評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,475評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,172評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,582評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,821評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,595評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,908評論 2 372

推薦閱讀更多精彩內容

  • 這個不錯分享給大家,從扣上看到的,就轉過來了 《電腦專業英語》 file [fail] n. 文件;v. 保存文...
    麥子先生R閱讀 6,595評論 5 24
  • 好的電影應該有什么樣的特質?在電影看得越來越多的時候我總會不時的想這個問題,當然這個問題不應該一統而論,不同類型的...
    知道社的西皮流水閱讀 747評論 0 2
  • 今天下午第一節課戴老師組織我們進行了《周末小試卷第三組》的測試,答題時我感覺第三題和第六道題很容易,很快就寫完了。...
    張軒赫閱讀 159評論 0 5
  • 今天事情比較多。就寫了一張 順便給明天請個假,明天爺爺生日,估計也有的忙了(???︿???)
    Mr_oOo閱讀 460評論 0 2
  • 再次見到曾經的朋友,第一句話是:變化不大嘛,沒胖也沒瘦啊? 不知道是不是每個女性朋友都是這樣子的,特別關注自己和別...
    花姑娘呀嘿閱讀 356評論 0 0