美文网首页
Jetpack学习(六)WorkManager

Jetpack学习(六)WorkManager

作者: 飞哥278999401 | 来源:发表于2021-09-04 17:45 被阅读0次

    导入

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

    使用

    class TestWorker(appContext: Context, workerParams: WorkerParameters): Worker(appContext,workerParams) {
    
        override fun doWork(): Result {
    
            Log.d("haha",Thread.currentThread().name)
    
            return Result.success()
    
        }
    
    }
    
    

    执行一次的任务

     val testWorkerRequest1 = OneTimeWorkRequestBuilder<TestWorker>()
                    // Additional configuration
                    .build()
                val testWorkerRequest2 = OneTimeWorkRequest.from(TestWorker::class.java)
                WorkManager.getInstance(requireContext()).enqueue(testWorkerRequest1)
            }
    

    执行定期的任务

    定期任务最低定义间隔为15分钟

    val workRequest =
        PeriodicWorkRequestBuilder<TestWorker>(1, TimeUnit.HOURS)
         // Additional configuration
        build()
     WorkManager.getInstance(requireContext()).enqueue(workRequest)
    
    

    工作约束

    约束将工作延迟到满足最佳条件时运行。

    NetworkType约束运行工作所需的[网络类型]。例如 Wi-Fi UNMETERED
    BatteryNotLow如果设置为 true,那么当设备处于“电量不足模式”时,工作不会运行。
    RequiresCharging 如果设置为 true,那么工作只能在设备充电时运行。
    DeviceIdle 如果设置为 true,则要求用户的设备必须处于空闲状态,才能运行工作。如果要运行批量操作,否则可能会降低用户设备上正在积极运行的其他应用的性能,建议使用此约束。
    StorageNotLow 如果设置为 true,那么当用户设备上的存储空间不足时,工作不会运行。

                val constraints = Constraints.Builder()
                    .setRequiredNetworkType(NetworkType.UNMETERED)
                    .setRequiresCharging(true)
                    .build()
    
                val workerRequest: WorkRequest =
                    OneTimeWorkRequestBuilder<TestWorker>()
                        .setConstraints(constraints)
                        .build()
    
                WorkManager.getInstance(requireContext()).enqueue(workerRequest)
    

    重试和退避政策

    class TestWorkerRetry (appContext: Context, workerParams: WorkerParameters): Worker(appContext,workerParams) {
    
        override fun doWork(): Result {
    
            Log.d("haha",Thread.currentThread().name)
    
            return Result.retry()
    
        }
    
    }
    
    val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
       .setBackoffCriteria(
           BackoffPolicy.LINEAR,
           OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
           TimeUnit.MILLISECONDS)
       .build()
    
    

    如果doWork返回retry(),BackoffPolicy.LINEAR会在设置的延时时间后重试,每次增加延时时间,比如设置的延时时间为10秒,如果一直返回retry(),就会在20秒、30秒、40秒依次增加设置的延时时间重试。BackoffPolicy.EXPONENTIAL,每次会以指数级增加下一次重试的时间,比如设置的延时时间为10秒,那么重试时长序列将接近 20、40、80 秒,以此类推。

    标记工作

    每个工作请求都有一个唯一标识符,该标识符可用于在以后标识该工作,以便取消工作或观察其进度。

    如果有一组在逻辑上相关的工作,对这些工作项进行标记可能也会很有帮助。通过标记,您一起处理一组工作请求。

    例如,WorkManager.cancelAllWorkByTag(String)会取消带有特定标记的所有工作请求,WorkManager.getWorkInfosByTag(String) 会返回一个 WorkInfo 对象列表,该列表可用于确定当前工作状态。

    val workerRequest: WorkRequest =
                    OneTimeWorkRequestBuilder<TestWorker>()
                        .addTag("test")
                        .build()
    
    

    传入数据

       val workerRequest: WorkRequest =
                    OneTimeWorkRequestBuilder<TestWorker>()
                        .setInputData(workDataOf("key" to "I'm value"))
                        .build()
    
    class TestWorker(appContext: Context, workerParams: WorkerParameters) :
        Worker(appContext, workerParams) {
    
        override fun doWork(): Result {
            val value = inputData.getString("key")
            Log.d("haha", value!!)
            return Result.success()
        }
    
    }
    

    工作状态

    一次性的工作状态

    对于 one-time工作请求,工作的初始状态为 ENQUEUED

    ENQUEUED 状态下,您的工作会在满足其 Constraints 和初始延迟计时要求后立即运行。接下来,该工作会转为 RUNNING状态,然后可能会根据工作的结果转为 SUCCEEDEDFAILED 状态;或者,如果结果是 retry,它可能会回到 ENQUEUED 状态。在此过程中,随时都可以取消工作,取消后工作将进入 CANCELLED 状态。

    image.png

    SUCCEEDEDFAILEDCANCELLED 均表示此工作的终止状态。如果您的工作处于上述任何状态,WorkInfo.State.isFinished() 都将返回 true。

    定期工作状态

    成功和失败状态仅适用于一次性工作和链式工作。定期工作只有一个终止状态 CANCELLED。这是因为定期工作永远不会结束。每次运行后,无论结果如何,系统都会重新对其进行调度。

    image.png

    管理工作

    唯一工作

    有的工作只需要开启一次,需要避免重复启动,可以使用唯一工作。

    唯一工作既可用于一次性工作,也可用于定期工作。可以通过调用以下方法之一创建唯一工作序列,具体取决于是调度重复工作还是一次性工作。

    WorkManager.enqueueUniqueWork()
    WorkManager.enqueueUniquePeriodicWork()`
    
         val testWorkerRequest =
                    PeriodicWorkRequestBuilder<TestWorker>(900000, TimeUnit.SECONDS).build()
         WorkManager.getInstance(requireContext()).enqueueUniquePeriodicWork(
                    "testwork",
                    ExistingPeriodicWorkPolicy.KEEP,
                    testWorkerRequest
    
    

    解决冲突政策

    ExistingWorkPolicy它支持用于处理冲突的 4 个选项。

    REPLACE:用新工作替换现有工作。此选项将取消现有工作。
    KEEP:保留现有工作,并忽略新工作。
    APPEND:将新工作附加到现有工作的末尾。此政策将导致您的新工作链接到现有工作,在现有工作完成后运行。

    现有工作将成为新工作的先决条件。如果现有工作变为 CANCELLEDFAILED 状态,新工作也会变为 CANCELLEDFAILED。如果您希望无论现有工作的状态如何都运行新工作,请改用 APPEND_OR_REPLACE

    APPEND_OR_REPLACE函数类似于 APPEND,不过它并不依赖于先决条件工作状态。即使现有工作变为 CANCELLEDFAILED 状态,新工作仍会运行。

    对于定期工作,需要提供一个 ExistingPeriodicWorkPolicy,它支持 REPLACEKEEP 这两个选项。这些选项的功能与其对应的 ExistingWorkPolicy 功能相同。

    观察工作

    // by id
    workManager.getWorkInfoByIdLiveData(syncWorker.id) // LiveData<WorkInfo>
    
    // by name
    workManager.getWorkInfosForUniqueWork("sync") // LiveData<List<WorkInfo>>
    
    // by tag
    workManager.getWorkInfosByTag("syncTag") // LiveData<List<WorkInfo>>
    
    val testWorkerRequest = OneTimeWorkRequestBuilder<TestWorker>().build()
                
    WorkManager.getInstance(requireContext()).enqueue(testWorkerRequest)
    
    val listener = WorkManager.getInstance(requireContext())
                    .getWorkInfoByIdLiveData(testWorkerRequest.id)
    
    listener.observe(viewLifecycleOwner, object : Observer<WorkInfo> {
            override fun onChanged(t: WorkInfo?) {
               Log.d("haha", t!!.state.name)
            }
                    
     })
    
    

    高级用法

    自定义初始化

    在manifest里面移除默认的WorkManagerInitializer,在Application中实现Configuration.Provider,可以对WorkManager进行配置,比如设置线程池

     <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>
    
    class MyApplication : Application(), Configuration.Provider {
    
    
        override fun getWorkManagerConfiguration(): Configuration {
    
            return Configuration.Builder().setExecutor(Executors.newSingleThreadExecutor()).build()
    
        }
    }
    
    

    在Worker处理线程

    WorkManager 会自动在后台线程中调用 Worker.doWork(),可以初始化的时候给WorkManager配置线程池或者不配置,系统会默认给一个线程池。Worker.doWork()是同步调用,内部调用方法需要调用同步方法。如果需要在内部调用异步回调操作的方法,需要使用ListenableWorker

    使用CoroutineWorker

    class TestWorker(appContext: Context, workerParams: WorkerParameters) :
        CoroutineWorker(appContext, workerParams) {
    
        override suspend fun doWork(): Result {
    
            Log.d("haha", "start" + Thread.currentThread().name)
            delay(5000)
            Log.d("haha", "worker" + Thread.currentThread().name)
    
            return Result.success()
    
        }
    }
    

    在ListenableWorker处理

    需要处理基于回调的异步操作。在这种情况下,不能只依靠 Worker 来完成操作,因为它无法以阻塞方式完成这项工作。WorkManager 通过 ListenableWorker 支持该用

    使用councurrent-futures包含到 gradle 文件中并使用 CallbackToFutureAdapter

    implementation "androidx.concurrent:concurrent-futures-ktx:1.1.0"
    
    import android.content.Context
    import android.util.Log
    
    import androidx.concurrent.futures.CallbackToFutureAdapter
    import androidx.work.ListenableWorker
    import androidx.work.WorkerParameters
    import com.google.common.util.concurrent.ListenableFuture
    import okhttp3.*
    import java.io.IOException
    
    class CallbackWorker(
        context: Context,
        params: WorkerParameters
    ) : ListenableWorker(context, params) {
        override fun startWork(): ListenableFuture<Result> {
    
    
            return CallbackToFutureAdapter.getFuture { completer ->
    
                val callback = object : Callback {
                    var successes = 0
    
                    override fun onFailure(call: Call, e: IOException) {
    
                        Log.d("onFailure", e.message + "")
                        e.printStackTrace()
                        completer.setException(e)
    
                    }
    
                    override fun onResponse(call: Call, response: Response) {
                        successes++
                        Log.d("onResponse", successes.toString() + " " + response.body?.string())
                        if (successes == 100) {
                            completer.set(Result.success())
                        }
                    }
                }
    
    
                repeat(100) {
    
                    val client = OkHttpClient();
    
                    val request: Request = Request.Builder()
                        .url("https://www.baidu.com")
                        .get()
                        .build()
                    val call = client.newCall(request)
    
                    call.enqueue(callback)
    
                }
    
                callback
            }
        }
    }
    
    
    val testWorkerRequest =
                    OneTimeWorkRequestBuilder<CallbackWorker>().build()
    WorkManager.getInstance(requireContext()).enqueue(testWorkerRequest)
    
    WorkManager.getInstance(requireContext()).getWorkInfoByIdLiveData(testWorkerRequest.id)
                    .observe(viewLifecycleOwner, object : Observer<WorkInfo> {
                        override fun onChanged(t: WorkInfo?) {
                            Log.d("haha", t!!.state.name)
    
                        }
    
                    })
    

    支持长时间运行的工作器

    WorkManager 可以向操作系统提供一个信号,指示在此项工作执行期间应尽可能让进程保持活跃状态。这些工作器可以运行超过 10 分钟。这一新功能的示例用例包括批量上传或下载(不可分块)、在本地进行的机器学习模型处理,或者对应用的用户很重要的任务。

    ListenableWorker现在支持 setForegroundAsync() API,而 CoroutineWorker则支持挂起 setForeground()API。这些 API 允许开发者指定此 WorkRequest 是“重要的”(从用户的角度来看)或“长时间运行的”任务。

    import android.annotation.TargetApi
    import android.app.NotificationChannel
    import android.app.NotificationManager
    import android.content.Context
    import android.os.Build
    import android.util.Log
    import androidx.core.app.NotificationCompat
    import androidx.work.*
    import kotlinx.coroutines.delay
    import com.example.myapplication.R
    
    class TestWorker(appContext: Context, workerParams: WorkerParameters) :
        CoroutineWorker(appContext, workerParams) {
    
        override suspend fun doWork(): Result {
    
            repeat(100) {
    
    
                delay(1000)
                Log.d(
                    "haha", "测试长时间任务" + it
                )
                setForeground(createForegroundInfo(it.toString()))
            }
    
    
            return Result.success()
    
        }
    
    
        private fun createForegroundInfo(progress: String): ForegroundInfo {
            val id = "123"
            val title = "测试长时间任务"
            val cancel = "取消长时间任务"
            // This PendingIntent can be used to cancel the worker
            val intent = WorkManager.getInstance(applicationContext)
                .createCancelPendingIntent(getId())
    
            // Create a Notification channel if necessary
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                createNotificationChannel(id, title, NotificationManager.IMPORTANCE_HIGH)
            }
    
            val notification = NotificationCompat.Builder(applicationContext, id)
                .setContentTitle(title)
                .setTicker(title)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentText(progress)
                .setOngoing(true)
                // Add the cancel action to the notification which can
                // be used to cancel the worker
                .addAction(android.R.drawable.ic_delete, cancel, intent)
                .build()
    
            return ForegroundInfo(123, notification)
        }
    
        @TargetApi(Build.VERSION_CODES.O)
        private fun createNotificationChannel(channelId: String, channelName: String, importance: Int) {
    
            val channel = NotificationChannel(channelId, channelName, importance)
    
    
            val manager =
                applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            manager.createNotificationChannel(channel)
        }
    
    
    }
    

    如果应用以 Android 10(API 级别 29)或更高版本为目标平台,且包含需要位置信息访问权限的长时间运行的工作器,指明该工作器使用 location 的前台服务类型。此外,如果您的应用以 Android 11(API 级别 30)或更高版本为目标平台,且包含需要访问相机或麦克风的长时间运行的工作器,请分别声明 cameramicrophone 前台服务类型。有2种方式申明

    在应用的清单中声明工作器的前台服务类型。
    <service
        android:name="androidx.work.impl.foreground.SystemForegroundService"
        android:foregroundServiceType="location|microphone"
        tools:node="merge" />
    
    在运行时指定前台服务类型
     return ForegroundInfo(
                123,
                notification,
                ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION or ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE
            )
    
    

    相关文章

      网友评论

          本文标题:Jetpack学习(六)WorkManager

          本文链接:https://www.haomeiwen.com/subject/lmqrwltx.html