jetpack学习笔录-WorkManager

本文详细介绍WorkManager API,用于后台执行可延期任务,如发送用户日志等。文章讲解了WorkManager的基本原理,如何创建和执行任务,以及如何利用WorkRequest进行任务调度。此外,还探讨了任务执行的不同模式及条件设定。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

WorkManager是一个API,用来调度任务,哪怕进程关闭或者设备重启都没事。这些任务是可延期的异步任务。

任务串行执行,具有延迟性,但好处是即使退出项目甚至重启设备,下次开启app时,只要满足执行条件就会执行。

比较适合在后台像服务器发送用户日志这类操作~

之所以关闭进程或者设备重启都没事,是因为已调度的工作都存储在SQLite中。

 

WorkManager是一个单例,获取方式:WorkManager.getInstance(this)

 

 

 

 上图是使用WorkManager的流程

首先创建一个worker,这是一个任务单元

class MyWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {


    override fun doWork(): Result {

        val name = inputData.getString("key")
        
        //获取缓存的数值
        val sp = applicationContext.getSharedPreferences("app", Context.MODE_PRIVATE)
        var num = sp.getInt(name, 0)
        num++
        sp.edit().putInt(name, num).apply()

        //doWork都是任务线程
        Thread.sleep(3000)

        //返回使用Result.success表示成功,里面可以带数据,数据使用workDataOf()这里面带一个键值对,可以中中缀表达式 to 
        //或者使用Pair都可以
        return Result.success(workDataOf("result" to "我的线程是:${Thread.currentThread().name}"))
    }
}

创建好任务单元后,我们需要创建一个任务请求,使用WorkRequest。WorkRequest规定了任务何时才能运行·.并将任务添加到任务队列中,对添加到任务队列中的任务,WorkManager会对其进行缓存。

class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceChangeListener {

    private val workManager = WorkManager.getInstance(this)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_activity)

        //增加对本地缓存中数值改变的监听
        getSharedPreferences("app", Context.MODE_PRIVATE)
            .registerOnSharedPreferenceChangeListener(this)

        //从本地缓存中获取数值
        updateView()

    }


    /**
     * 更新文本显示
     */
    fun updateView() {
        val sp = getSharedPreferences("app", Context.MODE_PRIVATE)
        text.text = sp.getInt("workA", 0).toString()
    }


    /**
     * 点击btn相应
     */
    fun addEnqueue(view: View) {

        val workRequest = createTaskRequest("workA")

        workManager.enqueue(workRequest)


        //按照顺序执行的任务列表
        //同时先执行workRequest 和workRequestA,然后顺序执行workRequestB,最后执行workRequestC
//        workManager.beginWith(workRequest, workRequestA)
//            .then(workRequestB)
//            .then(workRequestC)
//            .enqueue()

    }


    /**
     * 创建一个WorkRequest
     */
    private fun createTaskRequest(name: String): OneTimeWorkRequest {

        /**
         * 创建的请求可以添加一些条件,
         * .setRequiredNetworkType(NetworkType.CONNECTED)是指只有网络处于连接状态下,才能进行任务
         *
         */
        val contrans = Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED)
            .build()


        //创建data的一种方式
//        val data = Data.Builder()
//            .putString("key", "value")
//            .build()


        /**
         * 创建任务请求的时候,可以添加一些数据给Worker,使用setInputData方法,
         * 在Worker类中的doWork方法中,使用inputData.getString("key")获取我们这里传递的数据,
         * 注意:传递的数据也是键值对,使用Pair或者中缀表达式 to
         * OneTimeWorkRequestBuilder表示创建一次性的request
         *
         * 设置request的时候,可以设置任务执行的条件,通过 setConstraints 设置
         */

        val taskRequest = OneTimeWorkRequestBuilder<MyWorker>()
            .setConstraints(contrans)
            .setInputData(workDataOf("key" to name))
            .build()



        /**
         * OneTimeWorkRequestBuilder是创建单次任务,而PeriodicWorkRequest是创建周期任务,
         * 最小的周期间隔时长是15分钟
         */
//        val request = PeriodicWorkRequest
//            .Builder(MyWorker::class.java, 15, TimeUnit.MINUTES)
//            .setConstraints(constraints)
//            .setInputData(data)
//            .build()



        /**
         * 使用WorkManager获取任务的可观察者对象.并观察任务当前的状态
         * 在成功回调中,接收后台任务返回的结果
         */
        WorkManager.getInstance(this).getWorkInfoByIdLiveData(taskRequest.id)
            .observe(this, Observer {
                if (it.state == WorkInfo.State.SUCCEEDED) {
                    val result = it.outputData.getString("result")
                    Log.d("chenhua", "收到了结果:$result")
                }
            })
        return taskRequest
    }

    override fun onSharedPreferenceChanged(p0: SharedPreferences?, p1: String?) {
        updateView()
    }
}

xml界面就很简单了,一个textView显示数值,一个button按钮,点击才添加任务

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:onClick="addEnqueue"
        android:text="加入任务到队列" />
</RelativeLayout>

下面补充说明WrokRequest请求的可选择条件

val constraints = Constraints.Builder()
        .setRequiredNetworkType(NetworkType.CONNECTED)  // 网络状态
        .setRequiresBatteryNotLow(true)                 // 不在电量不足时执行
        .setRequiresCharging(true)                      // 在充电时执行
        .setRequiresStorageNotLow(true)                 // 不在存储容量不足时执行
        .setRequiresDeviceIdle(true)                    // 在待机状态下执行,需要 API 23
        .build()

多任务执行情况:

 /**
         * 下面这段看chain3,说明chain1和chain2同时执行,然后再执行workRequestE
         * 由于chain1和chain2同时执行,所以workRequestA和requestC基本同时执行,然后
         * 基本同时执行requestB和requestD哈~
         */
        val chain1 = WorkManager.getInstance(this)
            .beginWith(workRequestA)
            .then(workRequestB)

        val chain2 = WorkManager.getInstance(this)
            .beginWith(workRequestC)
            .then(workRequestD)

        val chain3 = WorkContinuation
            .combine(listOf(chain1, chain2))
            .then(workRequestE)

        chain3.enqueue()



输出结果:
收到了结果:workA 执行完毕~----------------
收到了结果:workC 执行完毕~----------------
收到了结果:workB 执行完毕~----------------
收到了结果:workD 执行完毕~----------------
收到了结果:workE 执行完毕~----------------

很多情况下,我们希望在任务队列里,同一个任务只存在一个,避免任务的重复执行,这时候可以用到 beginUniqueWork 这个方法:

 /**
         * 避免任务队列中有相同的任务,重复执行,可以使用beginUniqueWork
         *
         * ExistingWorkPolicy.REPLACE  删除已经有的任务,添加现有的任务
         * ExistingWorkPolicy.KEEP  不添加新的任务,让已经有的任务继续执行。
         * ExistingWorkPolicy.APPEND 加入任务链的末端
         * ExistingWorkPolicy.APPEND_OR_REPLACE 如果有一个同名的未完成的任务,
         *                                      则把新添加的任务归属到同名任务的附属任务?如果不行的话,
         *                                      就重新开一个队列,把新任务添加到这个新队列中?
         */
        workManager
            .beginUniqueWork("workNamer", ExistingWorkPolicy.APPEND_OR_REPLACE, workRequestA)
            .enqueue()

保活?

周期执行任务的最小时间是15分钟,而且进程一旦被杀,也没得救了。进程被杀只有再次启动app的时候,任务才会可能开始执行。所以保活就别想了。。

 

service对比

这东西就是做后台任务的,那service不是也能做吗。service是4大组件,滥用容易导致app卡顿。在后台的service,谷歌也做了不少限制。对于单纯只是为了在后台执行任务,更推荐使用WorkManager。

但并不是说service完全无用了,起码还是四大组件之一。开个前台服务,能降低app被杀的可能性等等。

 

 

 

### 如何在 Jetpack 4.4 上安装和配置 Python OpenCV #### 下载并安装必要的依赖项 为了确保系统的稳定性和兼容性,在安装特定版本的 OpenCV 前,建议先移除任何旧版可能存在的冲突库。对于已经存在多个不同版本的情况,可以考虑创建虚拟环境来隔离各个项目所需的软件栈[^2]。 ```bash sudo apt-get update && sudo apt-get upgrade -y sudo apt-get remove --purge libopencv* python-opencv -y ``` #### 创建 Python 虚拟环境 通过 `virtualenv` 或者 Anaconda 来管理独立的工作空间能够有效避免包之间的相互干扰: ```bash pip install virtualenv virtualenv opencv_env source opencv_env/bin/activate ``` #### 编译安装指定版本的 OpenCV 考虑到系统中已有的 CUDA 和 TensorRT 配置,编译时应启用 GPU 加速支持以充分利用硬件资源[^3]。针对 Jetson Nano 平台上的 JetPack 4.4 版本,推荐采用预构建二进制文件简化流程;然而若需自定义功能,则可按照如下步骤操作: 1. **获取源码** ```bash git clone https://github.com/opencv/opencv.git -b 3.4.6 cd opencv/ ``` 2. **设置 CMake 参数** 确认路径指向正确的开发工具链位置,并开启 GStreamer 支持以便于多媒体处理应用开发[^4]。 ```cmake cmake \ -D CMAKE_BUILD_TYPE=RELEASE \ -D CMAKE_INSTALL_PREFIX=/usr/local \ -D CUDA_ARCH_BIN="5.3" \ -D CUDA_ARCH_PTX="" \ -D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib/modules \ -D BUILD_EXAMPLES=OFF .. ``` 3. **执行多线程编译过程** 利用 `-j$(nproc)` 自动调整并发任务数加快速度。 ```bash make -j$(nproc) sudo make install ``` 完成上述步骤之后,重启终端使新加载的共享库生效,接着就可以利用 pip 工具快速部署对应的 Python 接口模块了。 ```bash deactivate cd ~ git clone https://github.com/skvark/opencv-python.git cd opencv-python python setup.py build_ext --inplace pip install . ``` 最后一步是验证安装成果,运行简单的测试脚本来确认一切正常工作。 ```python import cv2 print(cv2.__version__) cap = cv2.VideoCapture(0) while True: ret, frame = cap.read() if not ret: break gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) cv2.imshow('frame',gray) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows() ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值