Android DownloadManager下载完成通知打开临时文件 如何指向实际存储路径?
问题根因
该现象是Android 7.0及以上版本的系统默认行为:为了加强文件访问安全,系统禁止应用间直接传递file://格式的文件路径,DownloadManager返回给通知点击事件的是content://格式的URI,这类URI由系统媒体库/ContentProvider封装,第三方应用解析时就会显示为/document/数字ID的形式,和实际存储路径无关,文件本身存储是正常的。
解决方案
方案1:最简适配(无需自定义逻辑)
你当前的代码缺失了MIME类型声明,显式指定下载文件的MIME类型后,系统会优化URI的解析逻辑,大部分文件管理器可以直接识别到实际存储路径:
DownloadManager downloadmanager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE); Uri uri = Uri.parse("https://somedomain.com/myfile.jpg"); String filename = "myDownloadedFile.jpg"; DownloadManager.Request request = new DownloadManager.Request(uri); request.setTitle(filename); request.setDescription("Downloading"); request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); // 新增MIME类型设置,根据实际文件类型调整,此处为JPG图片示例 request.setMimeType("image/jpeg"); request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS,"myAppName/".concat(filename)); downloadmanager.enqueue(request);
常见文件MIME类型参考:
- APK安装包:
application/vnd.android.package-archive - PDF文件:
application/pdf - 普通文本:
text/plain - MP4视频:
video/mp4
方案2:完全自定义通知点击行为(100%兼容所有场景)
如果需要确保所有应用都能拿到实际文件路径,可以自己监听下载完成广播,自定义通知的点击逻辑,直接指向实际存储的文件:
- 首先在
AndroidManifest.xml中注册下载完成广播和FileProvider(Android7.0+文件访问必需配置)
<!-- 注册下载完成广播接收器 --> <receiver android:name=".DownloadCompleteReceiver"> <intent-filter> <action android:name="android.intent.action.DOWNLOAD_COMPLETE" /> </intent-filter> </receiver> <!-- 配置FileProvider --> <provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>
- 在
res/xml目录下新建file_paths.xml配置文件:
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-path name="downloads" path="Download/" /> </paths>
- 修改原DownloadManager配置,隐藏系统默认通知:
// 将原配置中的VISIBILITY参数修改为隐藏系统通知 request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
- 实现广播接收器,自定义通知点击逻辑:
public class DownloadCompleteReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { long downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1); if (downloadId == -1) return; DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId); Cursor cursor = downloadManager.query(query); if (cursor.moveToFirst()) { int status = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS)); if (status == DownloadManager.STATUS_SUCCESSFUL) { // 获取下载文件的实际本地路径 String localUri = cursor.getString(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_URI)); File targetFile = new File(Uri.parse(localUri).getPath()); // 构造打开文件的Intent Intent openIntent = new Intent(Intent.ACTION_VIEW); Uri fileUri; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { fileUri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", targetFile); openIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); } else { fileUri = Uri.fromFile(targetFile); } // 替换为对应文件的MIME类型 openIntent.setDataAndType(fileUri, "image/jpeg"); openIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 构造自定义通知 PendingIntent pendingIntent = PendingIntent.getActivity( context, (int) downloadId, openIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE ); NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); // Android8.0+需要配置通知渠道 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel( "download_notify", "下载通知", NotificationManager.IMPORTANCE_DEFAULT ); notificationManager.createNotificationChannel(channel); } Notification notification = new NotificationCompat.Builder(context, "download_notify") .setSmallIcon(android.R.drawable.stat_sys_download_done) .setContentTitle("下载完成") .setContentText(targetFile.getName()) .setContentIntent(pendingIntent) .setAutoCancel(true) .build(); notificationManager.notify((int) downloadId, notification); } } cursor.close(); } }
内容的提问来源于stack exchange,提问作者Fat Monk
相关产品推荐
相关产品推荐

