android.os.FileUriExposedException:file:///storage/emulated/0/test.txt通过Intent.getData()暴露在app之外

时间:2016-07-05 09:51:41

标签: android android-file android-7.0-nougat

当我尝试打开文件时,应用程序崩溃了。它可以在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:   file:///storage/emulated/0/test.txt暴露在app以外   Intent.getData()

修改

在定位Android Nougat时,不再允许file://个URI。我们应该使用content:// URI。但是,我的应用程序需要打开根目录中的文件。有什么想法吗?

31 个答案:

答案 0 :(得分:1104)

如果您的Synchronize(),那么我们必须使用targetSdkVersion >= 24类来授予对特定文件或文件夹的访问权限,以使其可供其他应用访问。我们创建自己的继承FileProvider的类,以确保我们的FileProvider不会与导入的依赖项中声明的FileProviders冲突,如here所述。

使用FileProvider URI替换file:// URI的步骤:

  • 添加一个扩展content://

    的班级
    FileProvider
  • public class GenericFileProvider extends FileProvider {} 标记下的<provider>中添加FileProvider AndroidManifest.xml标记。为<application>属性指定唯一权限以避免冲突,导入的依赖项可能指定android:authorities和其他常用权限。

${applicationId}.provider
  • 然后在<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" ... <application ... <provider android:name=".GenericFileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths"/> </provider> </application> </manifest> 文件夹中创建provider_paths.xml文件。如果文件夹不存在,则可能需要创建文件夹。该文件的内容如下所示。它描述了我们希望在根文件夹res/xml上共享对外部存储的访问权限,名称为 external_files
(path=".")
  • 最后一步是在

    中更改下面的代码行
    <?xml version="1.0" encoding="utf-8"?>
    <paths xmlns:android="http://schemas.android.com/apk/res/android">
        <external-path name="external_files" path="."/>
    </paths>
    

    Uri photoURI = Uri.fromFile(createImageFile());
    
  • 编辑:如果您正在使用意图使系统打开您的文件,则可能需要添加以下代码行:

    Uri photoURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".my.package.name.provider", createImageFile());
    

请参阅,完整的代码和解决方案已经解释here.

答案 1 :(得分:274)

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

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
Application.onCreate()中的

。这样,VM就会忽略文件URI曝光。

方式

builder.detectFileUriExposure()

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

我遇到了一个问题,即如果我使用content:// URI发送内容,某些应用就无法理解。并且不允许降级target SDK版本。在这种情况下,我的解决方案很有用。

<强>更新

如评论中所述,StrictMode是诊断工具,不应该用于此问题。当我在一年前发布此答案时,许多应用程序只能接收文件uris。当我尝试向他们发送FileProvider uri时,它们就崩溃了。这在大多数应用程序中已得到修复,因此我们应该使用FileProvider解决方案。

答案 2 :(得分:143)

如果您的应用面向API 24+,并且您仍然需要/需要使用file:// intents,则可以使用hacky方式禁用运行时检查:

<?php
// GET JSON CONTENT FROM JS api.php
$jsonStr = file_get_contents("php://input"); //read the HTTP body.
$json = json_decode($jsonStr);
?>

方法if(Build.VERSION.SDK_INT>=24){ try{ Method m = StrictMode.class.getMethod("disableDeathOnFileUriExposure"); m.invoke(null); }catch(Exception e){ e.printStackTrace(); } } 被隐藏并记录为:

StrictMode.disableDeathOnFileUriExposure

问题是我的应用程序并不蹩脚,而是不希望因使用内容而瘫痪://那些许多应用程序无法理解的意图。例如,使用content:// scheme打开mp3文件比在file:// scheme上打开相同的应用程序要少得多。我不想通过限制应用程序的功能来支付Google的设计错误。

谷歌希望开发人员使用内容方案,但系统还没有为此做好准备,多年来,应用程序使用文件而不是“内容”,文件可以编辑和保存回来,而文件服务通过内容方案不能是(他们可以吗?)。

答案 3 :(得分:124)

如果targetSdkVersion高于 24 ,则FileProvider用于授予访问权限。

创建xml文件(路径:res \ xml) provider_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>


AndroidManifest.xml

中添加提供商
    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="${applicationId}.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_paths"/>
    </provider>

如果您使用 androidx ,则FileProvider路径应为:

 android:name="androidx.core.content.FileProvider"

替换

Uri uri = Uri.fromFile(fileImagePath);

Uri uri = FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider",fileImagePath);

修改:当您将URI包含在Intent时,请确保添加以下行:

intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

你很高兴。希望它有所帮助。

答案 4 :(得分:80)

如果targetSdkVersion为24或更高,you can not use file: Uri values in Intents on Android 7.0+ devices

您的选择是:

  1. targetSdkVersion放到23或更低,或

  2. 将您的内容放在内部存储空间上,然后use FileProvider将其内容有选择地提供给其他应用

  3. 例如:

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

    (来自this sample project

答案 5 :(得分:44)

首先,您需要向AndroidManifest添加提供程序

  <application
    ...>
    <activity>
    .... 
    </activity>
    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.your.package.fileProvider"
        android:grantUriPermissions="true"
        android:exported="false">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>
  </application>

现在在xml资源文件夹中创建一个文件(如果使用android studio,你可以在突出显示file_paths后点击Alt + Enter并选择创建一个xml资源选项)

接下来在file_paths文件中输入

<?xml version="1.0" encoding="utf-8"?>
<paths>
  <external-path path="Android/data/com.your.package/" name="files_root" />
  <external-path path="." name="external_storage_root" />
</paths>

此示例适用于外部路径,您可以参考here以获取更多选项。 这将允许您共享该文件夹及其子文件夹中的文件。

现在剩下的就是按如下方式创建意图:

    MimeTypeMap mime = MimeTypeMap.getSingleton();
    String ext = newFile.getName().substring(newFile.getName().lastIndexOf(".") + 1);
    String type = mime.getMimeTypeFromExtension(ext);
    try {
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_VIEW);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            Uri contentUri = FileProvider.getUriForFile(getContext(), "com.your.package.fileProvider", newFile);
            intent.setDataAndType(contentUri, type);
        } else {
            intent.setDataAndType(Uri.fromFile(newFile), type);
        }
        startActivityForResult(intent, ACTIVITY_VIEW_ATTACHMENT);
    } catch (ActivityNotFoundException anfe) {
        Toast.makeText(getContext(), "No activity found to open this attachment.", Toast.LENGTH_LONG).show();
    }

编辑:我在file_paths中添加了sd卡的根文件夹。我已经测试了这段代码,但确实有用。

答案 6 :(得分:25)

@palash k答案是正确的,适用于内部存储文件,但在我的情况下我也想从外部存储打开文件,我的应用程序崩溃时从外部存储打开文件,如SD卡和USB,但我设法解决通过从接受的答案修改 provider_paths.xml 来解决问题

更改 provider_paths.xml ,如下所示

<?xml version="1.0" encoding="utf-8"?>
 <paths xmlns:android="http://schemas.android.com/apk/res/android">

<external-path path="Android/data/${applicationId}/" name="files_root" />

<root-path
    name="root"
    path="/" />

</paths>

和java类(没有改变,因为接受的答案只是一个小编辑)

Uri uri=FileProvider.getUriForFile(getActivity(), BuildConfig.APPLICATION_ID+".provider", File)

这有助于我修复来自外部存储的文件崩溃,希望这会帮助一些人遇到与我相同的问题 :)

答案 7 :(得分:20)

只需将以下代码粘贴到activity onCreate()

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

它将忽略URI暴露

答案 8 :(得分:19)

使用fileProvider是可行的方法。 但是你可以使用这个简单的解决方法:

  

警告:它将在下一个Android版本中修复 -   https://issuetracker.google.com/issues/37122890#comment4

取代:

startActivity(intent);

通过

startActivity(Intent.createChooser(intent, "Your title"));

答案 9 :(得分:18)

我的解决方案是将文件路径“ Uri.parse”作为字符串,而不是使用Uri.fromFile()。

String storage = Environment.getExternalStorageDirectory().toString() + "/test.txt";
File file = new File(storage);
Uri uri;
if (Build.VERSION.SDK_INT < 24) {
    uri = Uri.fromFile(file);
} else {
    uri = Uri.parse(file.getPath()); // My work-around for new SDKs, causes ActivityNotFoundException in API 10.
}
Intent viewFile = new Intent(Intent.ACTION_VIEW);
viewFile.setDataAndType(uri, "text/plain");
startActivity(viewFile);

似乎fromFile()使用了文件指针,我认为当内存地址公开给所有应用程序时,它可能是不安全的。但是文件路径字符串从不伤害任何人,因此它可以工作而不会抛出FileUriExposedException。

在API级别9到27上测试!成功打开文本文件以在另一个应用程序中进行编辑。 完全不需要FileProvider,也不需要Android支持库。

答案 10 :(得分:14)

只需将以下代码粘贴到活动onCreate()中。

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

它将忽略URI暴露。

快乐编码:-)

答案 11 :(得分:12)

我使用了Palash上​​面给出的答案,但它有点不完整,我必须提供这样的许可

Intent intent = new Intent(Intent.ACTION_VIEW);
    Uri uri;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        uri = FileProvider.getUriForFile(this, getPackageName() + ".provider", new File(path));

        List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        for (ResolveInfo resolveInfo : resInfoList) {
            String packageName = resolveInfo.activityInfo.packageName;
            grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
        }
    }else {
        uri = Uri.fromFile(new File(path));
    }

    intent.setDataAndType(uri, "application/vnd.android.package-archive");

    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

    startActivity(intent);

答案 12 :(得分:4)

要从服务器下载pdf,请在服务类中添加以下代码。希望这对您有帮助。

File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fileName + ".pdf");
    intent = new Intent(Intent.ACTION_VIEW);
    //Log.e("pathOpen", file.getPath());

    Uri contentUri;
    contentUri = Uri.fromFile(file);
    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);

    if (Build.VERSION.SDK_INT >= 24) {

        Uri apkURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", file);
        intent.setDataAndType(apkURI, "application/pdf");
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

    } else {

        intent.setDataAndType(contentUri, "application/pdf");
    }

是的,不要忘记在清单中添加权限和提供程序。

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

<application

<provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="${applicationId}.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_paths" />
    </provider>

</application>

答案 13 :(得分:4)

我花了将近一天的时间来弄清楚为什么我会得到这个例外。经过大量的努力,此配置非常有效( Kotlin ):

AndroidManifest.xml

<provider
  android:name="androidx.core.content.FileProvider"
  android:authorities="com.lomza.moviesroom.fileprovider"
  android:exported="false"
  android:grantUriPermissions="true">
  <meta-data
    android:name="android.support.FILE_PROVIDER_PATHS"
    android:resource="@xml/file_paths" />
</provider>

file_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths>
  <files-path name="movies_csv_files" path="."/>
</paths>

意图本身

fun goToFileIntent(context: Context, file: File): Intent {
    val intent = Intent(Intent.ACTION_VIEW)
    val contentUri = FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", file)
    val mimeType = context.contentResolver.getType(contentUri)
    intent.setDataAndType(contentUri, mimeType)
    intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION

    return intent
}

我解释了整个过程here

答案 14 :(得分:3)

我不知道为什么,我所做的一切都与Pkosta(https://stackoverflow.com/a/38858040)完全相同,但是不断出错:

java.lang.SecurityException: Permission Denial: opening provider redacted from ProcessRecord{redacted} (redacted) that is not exported from uid redacted

我在这个问题上浪费了时间。罪魁祸首?科特林。

val playIntent = Intent(Intent.ACTION_VIEW, uri)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)

intent实际上是在设置getIntent().addFlags而不是对我新声明的playIntent进行操作。

答案 15 :(得分:2)

将这两行添加到onCreate

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

共享方式

File dir = new File(Environment.getExternalStorageDirectory(), "ColorStory");
File imgFile = new File(dir, "0.png");
Intent sendIntent = new Intent(Intent.ACTION_VIEW);
sendIntent.setType("image/*");
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + imgFile));
sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(sendIntent, "Share images..."));

答案 16 :(得分:2)

As of Android N, in order to work around this issue, you need to use the FileProvider API

这里有3个主要步骤,如下所述

步骤1:清单输入

<manifest ...>
    <application ...>
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="${applicationId}.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>
    </application>
</manifest>

步骤2:创建XML文件res / xml / provider_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>

第3步:更改代码

File file = ...;
Intent install = new Intent(Intent.ACTION_VIEW);
install.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
// Old Approach
    install.setDataAndType(Uri.fromFile(file), mimeType);
// End Old approach
// New Approach
    Uri apkURI = FileProvider.getUriForFile(
                             context, 
                             context.getApplicationContext()
                             .getPackageName() + ".provider", file);
    install.setDataAndType(apkURI, mimeType);
    install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// End New Approach
    context.startActivity(install);

答案 17 :(得分:1)

我知道这是一个非常老的问题,但这是供将来的观众使用的答案。因此,我遇到了类似的问题,经过研究,我找到了这种方法的替代方法。

您的意图在这里 例如:从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),在我看来,它都可以正常工作

答案 18 :(得分:0)

https://developer.android.com/reference/androidx/core/content/FileProvider

此链接提供了针对所有情况的所有解决方案, 请确保通过Intent阅读许可授予部分,并按照要求进行打包。

应对api <24使用以下链接 https://developer.android.com/reference/android/support/v4/content/FileProvider.html

了解有关异常的更多信息 访问此链接 https://developer.android.com/reference/android/os/FileUriExposedException

答案 19 :(得分:0)

如果 android 版本 > 24,我刚刚完成了以下操作

File fl = new File(url);
    Uri uri = Uri.fromFile(fl);
    Intent intent = new Intent(Intent.ACTION_VIEW);
    if (android.os.Build.VERSION.SDK_INT>=24)
    {
        Context context = getApplicationContext();
        uri = FileProvider.getUriForFile(
                context,
                context.getApplicationContext()
                        .getPackageName() + ".provider", fl);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    }
    intent.setDataAndType(uri, mimetype);
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivity(intent);

点击此链接然后https://medium.com/@ali.muzaffar/what-is-android-os-fileuriexposedexception-and-what-you-can-do-about-it-70b9eb17c6d0#.54odzsnk4

答案 20 :(得分:0)

我想从应用程序的范围存储中共享图像,这就是我收到此异常的地方。搜索了几个小时,最后,我找到了 this blog

它有点长,所以我在这里分享要点,但我会建议你通读一遍。

最重要的是,您不能从应用的范围存储中共享任何内容。同样在 Android 12 中,意图选择器底部对话框显示您正在共享的图像的预览,顺便说一下,这非常酷,但它无法从范围存储 URI 加载预览。

解决方案是在缓存目录中创建您“打算”共享的文件的副本。

val cachePath = File(externalCacheDir, "my_images/")
cachePath.mkdirs()
val bitmap = loadImageFromStorage(currentQuote.bookId)
val file = File(cachePath, "cache.png")
val fileOutputStream: FileOutputStream
try {
    fileOutputStream = FileOutputStream(file)
    bitmap?.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream)
    fileOutputStream.flush()
    fileOutputStream.close()
} catch (e: FileNotFoundException) {
    e.printStackTrace()
} catch (e: IOException) {
    e.printStackTrace()
}
val cacheImageUri: Uri = FileProvider.getUriForFile(this, applicationContext.packageName + ".provider", file)
            
val intent = Intent(Intent.ACTION_SEND).apply {
    clipData = ClipData.newRawUri(null, cacheImageUri)
    putExtra(Intent.EXTRA_STREAM, cacheImageUri)
    type = "image/ *"
    addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
startActivity(Intent.createChooser(intent, null))
            

这就是我从作用域存储加载文件的方式

fun Context.loadImageFromStorage(path: String): Bitmap? {
    try {
        val file = getFile(path)
        val bitmap = BitmapFactory.decodeStream(FileInputStream(file))
        return bitmap
    } catch (e: Exception) {
        e.printStackTrace()

        //Returning file from public storage in case the file is stored in public storage 
        return BitmapFactory.decodeStream(FileInputStream(File(path)))
    }
    
    return null
}


fun Context.getFile(path: String): File? {
    val cw = ContextWrapper(this)
    val directory = cw.getDir("image_dir", Context.MODE_PRIVATE)
    if (!directory.exists())
        directory.mkdir()
    try {
        val fileName = directory.absolutePath + "/" + path.split("/").last()
        return File(fileName)
    } catch (e: Exception) {
        e.printStackTrace()
    }
    
    return null
}

最后,不要忘记更新您的 provider_paths.xml 文件

<external-cache-path name="external_cache" path="." />

<external-cache-path name="external_files" path="my_images/"/>

答案 21 :(得分:0)

这是我的解决方法:

Manifest.xml

<application
            android:name=".main.MainApp"
            android:allowBackup="true"
            android:icon="@drawable/ic_app"
            android:label="@string/application_name"
            android:logo="@drawable/ic_app_logo"
            android:theme="@style/MainAppBaseTheme">

        <provider
                android:name="androidx.core.content.FileProvider"
                android:authorities="${applicationId}.provider"
                android:exported="false"
                android:grantUriPermissions="true">
            <meta-data
                    android:name="android.support.FILE_PROVIDER_PATHS"
                    android:resource="@xml/provider_paths"/>
        </provider>

res / xml / provider_paths.xml

   <?xml version="1.0" encoding="utf-8"?>
    <paths xmlns:android="http://schemas.android.com/apk/res/android">
        <external-path name="external_files" path="."/>
    </paths>

在我的片段中,我有下一个代码:

 Uri myPhotoFileUri = FileProvider.getUriForFile(getActivity(), getActivity().getApplicationContext().getPackageName() + ".provider", myPhotoFile);               
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, myPhotoFileUri);

这就是您所需要的。

不需要创建

public class GenericFileProvider extends FileProvider {}

我在Android 5.0、6.0和Android 9.0上进行了测试,并且成功完成了工作。

答案 22 :(得分:0)

只需将以下代码粘贴到活动onCreate()

  

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

它将忽略URI暴露

答案 23 :(得分:0)

我采用了这种方法,因此imageuri路径很容易获得内容。

enter code here
public Uri getImageUri(Context context, Bitmap inImage)
{
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    inImage.compress(Bitmap.CompressFormat.PNG, 100, bytes);
    String path = MediaStore.Images.Media.insertImage(context.getContentResolver(), 
    inImage, "Title", null);
    return Uri.parse(path);
}

答案 24 :(得分:-1)

只需让它忽略URI暴露... 创建后将其添加

| col                                                                | string_agg |
| ------------------------------------------------------------------ | ---------- |
| {"itemID":["15671\/3#1#4202","15686\/4#1#4203","15687\/1#1#4203"]} | 4202,4203  |
| {"itemID":["15671\/3#1#4204","15687\/1#1#4204"]}                   | 4204       |
| {"itemID":["10966\/4#1#4201"]}                                     | 4201       |

答案 25 :(得分:-1)

尝试此解决方案

将这些权限放入清单

 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
 <uses-permission android:name="android.permission.CAMERA" />

意图捕捉图像

Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
                    startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
                }

在活动结果中获取捕获的图像

@Override
            protected void onActivityResult(int requestCode, int resultCode, Intent data) {
                super.onActivityResult(requestCode, resultCode, data);
                if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
                    Bundle extras = data.getExtras();
                    Bitmap imageBitmap = (Bitmap) extras.get("data");
                    // CALL THIS METHOD TO GET THE URI FROM THE BITMAP
                    Uri tempUri = getImageUri(getApplicationContext(), imageBitmap);
                    //DO SOMETHING WITH URI
                }
            } 

获取图像URI的方法

public Uri getImageUri(Context inContext, Bitmap inImage) {
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        inImage.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
        String path = MediaStore.Images.Media.insertImage(inContext.getContentResolver(), inImage, "Title", null);
        return Uri.parse(path);
    }

答案 26 :(得分:-1)

@Pkosta的答案是做到这一点的一种方法。

除了使用FileProvider,您还可以将文件插入MediaStore(尤其是图像和视频文件),因为MediaStore中的文件可供每个应用访问:

  

MediaStore主要针对视频,音频和图像MIME类型,但是从Android 3.0(API级别11)开始,它还可以存储非媒体类型(有关更多信息,请参见MediaStore.Files)。可以使用scanFile()将文件插入MediaStore,然后将适合共享的content://样式Uri传递到提供的onScanCompleted()回调中。请注意,将内容添加到系统MediaStore后,设备上的任何应用程序都可以访问该内容。

例如,您可以像这样将视频文件插入MediaStore:

ContentValues values = new ContentValues();
values.put(MediaStore.Video.Media.DATA, videoFilePath);
Uri contentUri = context.getContentResolver().insert(
      MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values);

contentUri类似于content://media/external/video/media/183473,可以直接传递给Intent.putExtra

intent.setType("video/*");
intent.putExtra(Intent.EXTRA_STREAM, contentUri);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
activity.startActivity(intent);

这对我有用,并节省了使用FileProvider的麻烦。

答案 27 :(得分:-1)

Xamarin.Android

注意:即使在路径下的 xml 文件夹下也无法解析路径 xml / provider_paths.xml (.axml)(.axml) 资源(也许可以将其放置在现有的位置,例如 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);

答案 28 :(得分:-1)

这有效

 val uri = if (Build.VERSION.SDK_INT < 24) Uri.fromFile(file) else Uri.parse(file.path)
                val shareIntent = Intent().apply {
                    action = Intent.ACTION_SEND
                    type = "application/pdf"
                    putExtra(Intent.EXTRA_STREAM, uri)
                    putExtra(
                        Intent.EXTRA_SUBJECT,
                        "Purchase Bill..."
                    )
                    putExtra(
                        Intent.EXTRA_TEXT,
                        "Sharing Bill purchase items..."
                    )
                }
                startActivity(Intent.createChooser(shareIntent, "Share Via"))

答案 29 :(得分:-1)

https://stackoverflow.com/a/38858040/395097这个答案已经完成了。

这个答案适用于 - 您已经有一个针对24岁以下的应用,现在您正在升级到targetSDKVersion&gt; = 24。

在Android N中,仅更改了暴露给第三方应用的文件uri。 (不是我们之前使用它的方式)。因此,只使用第三方应用程序(我的情况下为相机)

仅更改您共享路径的位置

在我们的应用程序中,我们将uri发送到相机应用程序,在该位置我们期待相机应用程序存储捕获的图像。

  1. 对于android N,我们生成新的Content://基于uri的url指向 文件。
  2. 我们生成相同的基于File api的路径(使用旧方法)。
  3. 现在我们有2个不同的uri用于同一个文件。 #1与相机应用共享。如果相机意图成功,我们可以从#2访问图像。

    希望这有帮助。

答案 30 :(得分:-2)

在我的情况下,我将SetDataAndType替换为SetData摆脱了异常。