【重生之我在学Android】WorkManager (章一)

相关文章

【重生之我在学Android原生】ContentProvider(Java)
【重生之我在学Android原生】Media3
【重生之我在学Android】WorkManager (章一)

前言

官方文档

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

案例

语言:JAVA

实现要求

一步步实现一个图片压缩APP

创建项目

添加WorkManager依赖

参考文章

添加到builder.gradle, sync一下

kt 复制代码
    val workVersion = "2.9.0"
    implementation("androidx.work:work-runtime:$workVersion")

接收share来的图片数据

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

xml 复制代码
<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

xml 复制代码
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系统使用

java 复制代码
bytes = new byte[inputStream.available()];
             inputStream.read(bytes);

运行项目

完整代码

java 复制代码
// 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);
        }
    }
}
java 复制代码
// 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


java 复制代码
    @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 之前的版本运行,会有通知显示;

通知需要申请权限

xml 复制代码
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
java 复制代码
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分钟一次

java 复制代码
WorkRequest uploadRequest = new PeriodicWorkRequest
                .Builder(PeriodicUploadLogWorker.class, 15, TimeUnit.MINUTES, 15, TimeUnit.MINUTES)
                .build();
        WorkManager workManager = WorkManager.getInstance(this);
        workManager.enqueue(uploadRequest);
java 复制代码
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链接起来,按顺序执行。

还可以定义合并器

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

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


长时间运行worker

观察Worker的中间进度

更新工作

相关推荐
ChinaDragonDreamer2 小时前
Kotlin:2.0.20 的新特性
android·开发语言·kotlin
网络研究院4 小时前
Android 安卓内存安全漏洞数量大幅下降的原因
android·安全·编程·安卓·内存·漏洞·技术
凉亭下4 小时前
android navigation 用法详细使用
android
小比卡丘7 小时前
C语言进阶版第17课—自定义类型:联合和枚举
android·java·c语言
前行的小黑炭8 小时前
一篇搞定Android 实现扫码支付:如何对接海外的第三方支付;项目中的真实经验分享;如何高效对接,高效开发
android
落落落sss9 小时前
MybatisPlus
android·java·开发语言·spring·tomcat·rabbitmq·mybatis
代码敲上天.10 小时前
数据库语句优化
android·数据库·adb
GEEKVIP12 小时前
手机使用技巧:8 个 Android 锁屏移除工具 [解锁 Android]
android·macos·ios·智能手机·电脑·手机·iphone
model200514 小时前
android + tflite 分类APP开发-2
android·分类·tflite
彭于晏68914 小时前
Android广播
android·java·开发语言