主要有以下优点
- 兼容高版本 Android:适配 Android 10 及以上版本的存储权限和安装权限。
- 断点续传:支持从断点继续下载。
- 下载进度监听:实时获取下载进度并回调。
- 错误处理:处理下载失败、网络异常等情况。
- 自动安装 APK:下载完成后自动安装 APK 文件。
- 通知栏进度:显示下载进度和状态。
优化后的 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() {@Overridepublic 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 获取文件的 UriUri apkUri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", apkFile);// 创建安装 IntentIntent 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;}@Overridepublic 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() {@Overridepublic void onProgress(int progress) {Log.d(TAG, "下载进度: " + progress + "%");}@Overridepublic void onError(String message) {Log.e(TAG, "下载失败: " + message);}@Overridepublic void onDownloadComplete() {Log.d(TAG, "下载完成");}
});
优化点说明
-
兼容高版本 Android:
- 使用
FileProvider
提供文件的 Uri,适配 Android 7.0 及以上版本。 - 适配 Android 8.0 及以上版本的未知来源应用安装权限。
- 使用
-
断点续传:
- 通过
setAllowedOverMetered
和setAllowedOverRoaming
支持断点续传。
- 通过
-
下载进度监听:
- 使用
ContentObserver
监听下载进度,并通过回调实时更新 UI。
- 使用
-
错误处理:
- 在
DownloadObserver
中检查下载状态,如果下载失败,通过onError
回调通知调用方。
- 在
-
自动安装 APK:
- 下载完成后自动安装 APK 文件,适配高版本 Android 的权限要求。
总结
该工具类封装了 DownloadManager
的核心功能,并针对高版本 Android 进行了优化,支持断点续传、下载进度监听和自动安装 APK 文件。适用于需要文件下载和 APK 升级功能的 Android 应用。