当我试图打开一个文件时,应用程序崩溃了。它可以在Android Nougat下运行,但在Android Nougat上它会崩溃。只有当我试图从SD卡,而不是从系统分区打开文件时,它才会崩溃。权限问题?

示例代码:

File file = new File("/storage/emulated/0/test.txt");
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "text/*");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent); // Crashes on this line

日志:

android.os.FileUriExposedException: ///storage/emulated/0/test.txt Intent.getData ()

编辑:

当针对Android Nougat时,file:// uri不再被允许。我们应该使用content:// uri。但是,我的应用程序需要打开根目录下的文件。什么好主意吗?


当前回答

只需将下面的代码粘贴到Activity onCreate()中:

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());

它将忽略URI公开。

其他回答

Xamarin的。安卓

注意:路径xml/provider_paths.xml (.axml)无法解析,即使在将xml文件夹放在资源下(也许它可以放在像Values这样的现有位置,但没有尝试),所以我采用了这个方法,目前它是有效的。测试表明,每次应用程序运行时只需要调用它一次(这是有意义的,因为它改变了主机VM的操作状态)。

注意:xml需要大写,所以Resources/ xml /provider_paths.xml

Java.Lang.ClassLoader cl = _this.Context.ClassLoader;
Java.Lang.Class strictMode = cl.LoadClass("android.os.StrictMode");                
System.IntPtr ptrStrictMode = JNIEnv.FindClass("android/os/StrictMode");
var method = JNIEnv.GetStaticMethodID(ptrStrictMode, "disableDeathOnFileUriExposure", "()V");                
JNIEnv.CallStaticVoidMethod(strictMode.Handle, method);

如果你的targetSdkVersion是24或更高,你不能在Android 7.0+设备的intent中使用file: Uri值。

你的选择是:

将targetSdkVersion降至23或更低,或者 把你的内容放在内部存储,然后使用FileProvider使它对其他应用程序有选择性地可用

例如:

Intent i=new Intent(Intent.ACTION_VIEW, FileProvider.getUriForFile(this, AUTHORITY, f));

i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(i);

(来自这个示例项目)

只需将下面的代码粘贴到Activity onCreate()中:

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());

它将忽略URI公开。

除了使用FileProvider的解决方案,还有另一种方法可以解决这个问题。简单地说

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());

在Application.onCreate()。通过这种方式,VM忽略文件URI公开。

方法

builder.detectFileUriExposure()

启用文件暴露检查,如果我们不设置VmPolicy,这也是默认行为。

我遇到了一个问题,如果我使用content:// URI发送一些东西,一些应用程序就是不能理解它。并且不允许降低目标SDK版本。在这种情况下,我的解决方案是有用的。

更新:

正如评论中提到的,StrictMode是诊断工具,不应该用于此问题。当我在一年前发布这个答案时,许多应用程序只能接收文件uri。当我试图向它们发送FileProvider uri时,它们就崩溃了。这在大多数应用程序中都是固定的,所以我们应该使用FileProvider解决方案。

我知道这是一个相当老的问题,但这个答案是给未来的观众的。所以我遇到过类似的问题,经过研究,我找到了这种方法的替代方案。

你的意图 从Kotlin中的路径查看您的图像

 val intent = Intent()
 intent.setAction(Intent.ACTION_VIEW)
 val file = File(currentUri)
 intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
 val contentURI = getContentUri(context!!, file.absolutePath)
 intent.setDataAndType(contentURI,"image/*")
 startActivity(intent)

主要功能如下

private fun getContentUri(context:Context, absPath:String):Uri? {
        val cursor = context.getContentResolver().query(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            arrayOf<String>(MediaStore.Images.Media._ID),
            MediaStore.Images.Media.DATA + "=? ",
            arrayOf<String>(absPath), null)
        if (cursor != null && cursor.moveToFirst())
        {
            val id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID))
            return Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, Integer.toString(id))
        }
        else if (!absPath.isEmpty())
        {
            val values = ContentValues()
            values.put(MediaStore.Images.Media.DATA, absPath)
            return context.getContentResolver().insert(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
        }
        else
        {
            return null
        }
    }

同样地,除了图像,你可以使用任何其他文件格式,比如pdf,在我的例子中,它工作得很好