【重生之我在学Android】WorkManager (章一)
相关文章
【重生之我在学Android原生】ContentProvider(Java)
【重生之我在学Android原生】Media3
【重生之我在学Android】WorkManager (章一)
前言
官方文档

官方推荐 - 前台服务、后台服务都可以使用WorkManger来实现

案例
语言:JAVA
实现要求
一步步实现一个图片压缩APP
创建项目

添加WorkManager依赖
参考文章

添加到builder.gradle, sync一下

val workVersion = "2.9.0"implementation("androidx.work:work-runtime:$workVersion")
接收share来的图片数据

要实现这种效果,需要在AndroidManifest.xml声明标签,过滤intent

<intent-filter><action android:name="android.intent.action.SEND" /><category android:name="android.intent.category.DEFAULT" /><data android:mimeType="image/*" /></intent-filter>
将Activity改为singleTop
运行APP。打开手机相册,分享一张图片,会重新使用这个Activity

android:launchMode="singleTop"
在onNewIntent接收数据

定义Worker
你需要做什么事情,你就定义一个Worker,指派它做事,做什么事,就在dowork里定义
dowork有三个返回,见图

传入Uri到Worker
参考这里

通过inputdata传入


Uri -> Bitmap

若有爆红位置



压缩图片直到图片的大小不超过XKB
传入图片的大小阀值

不断循环压缩,一直到图片的大小不超过20KB

生成文件


返回图片地址数据
构建Data,传值回去

监听Worker结果
在获取到WorkManager这个实例后
通过getWorkInfoByIdLiveData方法来监听workerrequest状态及结果返回

显示结果
在布局中,加入一张图片


Android版本 兼容问题
兼容低版本的Android系统

inputStream.readAllBytes() 需要在API 33之后使用
所以需要更改写法,来使低版本的Android系统使用

bytes = new byte[inputStream.available()];inputStream.read(bytes);
运行项目

完整代码
// ImageCompressionWorker
package com.test.imagecompressionworkerapplication;import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.util.Log;import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.work.Data;
import androidx.work.Worker;
import androidx.work.WorkerParameters;import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;public class ImageCompressionWorker extends Worker {private final String TAG = "worker - log";public static final String KEY_CONTENT_URI = "KEY_CONTENT_URI";public static final String KEY_COMPRESSION_THRESHOLD = "KEY_COMPRESSION_THRESHOLD";public static final String KEY_RESULT_PATH = "KEY_RESULT_PATH";private final WorkerParameters workerParameters;private final Context appContext;public ImageCompressionWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {super(context, workerParams);appContext = context;workerParameters = workerParams;}@NonNull@Overridepublic Result doWork() {Data inputData = workerParameters.getInputData();String uriStr = inputData.getString(KEY_CONTENT_URI);long size = inputData.getLong(KEY_COMPRESSION_THRESHOLD, 0L);assert uriStr != null;Uri uri = Uri.parse(uriStr);byte[] bytes;try {InputStream inputStream = appContext.getContentResolver().openInputStream(uri);assert inputStream != null;
// byte[] bytes_ = inputStream.readAllBytes();bytes = new byte[inputStream.available()];inputStream.read(bytes);Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);inputStream.close();int quality = 100;byte[] byteArray;do {ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();bitmap.compress(Bitmap.CompressFormat.JPEG, quality, byteArrayOutputStream);byteArray = byteArrayOutputStream.toByteArray();quality -= Math.round(quality * 0.1);} while (byteArray.length > size && quality > 5);File file = new File(appContext.getCacheDir(), workerParameters.getId() + ".jpg");FileOutputStream fileOutputStream = new FileOutputStream(file);fileOutputStream.write(byteArray);fileOutputStream.close();String absolutePath = file.getAbsolutePath();Data outputData = new Data.Builder().putString(KEY_RESULT_PATH, absolutePath).build();return Result.success(outputData);} catch (IOException e) {throw new RuntimeException(e);}}
}
// MainActivity.java
package com.test.imagecompressionworkerapplication;import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;
import androidx.work.Data;
import androidx.work.OneTimeWorkRequest;
import androidx.work.OutOfQuotaPolicy;
import androidx.work.WorkInfo;
import androidx.work.WorkManager;import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.widget.ImageView;import java.util.UUID;public class MainActivity extends AppCompatActivity {private final String TAG = "mainActivity - log";private WorkManager workManager;private ImageView sharedImage;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);workManager = WorkManager.getInstance(this);bindView();}@Overrideprotected void onNewIntent(Intent intent) {super.onNewIntent(intent);Uri uri;if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) {uri = intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri.class);} else {uri = intent.getParcelableExtra(Intent.EXTRA_STREAM);}assert uri != null;long size = 1024 * 20L;Data inputData = new Data.Builder().putString(ImageCompressionWorker.KEY_CONTENT_URI, uri.toString()).putLong(ImageCompressionWorker.KEY_COMPRESSION_THRESHOLD, size).build();OneTimeWorkRequest oneTimeWorkRequest = new OneTimeWorkRequest.Builder(ImageCompressionWorker.class).setInputData(inputData)
// .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST).build();workManager.enqueue(oneTimeWorkRequest);UUID id = oneTimeWorkRequest.getId();workManager.getWorkInfoByIdLiveData(id).observe(this, workInfo -> {if (workInfo.getState() == WorkInfo.State.SUCCEEDED) {Data outputData = workInfo.getOutputData();String filePath = outputData.getString(ImageCompressionWorker.KEY_RESULT_PATH);Bitmap bitmap = BitmapFactory.decodeFile(filePath);sharedImage.setImageBitmap(bitmap);}});}private void bindView() {sharedImage = findViewById(R.id.sharedImage);}
}
更多内容
这一节,有些流水账,看看就好
可以直接看官方文档吧
官方文档
执行加急工作


配额策略

加急工作 + CoroutineWorker + 通知
加急工作需要配合通知使用,否则会报错
将之前的继承Worker改为CoroutineWorker
重写方法getForegroundInfo



@Nullable@Overridepublic Object getForegroundInfo(@NonNull Continuation<? super ForegroundInfo> $completion) {return new ForegroundInfo(NOTIFICATION_ID, createNotification());}private Notification createNotification() {String CHANNEL_ID = "compressor_channel_id";String CHANNEL_NAME = "压缩图片通知通道";String NOTIFICATION_TITLE = "你有一个程序在压缩图片";int importance = NotificationManager.IMPORTANCE_HIGH;NotificationChannel notificationChannel;if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {notificationChannel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, importance);NotificationManager notificationManager = getSystemService(appContext, NotificationManager.class);assert notificationManager != null;notificationManager.createNotificationChannel(notificationChannel);}String NOTIFICATION_TEXT = "压缩中...";Intent intent = new Intent(appContext, ImageCompressionWorker.class);PendingIntent pendingIntent = PendingIntent.getActivity(appContext, 0, intent, PendingIntent.FLAG_IMMUTABLE);return new NotificationCompat.Builder(appContext, CHANNEL_ID).setContentTitle(NOTIFICATION_TITLE).setContentText(NOTIFICATION_TEXT).setSmallIcon(R.drawable.ic_launcher_background).setContentIntent(pendingIntent).build();}
通知

在Android 12 之前的版本运行,会有通知显示;
通知需要申请权限
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>

private static final String[] PERMISSION_REQUIRED = new String[]{Manifest.permission.POST_NOTIFICATIONS};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);workManager = WorkManager.getInstance(this);bindView();if (!checkAllPermissions()) {requestPermissions(PERMISSION_REQUIRED, REQUEST_CODE);}}private boolean checkAllPermissions() {for (String permission : PERMISSION_REQUIRED) {int permissionCheck = ContextCompat.checkSelfPermission(this, permission);if (permissionCheck == PackageManager.PERMISSION_DENIED) {return false;}}return true;}
运行项目
压缩过程很快,压缩完成之后,通知关闭了

调度定期工作
每间隔一小时的最后15分钟工作一次

为了方便测试,这里使用15分钟一次
WorkRequest uploadRequest = new PeriodicWorkRequest.Builder(PeriodicUploadLogWorker.class, 15, TimeUnit.MINUTES, 15, TimeUnit.MINUTES).build();WorkManager workManager = WorkManager.getInstance(this);workManager.enqueue(uploadRequest);
public class PeriodicUploadLogWorker extends Worker {private final String TAG = "periodic_upload_log";public PeriodicUploadLogWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {super(context, workerParams);}@NonNull@Overridepublic Result doWork() {uploadLog();return Result.success();}private void uploadLog() {Log.i(TAG, String.valueOf(System.currentTimeMillis()));}
}

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


延迟工作

重试和退避政策


标记工作


分配输入数据
setInputData 传入数据
getInputData 获取数据


唯一工作



查询工作
按id、name、tag查询

WorkQuery

取消工作

链接工作
将每个Worker链接起来,按顺序执行。

还可以定义合并器

默认合并器,变量名一致的,值采用最新的覆盖前者


第二种,会保留返回的结果,会合并相同变量名到一个数组中



长时间运行worker
观察Worker的中间进度
更新工作
相关文章:
【重生之我在学Android】WorkManager (章一)
相关文章 【重生之我在学Android原生】ContentProvider(Java) 【重生之我在学Android原生】Media3 【重生之我在学Android】WorkManager (章一) 前言 官方文档 官方推荐 - 前台服务、后台服务都可以使用WorkManger来实现 案例 语言:JA…...
【强训笔记】day23
NO.1 思路:直接计算结果,先计算怪物可以抗几次攻击,再计算勇士受到的伤害,如果勇士的攻击力大于等于怪物的血量,那么就可以击杀无数只,如果勇士的血量正好是受到攻击的整数倍,那么击杀的怪物数…...
C语言-STM32:介绍PWM,并使用PWM实现呼吸灯
1、什么是PWM PWM,全称为Pulse Width Modulation,中文名为脉冲宽度调制。这是一种模拟控制技术,通过改变脉冲信号的宽度来表征一个连续变量的平均值,通常用于对模拟信号的数字化控制,特别是在功率转换和信号处理中非常…...
SpringBean详解
文章目录 概述Spring获取Bean的流程依赖注入bean的作用域Spring 中的 Bean 是线程安全的吗Spring如何处理线程并发问题bean 的自动装配和方式Resource和Autowired的区别bean的自动装配bean的生命周期BeanFactoryBeanFactory 常用的实现类有哪些BeanFactory与FactoryBean的不同A…...
hive获取这周五到下周四的区间,周一到周日的区间
-- 获取每个日期所在周期的开始和结束时间 SELECTcreated_date AS date_in_period,CASEWHEN date_format(created_date, u) < 5 THEN date_sub(created_date, cast(date_format(created_date, u) AS INT) 2)ELSE date_sub(created_date, cast(date_format(created_date, u)…...
Iterable与Iterator
Iterator public interface Iterator<E> {} terator是一个接口,它是集合的迭代器。集合可以通过Iterator去遍历集合中的元素。Iterator提供的API接口如下: forEachRemaining(Consumer<? super E> action):为每个剩余元素执行给…...
免费Premiere模板,几何图形元素动画视频幻灯片模板素材下载
Premiere Pro模板,几何图形元素动画视频幻灯片模板 ,组织良好,易于自定义。包括PDF教程。 项目特点: 使用Adobe Premiere Pro 2021及以上版本。 19201080全高清。 不需要插件。 包括帮助视频。 免费下载:https://prmu…...
数据结构与算法学习笔记九---循环队列的表示和实现(C++)
目录 前言 1.为什么要使用循环队列 2.队列的顺序存储方式的实现 1.定义 2.队列初始化 3.销毁 4.清空队列 5.队列是否为空 6.队列长度 7.队头 8.入队 9.出队 10.遍历队列 11.完整代码 3.参考资料 前言 这篇文章介绍循环队列的表示和用法。 1.为什么要使用循环队…...
Mysql获取当前时间
1、今天开始时间和结束时间 SELECT DATE_FORMAT(NOW(),’%Y-%m-%d 00:00:00’) AS ‘今天开始’; SELECT DATE_FORMAT(NOW(),’%Y-%m-%d 23:59:59’) AS ‘今天结束’;2、昨天的开始时间和结束时间 SELECT DATE_FORMAT( DATE_SUB(CURDATE(), INTERVAL 1 DAY), ‘%Y-%m-%d 00:…...
计算机服务器中了locked勒索病毒怎么解决,locked勒索病毒解密恢复工具
在网络技术飞速发展的时代,通过网络开展各项工作业务成为众多企业的首选,网络也为企业的生产运营提供了极大便利,大大提升了企业办公效率,但是利用网络避免不了网络威胁的存在,数据安全问题一直是企业关心的主要话题。…...
基于springboot实现的在线动漫信息平台
开发语言:Java 框架:springboot JDK版本:JDK1.8 服务器:tomcat7 数据库:mysql 5.7(一定要5.7版本) 数据库工具:Navicat11 开发软件:eclipse/myeclipse/idea Maven…...
C# Winform+Halcon结合标准视觉工具
介绍 winform与halcon结合标准化工具实例 软件架构 软件架构说明 基于NET6 WINFORMHALCON 实现标准化视觉检测工具 集成相机通讯 集成PLC通讯 TCP等常见通讯 支持常见halcon算子 图形采集blob分析高精度匹配颜色提取找几何体二维码提取OCR识别等等 。。。 安装教程 …...
英语单词量测试
网址:https://preply.com/en/learn/english/test-your-vocab 测试结果: 细节:英语母语者有20000-35000个单词的词汇量,8岁孩子的词汇量在8000个左右。而不是我们教育系统里说的,6000个单词足够用了。足够用࿰…...
三、安装node_exporter
目录 一、简介 二、下载安装 一、简介 Exporter是Prometheus的指标数据收集组件。它负责从目标Jobs收集数据,并把收集到的数据转换为Prometheus支持的时序数据格式。 和传统的指标数据收集组件不同的是,他只负责收集,并不向Server端发送数据…...
kafka基础知识
kafka架构 producer -> kafka cluster(broker>topic>partition) -> consumer -> zookeeper kafka压测 kafka-producer-perf-test.sh kafka-consumer-perf-test.sh kafka日志保存位置及消息保存时间 /tpdata/client/Kafka/kafka/config/server.properties log.…...
华为昇腾310B1平台视频解码失败[ERROR] Send frame to vdec failed, errorno:507018
目录 1 [ERROR] Send frame to vdec failed, errorno:507018 2 bug解决尝试1 3 bug解决尝试2 4 最终解决方法 参考文献: 1 [ERROR] Send frame to vdec failed, errorno:507018 某项目中的代码运行报错 [ERROR] Send frame to vdec failed, errorno:507018 Ac…...
Flutter 中的 SwitchListTile 小部件:全面指南
Flutter 中的 SwitchListTile 小部件:全面指南 在Flutter的Material组件库中,SwitchListTile是一个包含开关(Switch)的列表项,非常适合用来创建带有标题、副标题以及开关的列表项,常用于设置界面ÿ…...
详细分析Vue3中的defineExpose(附Demo)
目录 前言1. 基本知识2. Demo3. 实战 前言 其基本知识可参考官网:Vue3中的defineExpose 1. 基本知识 defineExpose 是 Vue 3 的 Composition API 中一个新的实用函数,用于在 <script setup> 语法下显式暴露组件的公共属性和方法 这在处理子组件…...
合合信息:TextIn文档解析技术与高精度文本向量化模型再加速
文章目录 前言现有大模型文档解析问题表格无法解析无法按照阅读顺序解析文档编码错误 诉求文档解析技术技术难点技术架构关键技术回根溯源 文本向量化模型结语 前言 随着人工智能技术的持续演进,大语言模型在我们日常生活中正逐渐占据举足轻重的地位。大模型语言通…...
Git与Gitlab
第1章Git概述 Git是一个免费的、开源的分布式版本控制系统,可以快速高效地处理从小型到大型的各种项目。 代码托管中心,记录每个版本的代码,从项目创建到现在使用的代码,中间所有的修改都有记录。 1. 何为版本控制 版本控制是…...
Qwen3.5-2B边缘部署案例:车载终端实时识别路标+语音播报导航提示
Qwen3.5-2B边缘部署案例:车载终端实时识别路标语音播报导航提示 1. 项目背景与需求 在智能驾驶和车载辅助系统领域,实时路标识别与语音导航是提升驾驶安全性的关键技术。传统方案通常需要: 独立的视觉识别模块处理路标额外的语音合成引擎生…...
别再死记硬背了!用Verilog手写一个四位加减法器,帮你彻底搞懂补码和逻辑门
从逻辑门到补码运算:Verilog四位加减法器的硬件思维解密 记得第一次在《数字逻辑》课上听到"补码"这个概念时,我和大多数同学一样满脸困惑——为什么计算机要用这么绕的方式处理负数?直到亲手用Verilog实现了一个四位加减法器&…...
千问3.5-9B模型切换指南:OpenClaw多模型动态调用
千问3.5-9B模型切换指南:OpenClaw多模型动态调用 1. 为什么需要多模型动态调用 上周我尝试用OpenClaw自动整理电脑里积压的300多份PDF文档时,遇到了一个有趣的现象:处理简单文件重命名任务时,轻量级模型响应飞快;但遇…...
Open Event Server数据导入导出完全指南:支持JSON、XML、iCal格式的终极教程
Open Event Server数据导入导出完全指南:支持JSON、XML、iCal格式的终极教程 【免费下载链接】open-event-server The Open Event Organizer Server to Manage Events https://test-api.eventyay.com 项目地址: https://gitcode.com/gh_mirrors/op/open-event-ser…...
不止是打字机效果:手把手教你用SpannableStringBuilder打造Android富文本AI对话界面
超越基础文本渲染:用SpannableStringBuilder构建专业级AI对话界面 在移动应用开发中,AI对话界面的用户体验往往决定了产品的专业度。传统的TextView虽然能显示文字,但要实现类似DeepSeek等专业AI产品的交互效果,需要深入掌握Andro…...
7款AI论文写作工具推荐:爱毕业aibiye等平台提供自动排版及LaTeX模板适配
工具快速对比排名(前7推荐) 工具名称 核心功能亮点 处理时间 适配平台 aibiye 学生/编辑双模式降AIGC 1分钟 知网、万方等 aicheck AI痕迹精准弱化查重一体 ~20分钟 知网、格子达、维普 askpaper AIGC率个位数优化 ~20分钟 高校检测规则通…...
保姆级教程:用ArduPilot给无人车/船配置避障(附MR72雷达、TFmini Plus参数)
保姆级教程:用ArduPilot为无人车/船配置毫米波与激光雷达避障系统 当你的无人车在野外自动巡航时突然检测到前方障碍物,是紧急刹车还是智能绕行?水面无人船在夜间航行如何避开漂浮物?本文将手把手带你完成从硬件选型到参数调优的全…...
别只盯着价格!用统计学和三角函数“解剖”波场哈希:一份给数据科学家的区块链数据分析指南
区块链哈希值的数据科学探索:从统计建模到三角分析 区块链技术正在重塑数据科学的边界,而哈希值作为其核心组件之一,蕴含着丰富的数学特征等待挖掘。对于具备统计学基础的研究者而言,这些看似随机的字符串实际上是绝佳的研究样本。…...
如何高效利用孔祥仁线性代数网课?我的实战笔记与技巧分享
如何高效利用孔祥仁线性代数网课?我的实战笔记与技巧分享 线性代数作为数学领域的重要分支,在计算机科学、物理学、工程学等多个学科中都有广泛应用。对于许多学生来说,这门课程既抽象又充满挑战。孔祥仁老师的线性代数网课以其"零废话&…...
Beyond Compare许可证获取与激活全攻略
1. Beyond Compare简介与许可证类型解析 Beyond Compare作为一款老牌文件对比工具,已经陪伴开发者走过了20多个年头。我第一次接触它是在2015年做代码合并时,当时就被它直观的三栏式对比界面惊艳到了——左右两侧显示对比内容,中间实时标注差…...
