WorkManager的基本使用

news2025/1/18 16:56:52

目录

  • 一、WorkManager概述
    • 1. WorkManager的作用:
    • 2. WorkManager的各个角色
  • 二、依赖库的导入
  • 三、WorkManager几种基本使用
    • 1. 单一任务的执行
    • 2. 数据 互相传递
    • 3. 多个任务 顺序执行
    • 4. 重复执行后台任务
    • 5. 约束条件
    • 6. 证明 app被杀掉之后,还在后台执行
  • 四、WorkManager源码流程图

一、WorkManager概述

WorkManage: 排队和管理工作请求;将WorkRequest对象传递WorkManager的任务队列

(注意:如果未指定任何约束,WorkManager立即运行任务)

WorkStatus: 包含有关特点任务的信息;可以使用LiveData保存WorkStatus对象,监听任务状态;如LiveData

  你的任务不可能总是在前台,但是还要确保你的那么重要任务执行,就可以放置在后台执行,那么WorkManager就发挥其作用了。
  WorkManager是Android Jetpack提供执行后台任务管理的组件,它适用于需要保证系统即使应用程序退出也会运行的任务,WorkManager API可以轻松指定可延迟的异步任务以及何时运行它们,这些API允许您创建任务并将其交给WorkManager立即允许或在适当的时间运行。
  WorkManager根据设备API级别和应用程序状态等因素选择适当的方式来运行任务。如果WorkManager在应用程序运行时执行您的任务之一,WorkManager可以在您应用程序进程的新进程钟运行您的任务。如果您的应用程序未运行,WorkManager会选择一种合适的方式来安排后台任务–具体取决于设备API级别和包含的依赖项,WorkManager可能会使用JobScheduler,Firebase JobDispatcher或AlarmManager。

1. WorkManager的作用:

    1. 确保重要的后台任务,一定会被执行,后台任务(例如:非及时性的(请求服务器 及时性)上传、下载 ,同步数据等)
    1. 内部对电量进行了优化,不需要我们去处理电量优化了
    1. API 14 到最新版本,都可以使用WorkManager来管理你的后台任务
    1. 注意:WorkManager不能做保活操作
    1. 调度,管理,执行的后台任务的场景,通常是可延迟的后台任务

2. WorkManager的各个角色

  1. Worker:可以这样理解,指定需要执行的任务,可以成为Worker类的之类,在实现的方法钟,就可以执行任务逻辑了。
  2. WorkRequest:可以这样理解,执行一项单一的任务
    • 第一点:必须知道WorkRequest对象必须指定Work执行的任务
    • 第二点:需要知道WorkRequest都有一个自动生成的唯一ID,可以使用ID执行取消排队任务 或 获取任务状态等操作
    • 第三点:需要知道WorkRequest是一个抽象的类;系统默认实现子类OneTimeWorkRequest 或 PeriodicWorkRequest
    • 第四点:需要知道WorkRequest.Builder创建WorkRequest对象;相应的子类:OneTimeWorkRequest.Builder 或 PeriodicWorkRequest.Builder
    • 第五点:需要知道Constraints:指定对任务运行时间的限制(任务约束);使用Constraints.Builder创建Constraints对象,并传递给WorkRequest.Builder

二、依赖库的导入

在app目录下的build.gradle导入依赖库

// 导入WorkManager依赖
def work_version = "2.3.4"
implementation "androidx.work:work-runtime:$work_version"

三、WorkManager几种基本使用

布局文件 activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="测试后台任务1"
        android:onClick="testBackgroundWork1"
        />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="测试后台任务2"
        android:onClick="testBackgroundWork2"
        />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="测试后台任务3"
        android:onClick="testBackgroundWork3"
        />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="测试后台任务4"
        android:onClick="testBackgroundWork4"
        />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="测试后台任务5"
        android:onClick="testBackgroundWork5"
        />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/bt6"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="测试后台任务六"
            android:onClick="testBackgroundWork6"
            />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="SP归零"
            android:onClick="spReset"
            />

    </LinearLayout>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="分析源码"
        android:onClick="codeStudy"
        android:layout_marginTop="20dp"
        />

</LinearLayout>

1. 单一任务的执行

MainWorker1.kt

package com.example.myworkmanager

import android.content.Context
import android.util.Log
import androidx.work.Worker
import androidx.work.WorkerParameters

// 最简单的执行任务
class MainWorker1(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {

    // 定义静态变量  相对于java static   const val相对于定义常量
    companion object { const val TAG = "abc" }

    // 后台任务 并且 异步的(原理:线程池执行Runnable)
    override fun doWork(): Result {
        Log.d(TAG, "MainWorker1 doWork: run started ...")
        try {
            Thread.sleep(3000)  // 睡眠
        } catch (e : InterruptedException) {
            e.printStackTrace()
            Result.failure()  // 本次任务失败
        } finally {
            Log.d(TAG, "MainWorker1 doWork: run end ...")
        }

        return Result.success()  // 本次任务完成
    }
}

MainActivity.kt

 /**
  * 最简单的 执行任务
  * 测试后台任务1
  */
 fun testBackgroundWork1(view: View) {

     // OneTimeWorkRequest 单个 一次的任务
     val oneTimeWorkRequest = OneTimeWorkRequest.Builder(MainWorker1::class.java).build()

     WorkManager.getInstance(this).enqueue(oneTimeWorkRequest)

 }

2. 数据 互相传递

MainWorker2.kt

package com.example.myworkmanager

import android.annotation.SuppressLint
import android.content.Context
import android.util.Log
import androidx.work.Data
import androidx.work.Worker
import androidx.work.WorkerParameters

class MainWorker2(context: Context, private val workerParameters: WorkerParameters) : Worker(context, workerParameters) {

    companion object {
        const val TAG = "abc"
    }

    // 后台任何 并且异步的(原理:线程池执行Runnable)
    @SuppressLint("RestrictedApi")
    override fun doWork(): Result {
        Log.d(TAG, "MainWorker2 dowork: 后台任务执行了")

        // 接收MainActivity传递过来的数据
        val dataString = workerParameters.inputData.getString("maiworker2")
        Log.d(TAG, "MainWorker2 dowork: 接收MainActivity传递过来的数据:$dataString")

        // 反馈数据给MainActivity
        // 把任务中的数据回传到MainActivity中
        val outputData = Data.Builder().putString("mainworker2", "mainworker2回传的数据").build()

        /*
        return Result.Failure()  // 本次执行 doWork任务时 失败
        return Result.Retry()    // 本次执行 doWork任务时 重试一次
        return Result.Success()  // 本次执行 doWork任务时 成功 执行任务完毕
        */
        return Result.Success(outputData);
    }
}

MainActivity.kt

    /**
     * 数据 互相传递
     * 测试后台任务2
     * 单个任务状态:ENQUEUED、RUNNING、SUCCEEDED
     */
    fun testBackgroundWork2(view: View) {
        // 单一、一次的任务
        val oneTimeWorkRequest : OneTimeWorkRequest

        // 数据
        val sendData = Data.Builder().putString("mainworker2", "传给mainworker2的数据").build()

        // 请求对象初始化
        oneTimeWorkRequest = OneTimeWorkRequest.Builder(MainWorker2::class.java)
            .setInputData(sendData) // 数据的携带 发送 一般都是 携带到Request里面去 发送数据给WorkManager2
            .build()

        // 一般都是通过 状态机 接收 WorkManager2的回馈数据
        // 状态机(LiveData)才能接收 WorkManager 回馈的数据
        WorkManager.getInstance(this).getWorkInfoByIdLiveData(oneTimeWorkRequest.id)
            .observe(this, Observer{ workInfo ->

                // 状态包含:ENQUEUED、RUNNING、SUCCEEDED
                Log.d(MainWorker2.TAG, "状态:" + workInfo.state.name);

                // ENQUEUED, RUNNING 都取不到 回馈的数据,都是null
                // Log.d(MainWorker2.TAG, "取到了任务回传的数据:" + workInfo.outputData.getString("mainworker2"))

                if (workInfo.state.isFinished) {  // 判断成功 SUCCEEDED状态
                    Log.d(MainWorker2.TAG, "取到了任务回传的数据:" + workInfo.outputData.getString("mainworker2"))
                }
            })
        WorkManager.getInstance(this).equals(oneTimeWorkRequest)
    }

3. 多个任务 顺序执行

MainWorker3.kt

package com.example.myworkmanager

import android.annotation.SuppressLint
import android.content.Context
import android.util.Log
import androidx.work.Worker
import androidx.work.WorkerParameters

class MainWorker3(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters){

    companion object { const val TAG = "abc" }

    @SuppressLint("RestrictedApi")
    override fun doWork(): Result {
        Log.d(TAG, "MainWorker3 doWork: 后台任务执行了")
        return Result.Success();  // 本次执行 doWork任务时 成功 执行任务完毕
    }
}

MainWorker4.kt

package com.example.myworkmanager

import android.annotation.SuppressLint
import android.content.Context
import android.util.Log
import androidx.work.Worker
import androidx.work.WorkerParameters

class MainWorker4(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters){

    companion object { const val TAG = "abc" }

    @SuppressLint("RestrictedApi")
    override fun doWork(): Result {
        Log.d(TAG, "MainWorker4 doWork: 后台任务执行了")
        return Result.Success();  // 本次执行 doWork任务时 成功 执行任务完毕
    }
}

MainWorker5.kt

package com.example.myworkmanager

import android.annotation.SuppressLint
import android.content.Context
import android.util.Log
import androidx.work.Worker
import androidx.work.WorkerParameters

class MainWorker5(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters){

    companion object { const val TAG = "abc" }

    @SuppressLint("RestrictedApi")
    override fun doWork(): Result {
        Log.d(TAG, "MainWorker5 doWork: 后台任务执行了")
        return Result.Success();  // 本次执行 doWork任务时 成功 执行任务完毕
    }
}

MainWorker6.kt

package com.example.myworkmanager

import android.annotation.SuppressLint
import android.content.Context
import android.util.Log
import androidx.work.Worker
import androidx.work.WorkerParameters

class MainWorker6(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters){

    companion object { const val TAG = "abc" }

    @SuppressLint("RestrictedApi")
    override fun doWork(): Result {
        Log.d(TAG, "MainWorker3 doWork: 后台任务执行了")
        return Result.Success();  // 本次执行 doWork任务时 成功 执行任务完毕
    }
}

MainActivity.kt

/**
     * 多个任务  顺序执行
     * 测试后台任务3
     */
    fun testBackgroundWork3(view: View) {
        // 单一、一次的任务
        val oneTimeWorkRequest3 = OneTimeWorkRequest.Builder(MainWorker3::class.java).build()
        val oneTimeWorkRequest4 = OneTimeWorkRequest.Builder(MainWorker4::class.java).build()
        val oneTimeWorkRequest5 = OneTimeWorkRequest.Builder(MainWorker5::class.java).build()
        val oneTimeWorkRequest6 = OneTimeWorkRequest.Builder(MainWorker6::class.java).build()

        // 顺序执行 3 4 5 6
        WorkManager.getInstance(this)
            .beginWith(oneTimeWorkRequest3)
            .then(oneTimeWorkRequest4)
            .then(oneTimeWorkRequest5)
            .then(oneTimeWorkRequest6)
            .enqueue()

        // 另外一种使用
        // 需求: 先执行3 4 最后执行6
        val oneTimeWorkRequests : MutableList<OneTimeWorkRequest> = ArrayList()   // 集合方式
        oneTimeWorkRequests.add(oneTimeWorkRequest3)
        oneTimeWorkRequests.add(oneTimeWorkRequest4)

        WorkManager.getInstance(this)
            .beginWith(oneTimeWorkRequests)
            .then(oneTimeWorkRequest6)
            .enqueue()
    }

4. 重复执行后台任务

MainActivity.kt

    /**
     * 重复执行后台任务 非单个任务 ,多个任务
     * 单个任务状态:ENQUEUED、RUNNING、SUCCEEDED
     */
    fun testBackgroundWork4(view: View) {
        // OneTimeWorkRequest 单个任务  不会轮询  执行一次就结束

        // 重复的任务  多次/循环/轮询 哪怕设置为10s轮询一次,那么最少轮询/循环一次 也要15分钟(google规定)
        // 不能小于15分钟,否则默认修改成15分钟
        val periodicWorkRequest = PeriodicWorkRequest.Builder(MainWorker3::class.java,10, TimeUnit.SECONDS).build()

        // 状态机 为什么一直都是ENQUEUED 和 RUNNING,因为 你是轮询的任务,SUCCEEDED
        // 如果是单个任务,就会看到SUCCEEDED结束任务
        // 监听状态
        WorkManager.getInstance(this)
            .getWorkInfoByIdLiveData(periodicWorkRequest.id)
            .observe(this, Observer {workInfo ->
                Log.d("abc", "状态:" + workInfo.state.name)  // ENQUEUED RUNNING  循环反复

                if(workInfo.state.isFinished) {
                    Log.d("abc", "状态:isFinished = true 注意:后台任务已经完成了...")
                }
            })
         WorkManager.getInstance(this).equals(periodicWorkRequest)
        
        // 取消 任务的执行
        //WorkManager.getInstance(this).cancelWorkById(periodicWorkRequest.id);
    }

5. 约束条件

MainActivity.kt

/**
     * 约束条件,约束后台任务执行
     * 测试后台任务5
     */
    fun testBackgroundWork5(view: View) {
        val constraints = Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED)  // 必须是联网中
            .setRequiresCharging(true)    // 必须是充当中
            .setRequiresDeviceIdle(true)  // 必须是空闲时
            .build()
        /**
         * 除了上面设置的约束外,WorkManager还提供了以下的约束作为Work执行的条件:
         * setRequiredNetworkType:网络连接设置
         * setRequiresBatteryNotLow:是否为低电量时运行,默认false
         * setRequiresCharging:是否要插入设备(接入电源),默认false
         * setRequiresDeviceIdle:设备是否空闲,默认false
         * setRequiresStorageNotLow:设备可用存储是否不低于临界阈值
         */

        // 请求对象
        val request = OneTimeWorkRequest.Builder(MainWorker3::class.java)
            .setConstraints(constraints)
            .build()

        // 加入队列
        WorkManager.getInstance(this).enqueue(request)
    }

6. 证明 app被杀掉之后,还在后台执行

MainActivity.kt

class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceChangeListener {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
/**
     * 证明 app被杀掉之后,还在后台执行,通过写入文件的方式(SP)
     * 测试后台任务6
     */
    fun testBackgroundWork6(view: View) {
        // 约束条件
        val constraints = Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED)  // 约束条件 必须是网络连接
            .build()

        // 构建Request
        val request = OneTimeWorkRequest.Builder(MainWorker7::class.java)
            .setConstraints(constraints)
            .build()

        // 加入队列
        WorkManager.getInstance(this).enqueue(request)

    }

    // SP归零
    fun spReset(view: View) {
        val sp = getSharedPreferences(MainWorker7.SP_NAME, Context.MODE_PRIVATE)
        sp.edit().putInt(MainWorker7.SP_KEY, 0).apply()
        updateToUI()
    }

    // 从SP里面获取值,显示到界面给用户看
    private fun updateToUI() {
        val sp = getSharedPreferences(MainWorker7.SP_NAME, Context.MODE_PRIVATE)
        val resultValue = sp.getInt(MainWorker7.SP_KEY, 0)
        bt6?.text = "测试后台任务6---$resultValue"

    }

    // 文件内容只要有变化,此函数就会执行
    override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) = updateToUI()

}

MainActivity.kt 完整代码:

package com.example.myworkmanager

import android.content.Context
import android.content.SharedPreferences
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.lifecycle.Observer
import androidx.work.*
import kotlinx.android.synthetic.main.activity_main.*
import java.util.concurrent.TimeUnit

class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceChangeListener {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    /**
     * 最简单的 执行任务
     * 测试后台任务1
     */
    fun testBackgroundWork1(view: View) {

        // OneTimeWorkRequest 单个 一次的任务
        val oneTimeWorkRequest = OneTimeWorkRequest.Builder(MainWorker1::class.java).build()

        WorkManager.getInstance(this).enqueue(oneTimeWorkRequest)

    }

    /**
     * 数据 互相传递
     * 测试后台任务2
     * 单个任务状态:ENQUEUED、RUNNING、SUCCEEDED
     */
    fun testBackgroundWork2(view: View) {
        // 单一、一次的任务
        val oneTimeWorkRequest : OneTimeWorkRequest

        // 数据
        val sendData = Data.Builder().putString("mainworker2", "传给mainworker2的数据").build()

        // 请求对象初始化
        oneTimeWorkRequest = OneTimeWorkRequest.Builder(MainWorker2::class.java)
            .setInputData(sendData) // 数据的携带 发送 一般都是 携带到Request里面去 发送数据给WorkManager2
            .build()

        // 一般都是通过 状态机 接收 WorkManager2的回馈数据
        // 状态机(LiveData)才能接收 WorkManager 回馈的数据
        WorkManager.getInstance(this).getWorkInfoByIdLiveData(oneTimeWorkRequest.id)
            .observe(this, Observer{ workInfo ->

                // 状态包含:ENQUEUED、RUNNING、SUCCEEDED
                Log.d(MainWorker2.TAG, "状态:" + workInfo.state.name);

                // ENQUEUED, RUNNING 都取不到 回馈的数据,都是null
                // Log.d(MainWorker2.TAG, "取到了任务回传的数据:" + workInfo.outputData.getString("mainworker2"))

                if (workInfo.state.isFinished) {  // 判断成功 SUCCEEDED状态
                    Log.d(MainWorker2.TAG, "取到了任务回传的数据:" + workInfo.outputData.getString("mainworker2"))
                }
            })
        WorkManager.getInstance(this).equals(oneTimeWorkRequest)
    }

    /**
     * 多个任务  顺序执行
     * 测试后台任务3
     */
    fun testBackgroundWork3(view: View) {
        // 单一、一次的任务
        val oneTimeWorkRequest3 = OneTimeWorkRequest.Builder(MainWorker3::class.java).build()
        val oneTimeWorkRequest4 = OneTimeWorkRequest.Builder(MainWorker4::class.java).build()
        val oneTimeWorkRequest5 = OneTimeWorkRequest.Builder(MainWorker5::class.java).build()
        val oneTimeWorkRequest6 = OneTimeWorkRequest.Builder(MainWorker6::class.java).build()

        // 顺序执行 3 4 5 6
        WorkManager.getInstance(this)
            .beginWith(oneTimeWorkRequest3)
            .then(oneTimeWorkRequest4)
            .then(oneTimeWorkRequest5)
            .then(oneTimeWorkRequest6)
            .enqueue()

        // 另外一种使用
        // 需求: 先执行3 4 最后执行6
        val oneTimeWorkRequests : MutableList<OneTimeWorkRequest> = ArrayList()   // 集合方式
        oneTimeWorkRequests.add(oneTimeWorkRequest3)
        oneTimeWorkRequests.add(oneTimeWorkRequest4)

        WorkManager.getInstance(this)
            .beginWith(oneTimeWorkRequests)
            .then(oneTimeWorkRequest6)
            .enqueue()
    }

    /**
     * 重复执行后台任务 非单个任务 ,多个任务
     * 单个任务状态:ENQUEUED、RUNNING、SUCCEEDED
     */
    fun testBackgroundWork4(view: View) {
        // OneTimeWorkRequest 单个任务  不会轮询  执行一次就结束

        // 重复的任务  多次/循环/轮询 哪怕设置为10s轮询一次,那么最少轮询/循环一次 也要15分钟(google规定)
        // 不能小于15分钟,否则默认修改成15分钟
        val periodicWorkRequest = PeriodicWorkRequest.Builder(MainWorker3::class.java,10, TimeUnit.SECONDS).build()

        // 状态机 为什么一直都是ENQUEUED 和 RUNNING,因为 你是轮询的任务,SUCCEEDED
        // 如果是单个任务,就会看到SUCCEEDED结束任务
        // 监听状态
        WorkManager.getInstance(this)
            .getWorkInfoByIdLiveData(periodicWorkRequest.id)
            .observe(this, Observer {workInfo ->
                Log.d("abc", "状态:" + workInfo.state.name)  // ENQUEUED RUNNING  循环反复

                if(workInfo.state.isFinished) {
                    Log.d("abc", "状态:isFinished = true 注意:后台任务已经完成了...")
                }
            })
        WorkManager.getInstance(this).equals(periodicWorkRequest)
        
        // 取消 任务的执行
        //WorkManager.getInstance(this).cancelWorkById(periodicWorkRequest.id);
    }

    /**
     * 约束条件,约束后台任务执行
     * 测试后台任务5
     */
    fun testBackgroundWork5(view: View) {
        val constraints = Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED)  // 必须是联网中
            .setRequiresCharging(true)    // 必须是充当中
            .setRequiresDeviceIdle(true)  // 必须是空闲时
            .build()
        /**
         * 除了上面设置的约束外,WorkManager还提供了以下的约束作为Work执行的条件:
         * setRequiredNetworkType:网络连接设置
         * setRequiresBatteryNotLow:是否为低电量时运行,默认false
         * setRequiresCharging:是否要插入设备(接入电源),默认false
         * setRequiresDeviceIdle:设备是否空闲,默认false
         * setRequiresStorageNotLow:设备可用存储是否不低于临界阈值
         */

        // 请求对象
        val request = OneTimeWorkRequest.Builder(MainWorker3::class.java)
            .setConstraints(constraints)
            .build()

        // 加入队列
        WorkManager.getInstance(this).enqueue(request)
    }

    /**
     * 证明 app被杀掉之后,还在后台执行,通过写入文件的方式(SP)
     * 测试后台任务6
     */
    fun testBackgroundWork6(view: View) {
        // 约束条件
        val constraints = Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED)  // 约束条件 必须是网络连接
            .build()

        // 构建Request
        val request = OneTimeWorkRequest.Builder(MainWorker7::class.java)
            .setConstraints(constraints)
            .build()

        // 加入队列
        WorkManager.getInstance(this).enqueue(request)

    }

    // SP归零
    fun spReset(view: View) {
        val sp = getSharedPreferences(MainWorker7.SP_NAME, Context.MODE_PRIVATE)
        sp.edit().putInt(MainWorker7.SP_KEY, 0).apply()
        updateToUI()
    }

    // 从SP里面获取值,显示到界面给用户看
    private fun updateToUI() {
        val sp = getSharedPreferences(MainWorker7.SP_NAME, Context.MODE_PRIVATE)
        val resultValue = sp.getInt(MainWorker7.SP_KEY, 0)
        bt6?.text = "测试后台任务6---$resultValue"

    }

    // 文件内容只要有变化,此函数就会执行
    override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) = updateToUI()


    // TODO ------------------------下面是源码分析环节

    /**
     * TODO 分析源码
     */
    fun codeStudy(view: View) {
        // 没有约束条件

        // 请求对象
        val request = OneTimeWorkRequest.Builder(MainWorker3::class.java).build()

        // 第一次初始化是在 ContentProvider
        /**
         * APK 清单文件里面(第一次初始化)执行
         * 成果 WorkManagerImpl构建出来了
         * 1. 初始化 数据库 ROOM 来保存你的任务(持久性保存的)手机重启 APP被杀掉  没关系 一定执行
         * 2. 初始化 埋下伏笔 new GreedyScheduler(context, taskExcutor, this)
         * 3. 初始化 配置信息 configuration (执行信息  线程池任务)
         */

        WorkManager.getInstance(this) // 这里已经是第二次初始化了
            .enqueue(request)  // 执行流程源码分析

    }


}

四、WorkManager源码流程图

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/967309.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

中小企业常用的 IT 项目管理软件有哪些?

越热门&#xff0c;越贵的IT项目管理软件越好用吗&#xff1f;对于预算有限的中小型企业来说&#xff0c;如何选择一款适合自己的项目管理工具着实是个头疼的问题。 首先适用于中小型企业使用的 IT 项目管理软件需要具备哪些特点呢&#xff1f; 1、简单易用&#xff1a;中小企…

note_前端框架Vue的安装和简单入门(Windows 11)

1. Vue安装 (1) 下载安装node.js和npm # 下载msi安装包 https://nodejs.org/en# 点击安装包&#xff0c;按提示安装 # 默认安装nodejs, npm, 在线文档; PATH配置# 确认安装是否成功&#xff0c;在dos中输入 node -v # 验证nodejs是否安装成功 npm -v # 验证nodejs包管…

DSP应用技术学习笔记——第一讲

如果一项选择被分解为两个连续的选择&#xff0c;则原来的信息量H应该等于分解后的各个信息量H的加权和 DSP概念 DSP既是数字信号处理&#xff08;Digital Signal Processings&#xff09;的缩写也是数字信号处理器&#xff08;Digital Signal Processor&#xff09;的缩写。…

Lesson4-3:OpenCV图像特征提取与描述---SIFT/SURF算法

学习目标 理解 S I F T / S U R F SIFT/SURF SIFT/SURF算法的原理&#xff0c;能够使用 S I F T / S U R F SIFT/SURF SIFT/SURF进行关键点的检测 SIFT/SURF算法 1.1 SIFT原理 前面两节我们介绍了 H a r r i s Harris Harris和 S h i − T o m a s i Shi-Tomasi Shi−Tomasi…

城市白模三维重建

收费工具&#xff0c;学生党勿扰&#xff0c;闹眼子当勿扰 收费金额&#xff1a;2000元&#xff0c;不能开发票 1 概述 几个月前&#xff0c;一家小公司找我帮忙给他们开发一个程序&#xff0c;可以将一个城市进行白模的三维重建。 报酬大约5K。经过不懈的努力&#xff0c;终于…

亚马逊广告收入突破百亿美元,有望成为下一个收入支柱来源?

据外媒报道&#xff0c;亚马逊新兴的广告业务已经价值数百亿美元&#xff0c;很可能成为其下一个收入支柱来源。 市场研究公司Insider Intelligence的分析师Andrew Lipsman表示&#xff0c;按照目前的发展轨迹&#xff0c;亚马逊广告业务甚至可以与其云计算业务相互抗衡。“毫…

21 自定义miniweb框架|闭包装饰器|log输出

文章目录 前情知识介绍WSIG-miniWeb框架服务器动态资源请求浏览器请求动态页面的全流程WSGIWSGI接口的定义 静态服务器回顾以及改造web 服务器 和 逻辑处理代码 分离 web动态服务器的基本实现带参数的web动态服务器 闭包装饰器闭包闭包的基础使用函数、匿名函数、闭包、对象修改…

Locked勒索病毒:最新变种.locked袭击了您的计算机?

导言&#xff1a; 在今天的数字时代&#xff0c;勒索病毒已经不再是仅仅让数据变得不可访问的小威胁。 .locked 勒索病毒&#xff0c;作为其中的一种&#xff0c;以其高度复杂的加密算法和迅速变化的攻击手法而备受恶意分子喜爱。本文91数据恢复将带您深入了解 .locked 勒索病毒…

【Linux】简单的小程序:进度条

在学习进度条之前&#xff0c;需要学一点预备知识。 1. 预备知识 回车换行 现在的换行符&#xff08;\n&#xff09;其实就是回车式换行符&#xff0c;另起一行&#xff0c;光标指向最新一行的开头。回车符&#xff08;\r&#xff09;是光标指向这一行的开头。 缓冲区 &a…

springboot封装查询快递物流

目录 一、ApiClient代码解读二、ApiService代码解读三、HomeController代码解读四、整体代码五、结果展示 一、ApiClient代码解读 这是一个简单的Spring Boot的RestTemplate客户端&#xff0c;用于执行HTTP请求。 首先&#xff0c;这个类被Component注解标记&#xff0c;这意味…

销量蹭蹭上涨!亚马逊上这几款宿舍神器火爆了!

一、BedShelfie床边置物架 每年返校季&#xff0c;收纳工具都是最畅销的产品。在亚马逊床头柜热销榜单中&#xff0c;这款产品位居第二。过去一个月里&#xff0c;有1000多名用户购买了这件产品。 二、U Brands磁性干擦日历板 目前&#xff0c;亚马逊上这款产品已经卖到断货。…

ubuntu下Anaconda安装与使用教程

前言 好久没用anaconda了&#xff0c;还记得之前用anaconda的欢乐时光。pytorch和paddlepaddle(飞浆)&#xff0c;怀念&#xff0c;可生活&#xff08;换了ubuntu系统之后&#xff09;教会了我残忍&#xff08;可能很难有机会再用windows的anaconda了&#xff09;。找个时间&a…

C语言:动态内存(一篇拿捏动态内存!)

目录 学习目标&#xff1a; 为什么存在动态内存分配 动态内存函数&#xff1a; 1. malloc 和 free 2. calloc 3. realloc 常见的动态内存错误&#xff1a; 1. 对NULL指针的解引用操作 2. 对动态开辟空间的越界访问 3. 对非动态开辟内存使用free释放 4. 使用free释…

代码随想录—力扣算法题:24两两交换链表中的节点.Java版(示例代码与导图详解)

版本说明 当前版本号[20230903]。 版本修改说明20230903初版 24. 两两交换链表中的节点 力扣题目链接 更多内容可点击此处跳转到代码随想录&#xff0c;看原版文件 给定一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后的链表。 你不能只是单纯的改变…

淘宝/天猫获得淘宝商品详情 API 接口文档

item_get-获得淘宝商品详情 API测试工具 注册开通 taobao.item_get 公共参数 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;secretString是调用密钥api_nameString是API接口名称&#xff08;包括在请求地址中&#xff09;[item_sear…

Google colab部署VITS——零门槛快速克隆任意角色声音

目录 序言 查看GPU配置 复制代码库并安装运行环境 选择预训练模型 上传视频链接&#xff08;单个不应长于20分钟&#xff09; 自动处理所有上传的数据 训练质量相关&#xff1a;实验发现目前使用CJ模型勾选ADD_AUXILIARY&#xff0c;对于中/日均能训练出最好的效果&#x…

【数据结构】 二叉树面试题讲解->叁

文章目录 &#x1f30f;引言&#x1f332;[根据二叉树创建字符串](https://leetcode.cn/problems/construct-string-from-binary-tree/submissions/)&#x1f431;‍&#x1f464;题目描述&#xff1a;&#x1f431;‍&#x1f409;示例&#xff1a;&#x1f4cc;示例一&#x…

1921. 消灭怪物的最大数量

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;贪心排序 复杂度分析写在最后 Tag 【贪心】【排序】【数组】 题目来源 1921. 消灭怪物的最大数量 题目解读 dist[i] 是第 i 个怪兽与城市的初始距离&#xff0c;speed[i] 是第 i 个怪兽的移动距离。怪兽的目的是攻击…

Mybatis学习|注解开发、lombok

1.使用注解开发 无需再编写相应的Mapper.xml文件&#xff0c;直接将sql用注解的形式写在Mapper接口的对应方法上即可。 然后因为没有xml文件,所以要在mybatis-config.xml核心配置文件中注册这个Mapper接口&#xff0c;而不用去注册之前的Mapper.xml&#xff0c;这里其实如果用…

04_22 vma(进程下的每个虚拟内存区域查看)对象实战

前言 vma不太懂的可以往前翻 03_008内存映射原理_虚拟内存区域vm_area_struct详解,和mmap系统钓调用及物理内存结构体完全分析 vam 虚拟内存区域 每个进程下有多个vma 这次是查看每个vma的起始地址 结束地址和大小使用 1.进程在用户空间调用mmap也就是上面那个函数。 2.在当前…