android.os.FileUrieExposedException:file:///storage/emulated/0/test.txt 通过Intent.getData()在应用程序之外公开
当我试图打开文件时,应用程序正在崩溃。它在安卓牛轧糖下工作,但在安卓牛轧糖上崩溃。只有当我试图从SD卡而不是系统分区打开文件时,它才会崩溃。一些许可问题 示例代码:android.os.FileUrieExposedException:file:///storage/emulated/0/test.txt 通过Intent.getData()在应用程序之外公开,android,android-file,android-7.0-nougat,Android,Android File,Android 7.0 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.setFl
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.FileUrieExposedException:
file:///storage/emulated/0/test.txt 暴露在应用程序之外
Intent.getData()
编辑:
当针对Android Nougat时,
file://
URI不再被允许。我们应该使用content://
uri。但是,我的应用程序需要打开根目录中的文件。有什么想法吗?如果您的targetSdkVersion
为24或更高
你的选择是:
targetSdkVersion
降至23或更低,或Intent i=new Intent(Intent.ACTION_VIEW, FileProvider.getUriForFile(this, AUTHORITY, f));
i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(i);
(from)如果您的
targetSdkVersion>=24
,则我们必须使用FileProvider
类授予对特定文件或文件夹的访问权限,以便其他应用程序可以访问这些文件或文件夹。我们创建自己的类继承FileProvider
,以确保我们的FileProvider不会与导入的依赖项中声明的FileProvider发生冲突,如上所述
将文件://
URI替换为内容://
URI的步骤:
- 在
下的
中添加一个FileProviderAndroidManifest.xml
标记首先,您需要向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>
编辑:我在文件路径中添加了sd卡的根文件夹。我已经测试了这段代码,它确实有效。除了使用文件提供程序的解决方案之外,还有另一种解决方法。简而言之
在StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); StrictMode.setVmPolicy(builder.build());
中。通过这种方式,虚拟机忽略文件Application.onCreate()
exposure 方法URI
builder.detectFileUriExposure()
启用文件暴露检查,这也是未设置VmPolicy时的默认行为 我遇到了一个问题,如果我使用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..."));
内容://
发送东西,一些应用程序就无法理解它。并且不允许降级URI
版本。在这种情况下,我的解决方案很有用 更新:目标SDK
如注释中所述,StrictMode是诊断工具,不应用于此问题。当我一年前发布这个答案时,许多应用程序只能接收文件URI。当我试图向它们发送文件提供程序uri时,它们就崩溃了。这在大多数应用程序中都是固定的,所以我们应该使用FileProvider解决方案。我使用了上面给出的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);
@palash k的答案是正确的,适用于内部存储文件,但在我的情况下,我也想从外部存储打开文件,当从SD卡和usb等外部存储打开文件时,我的应用程序崩溃,但我通过修改已接受答案中的provider_path.xml解决了这个问题 更改提供者路径.xml如下所示Intent-Intent=新意图(Intent.ACTION\u视图); Uri; if(Build.VERSION.SDK\u INT>=Build.VERSION\u code.N){ uri=FileProvider.getUriForFile(这是getPackageName()+“.provider”,新文件(路径)); List resinfo List=getPackageManager().queryInputActivities(intent,PackageManager.MATCH_DEFAULT_仅限); 对于(ResolveInfo ResolveInfo:ResInfo列表){ 字符串packageName=resolveInfo.activityInfo.packageName; grantUriPermission(packageName、uri、Intent.FLAG_GRANT_uri_PERMISSION | Intent.FLAG_GRANT_READ_uri_PERMISSION); } }否则{ uri=uri.fromFile(新文件(路径)); } setDataAndType(uri,“application/vnd.android.package归档”); intent.setFlags(intent.FLAG\u活动\u新任务); 星触觉(意向);
这有助于我修复外部存储文件的崩溃,希望这能帮助一些和我有相同问题的人<?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>
:)使用文件提供程序是一种方法。 但您可以使用以下简单的解决方法: 警告:它将在下一个Android版本中修复- 替换:
借startActivity(intent);
如果您的应用程序以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(); } }
隐藏并记录为:StrictMode.DisableDeathonfileureExposure
问题是,我的应用程序并不是跛脚的,而是不想因为使用content://意图而瘫痪,而这一点在很多应用程序中是不理解的。例如,使用content://scheme打开mp3文件提供的应用程序比使用file://scheme打开mp3文件提供的应用程序少得多。我不想通过限制我的应用程序的功能来为谷歌的设计缺陷买单/** * Used by lame internal apps that haven't done the hard work to get * themselves off file:// Uris yet. */
谷歌希望开发者使用content scheme,但该系统并没有为此做好准备,多年来,应用程序使用的是文件而不是“内容”,文件可以编辑和保存,而通过content scheme提供的文件不能(可以吗?)。如果
高于24,则用于授予访问权限 创建一个xml文件(路径:res\xml)provider\u Path.xmltargetSdkVersion
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-path name="external_files" path="."/> </paths>
<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>
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-path name="external_files" path="."/> </paths>
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-path name="external_files" path="."/> </paths>
<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>
和替换<?xml version="1.0" encoding="utf-8"?> <paths> <files-path name="movies_csv_files" path="."/> </paths>
到 编辑:在使用Uri uri = Uri.fromFile(fileImagePath);
意图包含URI时,请确保添加以下行:
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
你可以走了。希望有帮助。只需将以下代码粘贴到activity onCreate()中即可 它将忽略URI暴露此答案已完成 此答案适用于-您已经有一个应用程序的目标值低于24,现在您正在升级到targetSDKVersion>=24 在安卓N中,只有暴露给第三方应用程序的文件uri被更改。(不是android:name="androidx.core.content.FileProvider"
Uri uri = Uri.fromFile(fileImagePath);
Uri uri = FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider",fileImagePath);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); StrictMode.setVmPolicy(builder.build());
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>
val playIntent = Intent(Intent.ACTION_VIEW, uri) intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); StrictMode.setVmPolicy(builder.build());
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);
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, worked for me in Android 10 using Solid Explorer Text Editor as the external editor. } Intent viewFile = new Intent(Intent.ACTION_VIEW); viewFile.setDataAndType(uri, "text/plain"); startActivity(viewFile);
ContentValues values = new ContentValues(); values.put(MediaStore.Video.Media.DATA, videoFilePath); Uri contentUri = context.getContentResolver().insert( MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values);
intent.setType("video/*"); intent.putExtra(Intent.EXTRA_STREAM, contentUri); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); activity.startActivity(intent);
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..."));
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); }
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 } }
<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>
<?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 {}
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); StrictMode.setVmPolicy(builder.build());
<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 } }
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); }
As of Android N, in order to work around this issue, you need to use the FileProvider API
<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>
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-path name="external_files" path="."/> </paths>
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);
<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>
<?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 }
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);
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"))