相关文章
【重生之我在学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
@Override
public 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;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
workManager = WorkManager.getInstance(this);
bindView();
}
@Override
protected 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
@Override
public 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
};
@Override
protected 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
@Override
public Result doWork() {
uploadLog();
return Result.success();
}
private void uploadLog() {
Log.i(TAG, String.valueOf(System.currentTimeMillis()));
}
}
工作约束
将工作延迟到满足最佳条件时运行
延迟工作
重试和退避政策
标记工作
分配输入数据
setInputData 传入数据
getInputData 获取数据
唯一工作
查询工作
按id、name、tag查询
WorkQuery
取消工作
链接工作
将每个Worker链接起来,按顺序执行。
还可以定义合并器
默认合并器,变量名一致的,值采用最新的覆盖前者
第二种,会保留返回的结果,会合并相同变量名到一个数组中