当前位置: 首页 > news >正文

Android 高版本 DownloadManager 封装工具类,支持 APK 断点续传与自动安装

主要有以下优点

  1. 兼容高版本 Android:适配 Android 10 及以上版本的存储权限和安装权限。
  2. 断点续传:支持从断点继续下载。
  3. 下载进度监听:实时获取下载进度并回调。
  4. 错误处理:处理下载失败、网络异常等情况。
  5. 自动安装 APK:下载完成后自动安装 APK 文件。
  6. 通知栏进度:显示下载进度和状态。

优化后的 DownloadManager 工具类

import android.app.DownloadManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import androidx.core.content.FileProvider;
import java.io.File;

public class DownloadUtils {

    private static final String TAG = "DownloadUtils";
    private static DownloadUtils instance;
    private DownloadManager downloadManager;
    private long downloadId;
    private Context context;
    private DownloadProgressListener progressListener;
    private DownloadObserver downloadObserver;

    private DownloadUtils(Context context) {
        this.context = context.getApplicationContext();
        downloadManager = (DownloadManager) this.context.getSystemService(Context.DOWNLOAD_SERVICE);
    }

    public static synchronized DownloadUtils getInstance(Context context) {
        if (instance == null) {
            instance = new DownloadUtils(context);
        }
        return instance;
    }

    /**
     * 下载文件
     *
     * @param url      文件下载地址
     * @param fileName 保存的文件名
     * @param listener 下载进度监听器
     */
    public void downloadFile(String url, String fileName, DownloadProgressListener listener) {
        this.progressListener = listener;

        // 创建下载请求
        DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
        request.setTitle("文件下载");
        request.setDescription("正在下载文件...");
        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);

        // 设置下载路径
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            // Android 10 及以上版本,使用应用专属目录
            request.setDestinationInExternalFilesDir(context, Environment.DIRECTORY_DOWNLOADS, fileName);
        } else {
            // Android 10 以下版本,使用公共下载目录
            request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);
        }

        // 支持断点续传
        request.setAllowedOverMetered(true); // 允许使用移动网络
        request.setAllowedOverRoaming(true); // 允许漫游时下载

        // 开始下载
        downloadId = downloadManager.enqueue(request);

        // 注册下载完成监听
        context.registerReceiver(downloadCompleteReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));

        // 注册下载进度监听
        if (progressListener != null) {
            downloadObserver = new DownloadObserver(new Handler(Looper.getMainLooper()), downloadId);
            context.getContentResolver().registerContentObserver(
                    Uri.parse("content://downloads/my_downloads"), true, downloadObserver);
        }
    }

    /**
     * 下载完成的广播接收器
     */
    private final BroadcastReceiver downloadCompleteReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
            if (id == downloadId) {
                if (progressListener != null) {
                    progressListener.onDownloadComplete();
                }
                installApk(context);
            }
        }
    };

    /**
     * 安装 APK 文件
     */
    private void installApk(Context context) {
        File apkFile;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            // Android 10 及以上版本,使用应用专属目录
            apkFile = new File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "app-update.apk");
        } else {
            // Android 10 以下版本,使用公共下载目录
            apkFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "app-update.apk");
        }

        if (!apkFile.exists()) {
            Log.e(TAG, "APK 文件不存在");
            return;
        }

        // 使用 FileProvider 获取文件的 Uri
        Uri apkUri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", apkFile);

        // 创建安装 Intent
        Intent installIntent = new Intent(Intent.ACTION_VIEW);
        installIntent.setDataAndType(apkUri, "application/vnd.android.package-archive");
        installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        // 适配 Android 7.0 及以上版本
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        }

        // 适配 Android 8.0 及以上版本,允许安装未知来源的应用
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            if (!context.getPackageManager().canRequestPackageInstalls()) {
                // 跳转到设置页面,允许安装未知来源应用
                Intent intent = new Intent(android.provider.Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,
                        Uri.parse("package:" + context.getPackageName()));
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                context.startActivity(intent);
                return;
            }
        }

        context.startActivity(installIntent);

        // 注销广播接收器和内容观察者
        context.unregisterReceiver(downloadCompleteReceiver);
        if (downloadObserver != null) {
            context.getContentResolver().unregisterContentObserver(downloadObserver);
        }
    }

    /**
     * 下载进度观察者
     */
    private class DownloadObserver extends ContentObserver {
        private final long downloadId;

        public DownloadObserver(Handler handler, long downloadId) {
            super(handler);
            this.downloadId = downloadId;
        }

        @Override
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);
            queryDownloadProgress();
        }

        private void queryDownloadProgress() {
            DownloadManager.Query query = new DownloadManager.Query();
            query.setFilterById(downloadId);
            try (Cursor cursor = downloadManager.query(query)) {
                if (cursor != null && cursor.moveToFirst()) {
                    int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
                    int bytesDownloaded = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
                    int bytesTotal = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));

                    if (status == DownloadManager.STATUS_RUNNING && bytesTotal > 0) {
                        int progress = (int) ((bytesDownloaded * 100L) / bytesTotal);
                        if (progressListener != null) {
                            progressListener.onProgress(progress);
                        }
                    } else if (status == DownloadManager.STATUS_FAILED) {
                        if (progressListener != null) {
                            progressListener.onError("下载失败");
                        }
                    }
                }
            }
        }
    }

    /**
     * 下载进度监听器
     */
    public interface DownloadProgressListener {
        void onProgress(int progress); // 下载进度(0-100)
        void onError(String message);  // 下载失败
        void onDownloadComplete();     // 下载完成
    }
}

使用示例

// 初始化工具类
DownloadUtils downloadUtils = DownloadUtils.getInstance(context);

// 开始下载
downloadUtils.downloadFile("https://example.com/app-update.apk", "app-update.apk", new DownloadUtils.DownloadProgressListener() {
    @Override
    public void onProgress(int progress) {
        Log.d(TAG, "下载进度: " + progress + "%");
    }

    @Override
    public void onError(String message) {
        Log.e(TAG, "下载失败: " + message);
    }

    @Override
    public void onDownloadComplete() {
        Log.d(TAG, "下载完成");
    }
});

优化点说明

  1. 兼容高版本 Android

    • 使用 FileProvider 提供文件的 Uri,适配 Android 7.0 及以上版本。
    • 适配 Android 8.0 及以上版本的未知来源应用安装权限。
  2. 断点续传

    • 通过 setAllowedOverMeteredsetAllowedOverRoaming 支持断点续传。
  3. 下载进度监听

    • 使用 ContentObserver 监听下载进度,并通过回调实时更新 UI。
  4. 错误处理

    • DownloadObserver 中检查下载状态,如果下载失败,通过 onError 回调通知调用方。
  5. 自动安装 APK

    • 下载完成后自动安装 APK 文件,适配高版本 Android 的权限要求。

总结

该工具类封装了 DownloadManager 的核心功能,并针对高版本 Android 进行了优化,支持断点续传、下载进度监听和自动安装 APK 文件。适用于需要文件下载和 APK 升级功能的 Android 应用。

相关文章:

  • 玩转python:通俗易懂掌握高级数据结构-collections模块之Counter
  • 利用委托用户控件、窗体之间传值 c#
  • 响应式编程-基于Reactor模式WebFlux框架的Spring Gateway
  • 生成省市区JSON
  • http 405 Not Allowed
  • 2018年全国职业院校技能大赛-高职组计算机网络应用竞赛竞赛样题A卷
  • 一文讲通锁标记对象std::adopt_lock盲点
  • OpenAI与谷歌DeepMind新品同日竞技,谁能引领机器人现实任务新潮流?
  • C#-委托delegate
  • C++设计模式-观察者模式:从基本介绍,内部原理、应用场景、使用方法,常见问题和解决方案进行深度解析
  • 网络视频监控平台在医疗领域的应用
  • 浏览器中输入 URL 到显示主页的完整过程
  • 【后端】【django】Django 自带的用户系统与 RBAC 机制
  • 历次科技泡沫对人工智能发展的启示与规避措施
  • containerd 拉取镜像的工具以及优劣
  • Python----计算机视觉处理(opencv:图片灰度化)
  • go 安装swagger
  • 【论文精读】Deformable DETR:用于端到端目标检测可变形 Transformer
  • go 加载yaml配置文件
  • 3-1 写分享报告
  • 巴西总统卢拉昨晚抵达北京
  • 中美经贸高层会谈将在午餐后继续
  • 体坛联播|郑钦文收获红土赛季首胜,国际乒联公布财报
  • 晋级中部非省会第一城,宜昌凭什么
  • 国家主席习近平同普京总统出席签字和合作文本交换仪式
  • 大风暴雨致湖南岳阳县6户房屋倒塌、100多户受损