java.lang.SecurityException: 权限拒绝:不允许在KitKat上发送广播android.intent.action.MEDIA_MOUNTED。

16

我正在使用DownloadManager从我们的服务器下载图片,并将文件放置在externalFilesDir中。

我发送广播意图,因为我不希望这些下载的图片出现在相册中。

sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + context.getExternalFilesDir(null))));

我只在我的Galaxy S3上测试过这个应用程序,之前它可以正常运行在Jelly Bean 4.3上,但是当我在KitKat 4.4上测试时,它会导致应用程序崩溃。

是否有更好的方法使从DownloadManager下载的文件不出现在手机相册中?

堆栈跟踪

06-05 17:34:41.940: E/AndroidRuntime(15410): FATAL EXCEPTION: main
06-05 17:34:41.940: E/AndroidRuntime(15410): Process: com.walintukai.lfdate, PID: 15410
06-05 17:34:41.940: E/AndroidRuntime(15410): java.lang.RuntimeException: Error receiving broadcast Intent { act=android.intent.action.DOWNLOAD_COMPLETE flg=0x10 pkg=com.walintukai.lfdate (has extras) } in com.walintukai.lfdate.SocketIOService$1@42359f40
06-05 17:34:41.940: E/AndroidRuntime(15410):    at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:778)
06-05 17:34:41.940: E/AndroidRuntime(15410):    at android.os.Handler.handleCallback(Handler.java:733)
06-05 17:34:41.940: E/AndroidRuntime(15410):    at android.os.Handler.dispatchMessage(Handler.java:95)
06-05 17:34:41.940: E/AndroidRuntime(15410):    at android.os.Looper.loop(Looper.java:136)
06-05 17:34:41.940: E/AndroidRuntime(15410):    at android.app.ActivityThread.main(ActivityThread.java:5057)
06-05 17:34:41.940: E/AndroidRuntime(15410):    at java.lang.reflect.Method.invokeNative(Native Method)
06-05 17:34:41.940: E/AndroidRuntime(15410):    at java.lang.reflect.Method.invoke(Method.java:515)
06-05 17:34:41.940: E/AndroidRuntime(15410):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:789)
06-05 17:34:41.940: E/AndroidRuntime(15410):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:605)
06-05 17:34:41.940: E/AndroidRuntime(15410):    at dalvik.system.NativeStart.main(Native Method)
06-05 17:34:41.940: E/AndroidRuntime(15410): Caused by: java.lang.SecurityException: Permission Denial: not allowed to send broadcast android.intent.action.MEDIA_MOUNTED from pid=15410, uid=10135
06-05 17:34:41.940: E/AndroidRuntime(15410):    at android.os.Parcel.readException(Parcel.java:1465)
06-05 17:34:41.940: E/AndroidRuntime(15410):    at android.os.Parcel.readException(Parcel.java:1419)
06-05 17:34:41.940: E/AndroidRuntime(15410):    at android.app.ActivityManagerProxy.broadcastIntent(ActivityManagerNative.java:2390)
06-05 17:34:41.940: E/AndroidRuntime(15410):    at android.app.ContextImpl.sendBroadcast(ContextImpl.java:1127)
06-05 17:34:41.940: E/AndroidRuntime(15410):    at android.content.ContextWrapper.sendBroadcast(ContextWrapper.java:365)
06-05 17:34:41.940: E/AndroidRuntime(15410):    at com.walintukai.lfdate.SocketIOService$1.onReceive(SocketIOService.java:111)
06-05 17:34:41.940: E/AndroidRuntime(15410):    at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:768)
06-05 17:34:41.940: E/AndroidRuntime(15410):    ... 9 more

1
你的问题不够明确:你是想要防止这些文件出现在图库中,还是想要确保它们出现在那里?如果要防止某个目录中的文件出现在图库(或其他媒体库)中,请在同一目录中放置一个名为.nomedia的文件。要立即让文件出现,请按照下面答案中所述运行媒体扫描。 - user149408
3个回答

47

看起来谷歌正在尝试防止KITKAT这样做。
查看core/rest/AndroidManifest.xml,您会注意到广播android.intent.action.MEDIA_MOUNTED现在受到保护。这意味着它是只有系统才能发送的广播。

<protected-broadcast android:name="android.intent.action.MEDIA_MOUNTED" />

以下内容适用于所有版本:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    final Intent scanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    final Uri contentUri = Uri.fromFile(outputFile); 
    scanIntent.setData(contentUri);
    sendBroadcast(scanIntent);
} else {
    final Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory()));
    sendBroadcast(intent);
}

如果上述方法无效,请尝试以下方法:
根据这篇post所说,你需要另一种修复方法。
比如使用MediaScannerConnectionACTION_MEDIA_SCANNER_SCAN_FILE
MediaScannerConnection.scanFile(this, new String[] {

file.getAbsolutePath()},

null, new MediaScannerConnection.OnScanCompletedListener() {

public void onScanCompleted(String path, Uri uri)

{


}

});

根据文档,MediaScannerConnection API 应该从 API 级别 1 开始工作,因此所有版本的 Android 都应该可以使用。http://developer.android.com/reference/android/media/MediaScannerConnection.html - Lazy Ninja
1
在Android 4.4.4以下的版本中,它无法正常工作并且无法刷新图库中的图像和文件夹。 - nida
1
第二个解决方案有效。谢谢回复。 - Umut ADALI
如果我没有访问应用程序源代码怎么办? - Paul

28

我知道现在有点晚了,但请试试这个方法,它适用于所有版本:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    Uri contentUri = Uri.fromFile(out); // out is your output file
    mediaScanIntent.setData(contentUri);
    this.sendBroadcast(mediaScanIntent);
} else {
    sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
        Uri.parse("file://" + Environment.getExternalStorageDirectory())));
}

如何获取文件:protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); } - Coas Mckey
谢谢,这对我的问题有所帮助:http://stackoverflow.com/questions/37996241/android-asus-nexus-7-does-not-commit-emulated-memory-until-restart - eplewis89

0

作为一种变体

public static void refreshDir(@NonNull Context context, @NonNull File dir) {

    notifyFileDeleted(context, dir);

    File[] files = dir.listFiles();

    if (files != null) {
        for (File file : files) {
            if (file.isDirectory())
                refreshDir(context, file);
            else
                notifyFileCreated(context, file);
        }
    }
}

public static void notifyFileCreated(@NonNull Context context, @NonNull File file) {
        //disable notification for non-existent files, folders and files from a location inaccessible via mtp
        if (file.exists() && file.isFile() && file.getAbsolutePath().indexOf("/data/data/") != 0) {
            MediaScannerConnection.scanFile(context, new String[]{file.getAbsolutePath()}, null, null);
            context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file)));
        }
    }

public static void notifyFileDeleted(@NonNull Context context, @NonNull File file) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
            context.getApplicationContext().sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.fromFile(file)));
        }
        else{
            Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
            Uri uri = Uri.fromFile(file);
            intent.setData(uri);
            context.sendBroadcast(intent);
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接