当我试图打开一个文件时,应用程序崩溃了。它可以在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。但是,我的应用程序需要打开根目录下的文件。什么好主意吗?
As of Android N, in order to work around this issue, you need to use the FileProvider API
这里有3个主要步骤,如下所述
第一步:舱单入境
<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);
我不知道为什么,我所做的一切与Pkosta (https://stackoverflow.com/a/38858040)完全相同,但一直得到错误:
Permission Denial:从ProcessRecord{reacted} (reacted)打开提供者,而不是从uid reacted导出
我在这个问题上浪费了好几个小时。罪魁祸首吗?芬兰湾的科特林。
val playIntent = Intent(Intent.ACTION_VIEW, uri)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent实际上是设置getIntent()。addFlags而不是在我新声明的playIntent上操作。
如果你的应用程序目标API 24+,你仍然想要/需要使用file:// intents,你可以使用hack方法禁用运行时检查:
if(Build.VERSION.SDK_INT>=24){
try{
Method m = StrictMode.class.getMethod("disableDeathOnFileUriExposure");
m.invoke(null);
}catch(Exception e){
e.printStackTrace();
}
}
StrictMode方法。disableDeathOnFileUriExposure被隐藏并记录为:
/**
* Used by lame internal apps that haven't done the hard work to get
* themselves off file:// Uris yet.
*/
问题是我的应用程序不是蹩脚的,而是不想被使用内容://意图所削弱,这是许多应用程序无法理解的。例如,以content:// scheme打开mp3文件,提供的应用程序比打开相同的over file:// scheme时少得多。我不想为谷歌的设计缺陷买单,限制我的应用程序的功能。
谷歌希望开发者使用内容方案,但系统并没有为此做好准备,多年来应用程序都是使用文件而不是“内容”,文件可以编辑和保存,而通过内容方案提供的文件则不能(可以吗?)
要从服务器下载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>
@Pkosta的回答是这样做的一种方式。
除了使用FileProvider,你还可以将文件插入到MediaStore中(特别是图像和视频文件),因为MediaStore中的文件可以被每个应用程序访问:
MediaStore主要针对视频、音频和图像MIME类型,但是从Android 3.0 (API级别11)开始,它也可以存储非媒体类型(见MediaStore)。文件获取更多信息)。文件可以使用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的麻烦。