Jetpack学习(六)WorkManager

导入

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()
                // Additional configuration
                .build()
            val testWorkerRequest2 = OneTimeWorkRequest.from(TestWorker::class.java)
            WorkManager.getInstance(requireContext()).enqueue(testWorkerRequest1)
        }

执行定期的任务

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

val workRequest =
    PeriodicWorkRequestBuilder(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()
                    .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()
   .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()
                    .addTag("test")
                    .build()

传入数据

   val workerRequest: WorkRequest =
                OneTimeWorkRequestBuilder()
                    .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(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

// by name
workManager.getWorkInfosForUniqueWork("sync") // LiveData>

// by tag
workManager.getWorkInfosByTag("syncTag") // LiveData>

val testWorkerRequest = OneTimeWorkRequestBuilder().build()
            
WorkManager.getInstance(requireContext()).enqueue(testWorkerRequest)

val listener = WorkManager.getInstance(requireContext())
                .getWorkInfoByIdLiveData(testWorkerRequest.id)

listener.observe(viewLifecycleOwner, object : Observer {
        override fun onChanged(t: WorkInfo?) {
           Log.d("haha", t!!.state.name)
        }
                
 })

高级用法

自定义初始化

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

 
    
    
 

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 {


        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().build()
WorkManager.getInstance(requireContext()).enqueue(testWorkerRequest)

WorkManager.getInstance(requireContext()).getWorkInfoByIdLiveData(testWorkerRequest.id)
                .observe(viewLifecycleOwner, object : Observer {
                    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种方式申明

在应用的清单中声明工作器的前台服务类型。

在运行时指定前台服务类型
 return ForegroundInfo(
            123,
            notification,
            ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION or ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE
        )

你可能感兴趣的:(Jetpack学习(六)WorkManager)