【Android】WorkManager(章二)

剩余的三部分
官方文档

案例

实现下载器,并监听下载进度

界面

定义Worker

在官方案例的前提下,进行完善

下载download



下载进度

授予权限


开始工作并监听

完整代码

MainActivity.java

java 复制代码
package com.test.downloadworkerapplication;

import static android.content.Context.NOTIFICATION_SERVICE;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC;
import static com.test.downloadworkerapplication.MainActivity.MY_LOG_TAG;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.net.Uri;
import android.os.Build;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;
import androidx.work.Data;
import androidx.work.ForegroundInfo;
import androidx.work.WorkManager;
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;
import java.net.URL;

public class DownloadWorker extends Worker {
    public static final String KEY_INPUT_URL = "KEY_INPUT_URL";
    public static final String KEY_OUTPUT_FILE_NAME = "KEY_OUTPUT_FILE_NAME";
    public static final String KEY_FILE_URI = "KEY_FILE_URI";
    public static final String PROGRESS = "PROGRESS";

    private final NotificationManager notificationManager;

    public DownloadWorker(
            @NonNull Context context,
            @NonNull WorkerParameters parameters) {
        super(context, parameters);
        notificationManager = (NotificationManager)
                context.getSystemService(NOTIFICATION_SERVICE);

        Data data = new Data.Builder().putInt(PROGRESS, 0).build();
        setProgressAsync(data);
    }

    @NonNull
    @Override
    public Result doWork() {
        Data inputData = getInputData();
        String inputUrl = inputData.getString(KEY_INPUT_URL);
        String outputFile = inputData.getString(KEY_OUTPUT_FILE_NAME);

        String progress = "Starting Download";
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            setForegroundAsync(createForegroundInfo(progress));
        }
        Uri uri = download(inputUrl, outputFile);

        Data progressData2 = new Data.Builder().putInt(PROGRESS, 100).build();
        setProgressAsync(progressData2);

        Data data = new Data.Builder()
                .putString(KEY_FILE_URI, uri.toString())
                .build();
        return Result.success(data);
    }

    private Uri download(String inputUrl, String outputFile) {
        // Downloads a file and updates bytes read
        // Calls setForegroundAsync(createForegroundInfo(myProgress))
        // periodically when it needs to update the ongoing Notification.
        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                String myProgress = "downloading...";
                setForegroundAsync(createForegroundInfo(myProgress));
            }

            long fileSize = FileSizeFetcher.getFileSize(inputUrl);
            URL url = new URL(inputUrl);
            InputStream inputStream = url.openStream();

            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = 0;
            int downloadLen = 0;
            while ((len = inputStream.read(buffer)) != -1) {
                downloadLen += len;
                byteArrayOutputStream.write(buffer, 0, len);

                /// 下载进度
                double progress = 1.0 * downloadLen / fileSize;
                Data data = new Data.Builder().putDouble(PROGRESS, progress).build();
                setProgressAsync(data);
            }
            byte[] byteArray = byteArrayOutputStream.toByteArray();
            inputStream.close();

            File file = new File(getApplicationContext().getCacheDir(), outputFile + ".pdf");
            FileOutputStream fileOutputStream = new FileOutputStream(file);
            fileOutputStream.write(byteArray);
            fileOutputStream.close();

            String absolutePath = file.getAbsolutePath();
            Log.i(MY_LOG_TAG, absolutePath);
            return Uri.fromFile(file);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.Q)
    @NonNull
    private ForegroundInfo createForegroundInfo(@NonNull String progress) {
        // Build a notification using bytesRead and contentLength
        Context context = getApplicationContext();
        String id = context.getString(R.string.notification_channel_id);
        String title = context.getString(R.string.notification_title);
        String cancel = context.getString(R.string.cancel_download);
        // This PendingIntent can be used to cancel the worker
        PendingIntent intent = WorkManager.getInstance(context)
                .createCancelPendingIntent(getId());

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            createChannel(id);
        }

        Notification notification = new NotificationCompat.Builder(context, id)
                .setContentTitle(title)
                .setTicker(title)
                .setContentText(progress)
                .setSmallIcon(R.drawable.ic_work_notification)
                .setOngoing(true)
                // Add the cancel action to the notification which can
                // be used to cancel the worker
                .addAction(android.R.drawable.ic_delete, cancel, intent)
                .build();

        int notificationId = 1000;
        return new ForegroundInfo(notificationId, notification, FOREGROUND_SERVICE_TYPE_DATA_SYNC);
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private void createChannel(String id) {
        // Create a Notification channel
        NotificationChannel channel = new NotificationChannel(id, "download channel", NotificationManager.IMPORTANCE_NONE);
        notificationManager.createNotificationChannel(channel);
    }
}

DownloadWorker.java

java 复制代码
package com.test.downloadworkerapplication;

import static android.content.Context.NOTIFICATION_SERVICE;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC;
import static com.test.downloadworkerapplication.MainActivity.MY_LOG_TAG;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.net.Uri;
import android.os.Build;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;
import androidx.work.Data;
import androidx.work.ForegroundInfo;
import androidx.work.WorkManager;
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;
import java.net.URL;

public class DownloadWorker extends Worker {
    public static final String KEY_INPUT_URL = "KEY_INPUT_URL";
    public static final String KEY_OUTPUT_FILE_NAME = "KEY_OUTPUT_FILE_NAME";
    public static final String KEY_FILE_URI = "KEY_FILE_URI";
    public static final String PROGRESS = "PROGRESS";

    private final NotificationManager notificationManager;

    public DownloadWorker(
            @NonNull Context context,
            @NonNull WorkerParameters parameters) {
        super(context, parameters);
        notificationManager = (NotificationManager)
                context.getSystemService(NOTIFICATION_SERVICE);

        Data data = new Data.Builder().putInt(PROGRESS, 0).build();
        setProgressAsync(data);
    }

    @NonNull
    @Override
    public Result doWork() {
        Data inputData = getInputData();
        String inputUrl = inputData.getString(KEY_INPUT_URL);
        String outputFile = inputData.getString(KEY_OUTPUT_FILE_NAME);

        String progress = "Starting Download";
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            setForegroundAsync(createForegroundInfo(progress));
        }
        Uri uri = download(inputUrl, outputFile);

        Data progressData2 = new Data.Builder().putInt(PROGRESS, 100).build();
        setProgressAsync(progressData2);

        Data data = new Data.Builder()
                .putString(KEY_FILE_URI, uri.toString())
                .build();
        return Result.success(data);
    }

    private Uri download(String inputUrl, String outputFile) {
        // Downloads a file and updates bytes read
        // Calls setForegroundAsync(createForegroundInfo(myProgress))
        // periodically when it needs to update the ongoing Notification.
        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                String myProgress = "downloading...";
                setForegroundAsync(createForegroundInfo(myProgress));
            }

            long fileSize = FileSizeFetcher.getFileSize(inputUrl);
            URL url = new URL(inputUrl);
            InputStream inputStream = url.openStream();

            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = 0;
            int downloadLen = 0;
            while ((len = inputStream.read(buffer)) != -1) {
                downloadLen += len;
                byteArrayOutputStream.write(buffer, 0, len);

                /// 下载进度
                double progress = 1.0 * downloadLen / fileSize;
                Data data = new Data.Builder().putDouble(PROGRESS, progress).build();
                setProgressAsync(data);
            }
            byte[] byteArray = byteArrayOutputStream.toByteArray();
            inputStream.close();

            File file = new File(getApplicationContext().getCacheDir(), outputFile + ".pdf");
            FileOutputStream fileOutputStream = new FileOutputStream(file);
            fileOutputStream.write(byteArray);
            fileOutputStream.close();

            String absolutePath = file.getAbsolutePath();
            Log.i(MY_LOG_TAG, absolutePath);
            return Uri.fromFile(file);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.Q)
    @NonNull
    private ForegroundInfo createForegroundInfo(@NonNull String progress) {
        // Build a notification using bytesRead and contentLength
        Context context = getApplicationContext();
        String id = context.getString(R.string.notification_channel_id);
        String title = context.getString(R.string.notification_title);
        String cancel = context.getString(R.string.cancel_download);
        // This PendingIntent can be used to cancel the worker
        PendingIntent intent = WorkManager.getInstance(context)
                .createCancelPendingIntent(getId());

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            createChannel(id);
        }

        Notification notification = new NotificationCompat.Builder(context, id)
                .setContentTitle(title)
                .setTicker(title)
                .setContentText(progress)
                .setSmallIcon(R.drawable.ic_work_notification)
                .setOngoing(true)
                // Add the cancel action to the notification which can
                // be used to cancel the worker
                .addAction(android.R.drawable.ic_delete, cancel, intent)
                .build();

        int notificationId = 1000;
        return new ForegroundInfo(notificationId, notification, FOREGROUND_SERVICE_TYPE_DATA_SYNC);
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private void createChannel(String id) {
        // Create a Notification channel
        NotificationChannel channel = new NotificationChannel(id, "download channel", NotificationManager.IMPORTANCE_NONE);
        notificationManager.createNotificationChannel(channel);
    }
}

FileSizeFetch.java

java 复制代码
package com.test.downloadworkerapplication;

import java.net.HttpURLConnection;
import java.net.URL;

public class FileSizeFetcher {
    public static long getFileSize(String urlString) {
        long fileSize = 0;
        HttpURLConnection connection = null;
        try {
            URL url = new URL(urlString);
            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("HEAD"); // 使用HEAD请求以节省带宽
            int responseCode = connection.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) {
                String contentLength = connection.getHeaderField("Content-Length");
                fileSize = Long.parseLong(contentLength);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
        return fileSize;
    }
}

运行


相关推荐
石山岭8 小时前
自己动手写了一个 Android 虚拟定位 App:GPSSimulate 技术实
android·前端
杉氧10 小时前
副作用 (Side Effects) 全攻略:如何像大师一样掌控 Composable 的生命周期?
android·架构·android jetpack
Kapaseker15 小时前
Kotlin Toolchain 0.11 发布:主要是把 Amper 干没了
android·kotlin
三少爷的鞋16 小时前
Android 现代架构不需要事件总线进阶篇
android
杉氧1 天前
深入理解 Compose 重组机制:快照系统如何驱动 UI 精准刷新?
android·架构·android jetpack
召钱熏1 天前
状态枚举正确≠渲染正确:一个语音按钮的状态机边界修复实录
android·前端
杉氧1 天前
深度解析:Jetpack Compose 核心架构与底层原理 —— 十年安卓老兵的“破茧重生”
android·架构·android jetpack
通玄1 天前
Jetpack Compose 入门系列(七):ViewModel 与界面状态管理
android
落魄Android在线炒饭1 天前
Android Framework 开发技巧:android.jar 生成与系统快速编译验证
android
如此风景1 天前
Kotlin Flow操作符学习
android·kotlin