android.os。FileUriExposedException:文件:/ / /存储/模拟/ 0 /测试。txt通过Intent.getData()暴露于app之外

时间:2022-12-03 15:32:50

The app is crashing when I'm trying to open a file. It works below Android Nougat, but on Android Nougat it crashes. It only crashes when I try to open a file from the SD card, not from the system partition. Some permission problem?

当我试图打开文件时,应用程序崩溃了。它在Android Nougat下工作,但是在Android Nougat上就崩溃了。当我试图从SD卡打开文件时,它只会崩溃,而不是从系统分区。一些权限问题?

Sample code:

示例代码:

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

Log:

日志:

android.os.FileUriExposedException: file:///storage/emulated/0/test.txt exposed beyond app through Intent.getData()

android.os。FileUriExposedException:文件:/ / /存储/模拟/ 0 /测试。txt通过Intent.getData()暴露于app之外

Edit:

编辑:

When targeting Android Nougat, file:// URIs are not allowed anymore. We should use content:// URIs instead. However, my app needs to open files in root directories. Any ideas?

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

11 个解决方案

#1


794  

If your targetSdkVersion >= 24, then we have to use FileProvider class to give access to the particular file or folder to make them accessible for other apps. We create our own class inheriting FileProvider in order to make sure our FileProvider doesn't conflict with FileProviders declared in imported dependencies as described here.

如果您的targetSdkVersion >= 24,那么我们必须使用FileProvider类来访问特定的文件或文件夹,以便其他应用程序可以访问它们。我们创建自己的类来继承FileProvider,以确保我们的FileProvider不会与这里描述的导入依赖项中声明的文件提供程序发生冲突。

Steps to replace file:// URI with content:// URI:

将文件:// URI替换为内容:// URI的步骤:

  • Add a class extending FileProvider

    添加扩展文件提供程序的类

    public class GenericFileProvider extends FileProvider {}
    
  • Add a FileProvider tag in AndroidManifest.xml under tag. Specify a unique authority for the android:authorities attribute to avoid conflicts, imported dependencies might specify ${applicationId}.provider and other commonly used authorities.

    在AndroidManifest中添加FileProvider标记。xml标签下。为android指定一个惟一的权限:权限属性以避免冲突,导入的依赖项可能指定${applicationId}。提供者和其他常用的权限。

<?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}.my.package.name.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>
    </application>
</manifest>
  • Then create a provider_paths.xml file in res/xml folder. Folder may be needed to created if it doesn't exist. The content of the file is shown below. It describes that we would like to share access to the External Storage at root folder (path=".") with the name external_files.
  • 然后创建一个provider_paths。xml文件在res/xml文件夹中。如果文件夹不存在,可能需要创建它。文件的内容如下所示。它描述了我们希望在根文件夹(path=“.”)中与名称external_files共享对外部存储的访问。
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>
  • The final step is to change the line of code below in

    最后一步是更改下面的代码行

    Uri photoURI = Uri.fromFile(createImageFile());
    

    to

    Uri photoURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".my.package.name.provider", createImageFile());
    
  • Edit: If you're using an intent to make the system open your file, you may need to add the following line of code:

    编辑:如果您想让系统打开您的文件,您可能需要添加以下代码行:

    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    

Please refer, full code and solution has been explained here.

请参考完整的代码和解决方案。

#2


200  

Besides the solution using the FileProvider, there is another way to work around this. Simply put

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

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

in Application.onCreate(). In this way the VM ignores the file URI exposure.

在Application.onCreate()。这样,VM就会忽略文件URI公开。

Method

方法

builder.detectFileUriExposure()

enables the file exposure check, which is also the default behavior if we don't setup a VmPolicy.

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

I encountered a problem that if I use a content:// URI to send something, some apps just can't understand it. And downgrading the target SDK version is not allowed. In this case my solution is useful.

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

Update:

更新:

As mentioned in the comment, StrictMode is diagnostic tool, and is not supposed to be used for this problem. When I posted this answer a year ago, many apps can only receive File uris. They just crash when I tried to send a FileProvider uri to them. This is fixed in most apps now, so we should go with the FileProvider solution.

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

#3


100  

If your app targets API 24+, and you still want/need to use file:// intents, you can use hacky way to disable the runtime check:

如果你的应用目标是API 24+,而你仍然想要/需要使用文件://意图,你可以使用hacky方法来禁用运行时检查:

if(Build.VERSION.SDK_INT>=24){
   try{
      Method m = StrictMode.class.getMethod("disableDeathOnFileUriExposure");
      m.invoke(null);
   }catch(Exception e){
      e.printStackTrace();
   }
}

Method StrictMode.disableDeathOnFileUriExposure is hidden and documented as:

StrictMode方法。disableDeathOnFileUriExposure被隐藏并记录为:

/**
* Used by lame internal apps that haven't done the hard work to get
* themselves off file:// Uris yet.
*/

Problem is that my app is not lame, but rather doesn't want to be crippled by using content:// intents which are not understood by many apps out there. For example, opening mp3 file with content:// scheme offers much fewer apps than when opening same over file:// scheme. I don't want to pay for Google's design faults by limiting my app's functionality.

问题是,我的应用程序并不是蹩脚的,而是不希望通过使用内容来削弱它的功能:// intents,很多应用程序都不了解它。例如,用content:// scheme打开mp3文件比打开file:// / scheme提供的应用程序要少得多。我不想通过限制我的应用程序的功能来为谷歌的设计错误买单。

Google wants developers to use content scheme, but the system is not prepared for this, for years apps were made to use Files not "content", files can be edited and saved back, while files served over content scheme can't be (can they?).

谷歌希望开发人员使用内容方案,但系统并没有为此做好准备,多年来,应用程序都是使用文件而不是“内容”,文件可以被编辑和保存,而内容方案提供的文件则不能(对吗?)

#4


69  

If your targetSdkVersion is 24 or higher, you can not use file: Uri values in Intents on Android 7.0+ devices.

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

Your choices are:

你的选择是:

  1. Drop your targetSdkVersion to 23 or lower, or

    将targetSdkVersion降低到23或更低,或

  2. Put your content on internal storage, then use FileProvider to make it available selectively to other apps

    将你的内容放在内部存储中,然后使用FileProvider让它有选择地提供给其他应用程序

For example:

例如:

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

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

(from this sample project)

(从这个示例项目)

#5


67  

If targetSdkVersion is higher than 24, then FileProvider is used to grant access.

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

Create an xml file(Path: res\xml) provider_paths.xml

创建一个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>


Add a Provider in AndroidManifest.xml

在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>

and replace

和替换

Uri uri = Uri.fromFile(fileImagePath);

to

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

and you are good to go. Hope it helps.

你可以走了。希望它可以帮助。

#6


40  

First you need to add a provider to your AndroidManifest

首先,您需要向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>

now create a file in xml resource folder (if using android studio you can hit Alt + Enter after highlighting file_paths and select create a xml resource option)

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

Next in the file_paths file enter

接下来在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>

This example is for external-path you can refere here for more options. This will allow you to share files which are in that folder and its sub-folder.

这个例子是关于外部路径的,你可以在这里参考更多的选项。这将允许您共享文件夹及其子文件夹中的文件。

Now all that's left is to create the intent as follows:

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

    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();
    }

EDIT: I added the root folder of the sd card in the file_paths. I have tested this code and it does work.

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

#7


23  

@palash k answer is correct and worked for internal storage files, but in my case I want to open files from external storage also, my app crashed when open file from external storage like sdcard and usb, but I manage to solve the issue by modifying provider_paths.xml from the accepted answer

@palash k的回答是正确的,适用于内部存储文件,但在我的情况下,我也想从外部存储打开文件,我的应用程序在从外部存储(如sdcard和usb)打开文件时崩溃,但我通过修改provider_paths设法解决了这个问题。从已接受的答案中获得的xml

change the provider_paths.xml like below

改变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>

and in java class(No change as the accepted answer just a small edit)

在java类中(没有更改为可接受的答案,只需进行少量编辑)

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

This help me to fix the crash for files from external storages, Hope this will help some one having same issue as mine :)

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

#8


15  

Using the fileProvider is the way to go. But you can use this simple workaround:

使用fileProvider是正确的方法。但是你可以用这个简单的方法:

WARNING: It will be fixed in next Android release - https://issuetracker.google.com/issues/37122890#comment4

警告:它将在下一个Android版本中被修正- https://addreetracker.google.com/addrees/37122890 #comment4。

replace:

替换:

startActivity(intent);

by

通过

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

#9


10  

I used Palash's answer given above but it was somewhat incomplete, I had to provide permission like this

我用了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);

#10


0  

Just paste the below code in activity onCreate()

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

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

It will ignore URI exposure

它将忽略URI公开

#11


-1  

https://*.com/a/38858040/395097 this answer is complete.

这个答案是完整的。

This answer is for - you already have an app which was targeting below 24, and now you are upgrading to targetSDKVersion >= 24.

这个答案是-你已经有了一个目标在24以下的应用,现在你升级到targetSDKVersion >= 24。

In Android N, only the file uri exposed to 3rd party app is changed. (Not the way we were using it before). So change only the places where you are sharing the path with 3rd party app (Camera in my case)

在Android N中,只有公开给第三方应用程序的文件uri被更改。(不是我们以前用的方式)。所以只改变你与第三方应用共享路径的地方(我用的是摄像头)

In our app we were sending uri to Camera app, in that location we are expecting the camera app to store the captured image.

在我们的应用程序中,我们将uri发送到摄像头应用程序,在那个位置,我们期望摄像头应用程序存储捕获的图像。

  1. For android N, we generate new Content:// uri based url pointing to file.
  2. 对于android N,我们生成新的内容://基于uri的指向文件的url。
  3. We generate usual File api based path for the same (using older method).
  4. 我们为相同的(使用旧的方法)生成基于文件api的路径。

Now we have 2 different uri for same file. #1 is shared with Camera app. If the camera intent is success, we can access the image from #2.

现在我们对同一个文件有两个不同的uri。1与Camera app共享。如果Camera intent成功,我们可以从2*问图片。

Hope this helps.

希望这个有帮助。

#1


794  

If your targetSdkVersion >= 24, then we have to use FileProvider class to give access to the particular file or folder to make them accessible for other apps. We create our own class inheriting FileProvider in order to make sure our FileProvider doesn't conflict with FileProviders declared in imported dependencies as described here.

如果您的targetSdkVersion >= 24,那么我们必须使用FileProvider类来访问特定的文件或文件夹,以便其他应用程序可以访问它们。我们创建自己的类来继承FileProvider,以确保我们的FileProvider不会与这里描述的导入依赖项中声明的文件提供程序发生冲突。

Steps to replace file:// URI with content:// URI:

将文件:// URI替换为内容:// URI的步骤:

  • Add a class extending FileProvider

    添加扩展文件提供程序的类

    public class GenericFileProvider extends FileProvider {}
    
  • Add a FileProvider tag in AndroidManifest.xml under tag. Specify a unique authority for the android:authorities attribute to avoid conflicts, imported dependencies might specify ${applicationId}.provider and other commonly used authorities.

    在AndroidManifest中添加FileProvider标记。xml标签下。为android指定一个惟一的权限:权限属性以避免冲突,导入的依赖项可能指定${applicationId}。提供者和其他常用的权限。

<?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}.my.package.name.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>
    </application>
</manifest>
  • Then create a provider_paths.xml file in res/xml folder. Folder may be needed to created if it doesn't exist. The content of the file is shown below. It describes that we would like to share access to the External Storage at root folder (path=".") with the name external_files.
  • 然后创建一个provider_paths。xml文件在res/xml文件夹中。如果文件夹不存在,可能需要创建它。文件的内容如下所示。它描述了我们希望在根文件夹(path=“.”)中与名称external_files共享对外部存储的访问。
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>
  • The final step is to change the line of code below in

    最后一步是更改下面的代码行

    Uri photoURI = Uri.fromFile(createImageFile());
    

    to

    Uri photoURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".my.package.name.provider", createImageFile());
    
  • Edit: If you're using an intent to make the system open your file, you may need to add the following line of code:

    编辑:如果您想让系统打开您的文件,您可能需要添加以下代码行:

    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    

Please refer, full code and solution has been explained here.

请参考完整的代码和解决方案。

#2


200  

Besides the solution using the FileProvider, there is another way to work around this. Simply put

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

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

in Application.onCreate(). In this way the VM ignores the file URI exposure.

在Application.onCreate()。这样,VM就会忽略文件URI公开。

Method

方法

builder.detectFileUriExposure()

enables the file exposure check, which is also the default behavior if we don't setup a VmPolicy.

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

I encountered a problem that if I use a content:// URI to send something, some apps just can't understand it. And downgrading the target SDK version is not allowed. In this case my solution is useful.

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

Update:

更新:

As mentioned in the comment, StrictMode is diagnostic tool, and is not supposed to be used for this problem. When I posted this answer a year ago, many apps can only receive File uris. They just crash when I tried to send a FileProvider uri to them. This is fixed in most apps now, so we should go with the FileProvider solution.

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

#3


100  

If your app targets API 24+, and you still want/need to use file:// intents, you can use hacky way to disable the runtime check:

如果你的应用目标是API 24+,而你仍然想要/需要使用文件://意图,你可以使用hacky方法来禁用运行时检查:

if(Build.VERSION.SDK_INT>=24){
   try{
      Method m = StrictMode.class.getMethod("disableDeathOnFileUriExposure");
      m.invoke(null);
   }catch(Exception e){
      e.printStackTrace();
   }
}

Method StrictMode.disableDeathOnFileUriExposure is hidden and documented as:

StrictMode方法。disableDeathOnFileUriExposure被隐藏并记录为:

/**
* Used by lame internal apps that haven't done the hard work to get
* themselves off file:// Uris yet.
*/

Problem is that my app is not lame, but rather doesn't want to be crippled by using content:// intents which are not understood by many apps out there. For example, opening mp3 file with content:// scheme offers much fewer apps than when opening same over file:// scheme. I don't want to pay for Google's design faults by limiting my app's functionality.

问题是,我的应用程序并不是蹩脚的,而是不希望通过使用内容来削弱它的功能:// intents,很多应用程序都不了解它。例如,用content:// scheme打开mp3文件比打开file:// / scheme提供的应用程序要少得多。我不想通过限制我的应用程序的功能来为谷歌的设计错误买单。

Google wants developers to use content scheme, but the system is not prepared for this, for years apps were made to use Files not "content", files can be edited and saved back, while files served over content scheme can't be (can they?).

谷歌希望开发人员使用内容方案,但系统并没有为此做好准备,多年来,应用程序都是使用文件而不是“内容”,文件可以被编辑和保存,而内容方案提供的文件则不能(对吗?)

#4


69  

If your targetSdkVersion is 24 or higher, you can not use file: Uri values in Intents on Android 7.0+ devices.

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

Your choices are:

你的选择是:

  1. Drop your targetSdkVersion to 23 or lower, or

    将targetSdkVersion降低到23或更低,或

  2. Put your content on internal storage, then use FileProvider to make it available selectively to other apps

    将你的内容放在内部存储中,然后使用FileProvider让它有选择地提供给其他应用程序

For example:

例如:

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

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

(from this sample project)

(从这个示例项目)

#5


67  

If targetSdkVersion is higher than 24, then FileProvider is used to grant access.

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

Create an xml file(Path: res\xml) provider_paths.xml

创建一个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>


Add a Provider in AndroidManifest.xml

在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>

and replace

和替换

Uri uri = Uri.fromFile(fileImagePath);

to

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

and you are good to go. Hope it helps.

你可以走了。希望它可以帮助。

#6


40  

First you need to add a provider to your AndroidManifest

首先,您需要向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>

now create a file in xml resource folder (if using android studio you can hit Alt + Enter after highlighting file_paths and select create a xml resource option)

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

Next in the file_paths file enter

接下来在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>

This example is for external-path you can refere here for more options. This will allow you to share files which are in that folder and its sub-folder.

这个例子是关于外部路径的,你可以在这里参考更多的选项。这将允许您共享文件夹及其子文件夹中的文件。

Now all that's left is to create the intent as follows:

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

    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();
    }

EDIT: I added the root folder of the sd card in the file_paths. I have tested this code and it does work.

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

#7


23  

@palash k answer is correct and worked for internal storage files, but in my case I want to open files from external storage also, my app crashed when open file from external storage like sdcard and usb, but I manage to solve the issue by modifying provider_paths.xml from the accepted answer

@palash k的回答是正确的,适用于内部存储文件,但在我的情况下,我也想从外部存储打开文件,我的应用程序在从外部存储(如sdcard和usb)打开文件时崩溃,但我通过修改provider_paths设法解决了这个问题。从已接受的答案中获得的xml

change the provider_paths.xml like below

改变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>

and in java class(No change as the accepted answer just a small edit)

在java类中(没有更改为可接受的答案,只需进行少量编辑)

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

This help me to fix the crash for files from external storages, Hope this will help some one having same issue as mine :)

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

#8


15  

Using the fileProvider is the way to go. But you can use this simple workaround:

使用fileProvider是正确的方法。但是你可以用这个简单的方法:

WARNING: It will be fixed in next Android release - https://issuetracker.google.com/issues/37122890#comment4

警告:它将在下一个Android版本中被修正- https://addreetracker.google.com/addrees/37122890 #comment4。

replace:

替换:

startActivity(intent);

by

通过

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

#9


10  

I used Palash's answer given above but it was somewhat incomplete, I had to provide permission like this

我用了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);

#10


0  

Just paste the below code in activity onCreate()

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

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

It will ignore URI exposure

它将忽略URI公开

#11


-1  

https://*.com/a/38858040/395097 this answer is complete.

这个答案是完整的。

This answer is for - you already have an app which was targeting below 24, and now you are upgrading to targetSDKVersion >= 24.

这个答案是-你已经有了一个目标在24以下的应用,现在你升级到targetSDKVersion >= 24。

In Android N, only the file uri exposed to 3rd party app is changed. (Not the way we were using it before). So change only the places where you are sharing the path with 3rd party app (Camera in my case)

在Android N中,只有公开给第三方应用程序的文件uri被更改。(不是我们以前用的方式)。所以只改变你与第三方应用共享路径的地方(我用的是摄像头)

In our app we were sending uri to Camera app, in that location we are expecting the camera app to store the captured image.

在我们的应用程序中,我们将uri发送到摄像头应用程序,在那个位置,我们期望摄像头应用程序存储捕获的图像。

  1. For android N, we generate new Content:// uri based url pointing to file.
  2. 对于android N,我们生成新的内容://基于uri的指向文件的url。
  3. We generate usual File api based path for the same (using older method).
  4. 我们为相同的(使用旧的方法)生成基于文件api的路径。

Now we have 2 different uri for same file. #1 is shared with Camera app. If the camera intent is success, we can access the image from #2.

现在我们对同一个文件有两个不同的uri。1与Camera app共享。如果Camera intent成功,我们可以从2*问图片。

Hope this helps.

希望这个有帮助。