Android Jetpack 系列jetpack人生几何?

Android Jetpack系列--7. WorkManage

2021-09-02  本文已影响0人  今阳说

相关知识

# Define the oom_adj values for the classes of processes that can be
# killed by the kernel.  These are used in ActivityManagerService.
    setprop ro.FOREGROUND_APP_ADJ 0    //前台进程
    setprop ro.VISIBLE_APP_ADJ 1       //可见进程
    setprop ro.SECONDARY_SERVER_ADJ 2  //次要服务
    setprop ro.BACKUP_APP_ADJ 2        //备份进程
    setprop ro.HOME_APP_ADJ 4          //桌面进程
    setprop ro.HIDDEN_APP_MIN_ADJ 7    //后台进程
    setprop ro.CONTENT_PROVIDER_ADJ 14 //内容供应节点
    setprop ro.EMPTY_APP_ADJ 15        //空进程

后台任务

Google推荐的不同场景后台任务的处理方案

  1. 需系统触发,不必完成:ThreadPool + Broadcast
  2. 需系统触发,必须完成,可推迟:WorkManager
  3. 需系统触发,必须完成,立即:ForegroundService + Broadcast
  4. 不需系统触发,不必完成:ThreadPool
  5. 不需系统触发,必须完成,可推迟:WorkManager
  6. 不需系统触发,必须完成,立即:ForegroundService

WorkManager简介

WorkManager优点

WorkManager使用

导入依赖
implementation "androidx.work:work-runtime-ktx:2.5.0"
// optional - RxJava2 support
implementation "androidx.work:work-rxjava2:2.5.0"
// optional - Test helpers
androidTestImplementation "androidx.work:work-testing:2.5.0"
初始化配置
// provide custom configuration
val myConfig = Configuration.Builder()
    .setMinimumLoggingLevel(android.util.Log.INFO)
    .build()

// initialize WorkManager
WorkManager.initialize(this, myConfig)
<provider
    android:name="androidx.work.impl.WorkManagerInitializer"
    android:authorities="${applicationId}.workmanager-init"
    android:directBootAware="false"
    android:exported="false"
    android:multiprocess="true"
    tools:targetApi="n" />
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class WorkManagerInitializer extends ContentProvider {
    @Override
    public boolean onCreate() {
        // Initialize WorkManager with the default configuration.
        WorkManager.initialize(getContext(), new Configuration.Builder().build());
        return true;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri,
            @Nullable String[] projection,
            @Nullable String selection,
            @Nullable String[] selectionArgs,
            @Nullable String sortOrder) {
        return null;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        return null;
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        return null;
    }

    @Override
    public int delete(@NonNull Uri uri,
            @Nullable String selection,
            @Nullable String[] selectionArgs) {
        return 0;
    }

    @Override
    public int update(@NonNull Uri uri,
            @Nullable ContentValues values,
            @Nullable String selection,
            @Nullable String[] selectionArgs) {
        return 0;
    }
}
//1. AndroidManifest.xml中覆盖其provider,并设置tools:node="remove"
//WorkManager 2.6以前版本
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
tools:node="remove" />
//WorkManager 2.6以后的版本
<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">
    <!-- If you are using androidx.startup to initialize other components -->
    <meta-data
        android:name="androidx.work.WorkManagerInitializer"
        android:value="androidx.startup"
        tools:node="remove" />
</provider>

//2. Application实现Configuration.Provider接口
class MyApplication() : Application(), Configuration.Provider {
     override fun getWorkManagerConfiguration() =
           Configuration.Builder()
                .setMinimumLoggingLevel(android.util.Log.INFO)
                .build()
}
//You do not need to call `WorkManager.initialize()` yourself
//注意这里是实现Provider接口而不是像2.1版本一样手动调用initialize,
//当然如果我就要手动调用WorkManager.initialize也不会有报错,只是官方不推荐
//看看WorkManagerImpl.getInstance方法就知道了
public static @NonNull WorkManagerImpl getInstance(@NonNull Context context) {
    synchronized (sLock) {
        WorkManagerImpl instance = getInstance();
        if (instance == null) {
            Context appContext = context.getApplicationContext();
            //这里如果application没有实现Provider接口就直接抛出异常
            if (appContext instanceof Configuration.Provider) {
                initialize(
                        appContext,
                        ((Configuration.Provider) appContext).getWorkManagerConfiguration());
                instance = getInstance(appContext);
            } else {
                throw new IllegalStateException("WorkManager is not initialized properly.  You "
                        + "have explicitly disabled WorkManagerInitializer in your manifest, "
                        + "have not manually called WorkManager#initialize at this point, and "
                        + "your Application does not implement Configuration.Provider.");
            }
        }
        return instance;
    }
}
自定义Worker
class MyWorker(appContext: Context, workerParameters: WorkerParameters) :
    CoroutineWorker(appContext, workerParameters) {

    //执行在一个单独的后台线程里
    override suspend fun doWork(): Result {
        LjyLogUtil.d("doWork start")
        delay(5000)//模拟处理任务耗时
        LjyLogUtil.d("doWork end")
        return Result.success()
    }
}
执行单次任务
class WorkManagerActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_work_manager)
        //执行单次任务
        val workRequest = OneTimeWorkRequestBuilder<MyWorker>().build()
        WorkManager.getInstance(this).enqueue(workRequest)
    }
}
定期循环任务
val workRequest2 =
            PeriodicWorkRequest.Builder(MyWorker::class.java, 3, TimeUnit.SECONDS).build()
WorkManager.getInstance(this).enqueue(workRequest2)
val workRequest2: WorkRequest = PeriodicWorkRequest.Builder(
        MyWorker::class.java,
        1, TimeUnit.HOURS,
        15, TimeUnit.MINUTES
    ).build()
WorkManager.getInstance(this).enqueue(workRequest2)
设置任务约束条件
val constraints = Constraints.Builder()
            //设备空闲状态时运行
            .setRequiresDeviceIdle(true)
            //特定的网络状态运行
            //NOT_REQUIRED  不需要网络
            //CONNECTED 任何可用网络
            //UNMETERED 需要不计量网络,如WiFi
            //NOT_ROAMING   需要非漫游网络
            //METERED   需要计量网络,如4G
            .setRequiredNetworkType(NetworkType.CONNECTED)
            //电量充足时运行
            .setRequiresBatteryNotLow(true)
            //充电时执行
            .setRequiresCharging(true)
            //存储空间足够时运行
            .setRequiresStorageNotLow(true)
            //指定是否在(Uri指定的)内容更新时执行本次任务
            .addContentUriTrigger(Uri.EMPTY, true)
            .build()
val workRequest = OneTimeWorkRequestBuilder<MyWorker>()
        .setConstraints(constraints)
        .build()
WorkManager.getInstance(this).enqueue(workRequest)
分配输入数据
//1. 传入数据
val inputData = Data.Builder().putString("name", "ljy").build()
val workRequest = OneTimeWorkRequestBuilder<MyWorker>()
        .setInputData(inputData)
        .build()
WorkManager.getInstance(this).enqueue(workRequest)
//2.接收数据
class MyWorker(appContext: Context, workerParameters: WorkerParameters) :
        CoroutineWorker(appContext, workerParameters) {
    override suspend fun doWork(): Result {
        val name = inputData.getString("name")
        LjyLogUtil.d("doWork start:name=$name")
        delay(5000)
        LjyLogUtil.d("doWork end")
        return Result.success()
    }
}
延时执行
val workRequest = OneTimeWorkRequestBuilder<MyWorker>()
        .setInitialDelay(1, TimeUnit.SECONDS)
        .build()
WorkManager.getInstance(this).enqueue(workRequest)
设置tag
val workRequest = OneTimeWorkRequestBuilder<MyWorker>()
        .addTag("myWorker")
        .build()
WorkManager.getInstance(this).enqueue(workRequest)
重试和退避政策
val workRequest4: WorkRequest = OneTimeWorkRequest.Builder(MyWorker::class.java)
    .setBackoffCriteria(
        BackoffPolicy.LINEAR,
        OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
        TimeUnit.MILLISECONDS
    )
    .build()
创建任务链
val request1 = OneTimeWorkRequest.Builder(MyWorker::class.java).build()
val request2 = OneTimeWorkRequest.Builder(MyWorker::class.java).build()
val request3 = OneTimeWorkRequest.Builder(MyWorker::class.java).build()
val request4 = OneTimeWorkRequest.Builder(MyWorker::class.java).build()
val request5 = OneTimeWorkRequest.Builder(MyWorker::class.java).build()
val workConstraints = WorkManager.getInstance(this).beginWith(request1)
workConstraints.then(request2).then(listOf(request3, request4)).enqueue()
workConstraints.then(request5).enqueue()
唯一链
val requestLoadFromFile = OneTimeWorkRequest.Builder(MyWorker::class.java).build()
val requestZip = OneTimeWorkRequest.Builder(MyWorker::class.java)
    .setInputData(createInputDataForUri()).build()
val requestSubmitToService = OneTimeWorkRequest.Builder(MyWorker::class.java).build()
WorkManager.getInstance(this).beginUniqueWork(
    "tagChangeImageHeader",
    ExistingWorkPolicy.REPLACE,
    requestLoadFromFile
)
    .then(requestZip)
    .then(requestSubmitToService)
    .enqueue()
Work状态
ENQUEUED,//已加入队列
RUNNING,//运行中
SUCCEEDED,//已成功
FAILED,//已失败
BLOCKED,//已挂起
CANCELLED;//已取消
状态监听
// by id
WorkManager.getInstance(this).getWorkInfoById(request1.id)
WorkManager.getInstance(this).getWorkInfoByIdLiveData(request1.id)
// by name
WorkManager.getInstance(this).getWorkInfosForUniqueWork("sync");
WorkManager.getInstance(this).getWorkInfosForUniqueWorkLiveData("sync");
// by tag
WorkManager.getInstance(this).getWorkInfosByTag("syncTag")
WorkManager.getInstance(this).getWorkInfosByTagLiveData("syncTag")
WorkQuery
val workQuery = WorkQuery.Builder
    .fromTags(listOf("syncTag"))
    .addStates(listOf(WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
    .addUniqueWorkNames(
        listOf("preProcess", "sync")
    )
    .build()
val workInfos: ListenableFuture<List<WorkInfo>> =
    WorkManager.getInstance(this).getWorkInfos(workQuery)
更新进度 和 观察进度
class MyWorker(appContext: Context, workerParameters: WorkerParameters) :
        CoroutineWorker(appContext, workerParameters) {
        
    override suspend fun doWork(): Result {
        val name = inputData.getString("name")
        LjyLogUtil.d("doWork start:name=$name")
        val p0 = workDataOf("progressValue" to 0)
        val p1 = workDataOf("progressValue" to 20)
        val p2 = workDataOf("progressValue" to 40)
        val p3 = workDataOf("progressValue" to 60)
        val p4 = workDataOf("progressValue" to 80)
        val p5 = workDataOf("progressValue" to 100)
        setProgress(p0)
        delay(1000)
        setProgress(p1)
        delay(1000)
        setProgress(p2)
        delay(1000)
        setProgress(p3)
        delay(1000)
        setProgress(p4)
        delay(1000)
        setProgress(p5)
        LjyLogUtil.d("doWork end")
        return Result.success()
    }
}
val workRequest10 = OneTimeWorkRequestBuilder<MyWorker>().build()
WorkManager.getInstance(this).enqueue(workRequest10)
WorkManager.getInstance(this)
    .getWorkInfoByIdLiveData(workRequest10.id)
    .observe(this, {
        if (it != null) {
            LjyLogUtil.d("workRequest10:state=${it.state}")
            val progress = it.progress;
            val value = progress.getInt("progressValue", 0)
            LjyLogUtil.d("workRequest10:progress=$value")
        }
    })
取消任务
//取消所有任务
WorkManager.getInstance(this).cancelAllWork()
//取消一组带有相同标签的任务
WorkManager.getInstance(this).cancelAllWorkByTag("tagName")
//根据name取消任务
WorkManager.getInstance(this).cancelUniqueWork("uniqueWorkName")
//根据id取消任务
WorkManager.getInstance(this).cancelWorkById(workRequest.id)
任务停止
1. 明确要求取消它,可以调用WorkManager.cancelWorkById(UUID)方法。
2. 如果是唯一任务,将 ExistingWorkPolicy 为 REPLACE 的新 WorkRequest 加入到了队列中时,旧的 WorkRequest 会立即被视为已取消。
3. 添加的任务约束条件不再适合。
4. 系统出于某种原因指示应用停止工作。
5. 当任务停止后,WorkManager 会立即调用 ListenableWorker.onStopped()关闭可能保留的所有资源。

我是今阳,如果想要进阶和了解更多的干货,欢迎关注微信公众号 “今阳说” 接收我的最新文章

上一篇 下一篇

猜你喜欢

热点阅读