Tho*_*Vos 682 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
Run Code Online (Sandbox Code Playgroud)
日志:
android.os.FileUriExposedException:file:///storage/emulated/0/test.txt通过Intent.getData()暴露在app之外
编辑:
在定位Android Nougat时,file://不再允许使用URI.我们应该使用content://URI代替.但是,我的应用程序需要打开根目录中的文件.有任何想法吗?
小智 1203
如果您targetSdkVersion >= 24,那么我们必须使用FileProvider类来授予对特定文件或文件夹的访问权限,以使其可供其他应用程序访问.我们创造我们自己的类继承FileProvider,以确保我们的FileProvider不会描述进口依赖宣布FileProviders冲突这里.
file://用content://URI 替换URI的步骤:
添加一个扩展类 FileProvider
public class GenericFileProvider extends FileProvider {}
Run Code Online (Sandbox Code Playgroud)添加FileProvider <provider>在标签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}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
</application>
</manifest>
Run Code Online (Sandbox Code Playgroud)
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>
Run Code Online (Sandbox Code Playgroud)
最后一步是更改下面的代码行
Uri photoURI = Uri.fromFile(createImageFile());
Run Code Online (Sandbox Code Playgroud)
至
Uri photoURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", createImageFile());
Run Code Online (Sandbox Code Playgroud)编辑:如果您正在使用意图使系统打开您的文件,您可能需要添加以下代码行:
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Run Code Online (Sandbox Code Playgroud)hqz*_*zwb 292
除了使用the的解决方案之外FileProvider,还有另一种方法可以解决这个问题.简单的说
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
Run Code Online (Sandbox Code Playgroud)
在Application.onCreate().以这种方式,VM忽略文件URI曝光.
方法
builder.detectFileUriExposure()
Run Code Online (Sandbox Code Playgroud)
启用文件曝光检查,如果我们不设置VmPolicy,这也是默认行为.
我遇到了一个问题,如果我使用一个content:// URI发送的东西,一些应用程序只是无法理解它.并且target SDK不允许降级版本.在这种情况下,我的解决方案很有用
更新:
正如评论中所提到的,StrictMode是诊断工具,不应该用于此问题.当我在一年前发布此答案时,许多应用程序只能接收文件uris.当我尝试向他们发送FileProvider uri时,它们就崩溃了.这在大多数应用程序中已得到修复,因此我们应该使用FileProvider解决方案.
Poi*_*ull 153
如果您的应用面向API 24+,并且您仍然需要/需要使用file:// intents,则可以使用hacky方式禁用运行时检查:
if(Build.VERSION.SDK_INT>=24){
try{
Method m = StrictMode.class.getMethod("disableDeathOnFileUriExposure");
m.invoke(null);
}catch(Exception e){
e.printStackTrace();
}
}
Run Code Online (Sandbox Code Playgroud)
方法StrictMode.disableDeathOnFileUriExposure被隐藏并记录为:
/**
* Used by lame internal apps that haven't done the hard work to get
* themselves off file:// Uris yet.
*/
Run Code Online (Sandbox Code Playgroud)
问题是我的应用程序不是蹩脚的,而是不希望被使用内容瘫痪://那些许多应用程序无法理解的意图.例如,使用content:// scheme打开mp3文件比在file:// scheme上打开相同的应用程序要少得多.我不想通过限制我的应用程序的功能来支付Google的设计错误.
谷歌希望开发人员使用内容方案,但系统并没有为此做好准备,多年来,应用程序使用文件而不是"内容",文件可以编辑和保存回来,而文件通过内容方案提供服务不能(可以他们?).
Pan*_*lan 148
如果targetSdkVersion高于24,则使用FileProvider授予访问权限.
创建一个xml文件(Path: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>
Run Code Online (Sandbox Code Playgroud)
在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>
Run Code Online (Sandbox Code Playgroud)
并替换
android:name="androidx.core.content.FileProvider"
Run Code Online (Sandbox Code Playgroud)
至
Uri uri = Uri.fromFile(fileImagePath);
Run Code Online (Sandbox Code Playgroud)
编辑:当您包含URI时,请Intent确保添加以下行:
Uri uri = FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider",fileImagePath);
Run Code Online (Sandbox Code Playgroud)
你很高兴.希望能帮助到你.
Com*_*are 87
如果您的版本targetSdkVersion是24或更高,则无法在Android 7.0及file: UriIntents更高版本的设备上使用值.
你的选择是:
放下你的targetSdkVersion23或更低,或
将您的内容放在内部存储上,然后使用FileProvider它将其有选择地提供给其他应用程序
例如:
Intent i=new Intent(Intent.ACTION_VIEW, FileProvider.getUriForFile(this, AUTHORITY, f));
i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(i);
Run Code Online (Sandbox Code Playgroud)
(来自这个示例项目)
Kar*_*tel 47
首先,您需要为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>
Run Code Online (Sandbox Code Playgroud)
现在在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>
Run Code Online (Sandbox Code Playgroud)
此示例适用于外部路径,您可以在此处引用更多选项.这将允许您共享该文件夹及其子文件夹中的文件.
现在剩下的就是按如下方式创建意图:
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();
}
Run Code Online (Sandbox Code Playgroud)
编辑:我在file_paths中添加了SD卡的根文件夹.我已经测试了这段代码,但确实有效.
Ram*_*amz 25
@palash k答案是正确的,适用于内部存储文件,但在我的情况下我也想从外部存储打开文件,我的应用程序在从外部存储打开文件时崩溃,如sdcard和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>
Run Code Online (Sandbox Code Playgroud)
并在java类中(没有更改,因为接受的答案只是一个小编辑)
Uri uri=FileProvider.getUriForFile(getActivity(), BuildConfig.APPLICATION_ID+".provider", File)
Run Code Online (Sandbox Code Playgroud)
这有助于我修复来自外部存储的文件的崩溃,希望这将帮助一些人有同样的问题,如我的:)
小智 22
只需在活动onGreate()中粘贴以下代码即可
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); StrictMode.setVmPolicy(builder.build());
Run Code Online (Sandbox Code Playgroud)
它将忽略URI暴露
Cra*_*J36 22
我的解决方案是将文件路径'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);
Run Code Online (Sandbox Code Playgroud)
似乎fromFile()使用了一个文件指针,我认为当内存地址暴露给所有应用程序时,这可能是不安全的.但是文件路径字符串从不会伤害任何人,因此它可以在不抛出FileUriExposedException的情况下工作.
测试API级别9到26!不需要FileProvider,也不需要Android支持库.
Sim*_*mon 18
使用fileProvider是可行的方法.但是你可以使用这个简单的解决方法:
警告:它将在下一个Android版本中修复 - https://issuetracker.google.com/issues/37122890#comment4
更换:
startActivity(intent);
Run Code Online (Sandbox Code Playgroud)
通过
startActivity(Intent.createChooser(intent, "Your title"));
Run Code Online (Sandbox Code Playgroud)
小智 18
只需在活动中粘贴以下代码即可onCreate().
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
Run Code Online (Sandbox Code Playgroud)
它将忽略URI暴露.
快乐的编码:-)
Max*_*Max 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);
Run Code Online (Sandbox Code Playgroud)
只需将以下代码粘贴到活动onCreate()中
StrictMode.VmPolicy.Builder builder =新的StrictMode.VmPolicy.Builder(); StrictMode.setVmPolicy(builder.build());
它将忽略URI暴露
在onCreate中添加这两行
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
Run Code Online (Sandbox Code Playgroud)
共享方式
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..."));
Run Code Online (Sandbox Code Playgroud)
这是我的解决方案:
在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>
Run Code Online (Sandbox Code Playgroud)
在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>
Run Code Online (Sandbox Code Playgroud)
在我的片段中,我有下面的代码:
Uri myPhotoFileUri = FileProvider.getUriForFile(getActivity(), getActivity().getApplicationContext().getPackageName() + ".provider", myPhotoFile);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra(MediaStore.EXTRA_OUTPUT, myPhotoFileUri);
Run Code Online (Sandbox Code Playgroud)
您需要什么?
也没有必要创建
public class GenericFileProvider extends FileProvider {}
Run Code Online (Sandbox Code Playgroud)
我在Android 5.0、6.0和Android 9.0上进行了测试,并且测试成功。
| 归档时间: |
|
| 查看次数: |
377881 次 |
| 最近记录: |