2024-07-16升级问题:调用手机自带软件打开文件时,出现以下问题:
E/AndroidRuntime: FATAL EXCEPTION: mainProcess: rs.tabletcropland, PID: 10997android.os.FileUriExposedException: file:///storage/emulated/0/arcgis/%E7%9F%B3%E7%8B%AE%E5%B8%82/Attachment/%E7%9F%B3%E7%8B%AE%E5%B8%82%E8%AE%BE%E6%96%BD%E5%86%9C%E4%B8%9A%E5%A4%A7%E6%A3%9A%E8%B0%83%E6%9F%A5%E6%95%B0%E6%8D%AE.xlsx exposed beyond app through Intent.getData()at android.os.StrictMode.onFileUriExposed(StrictMode.java:2210)at android.net.Uri.checkFileUriExposed(Uri.java:2419)at android.content.Intent.prepareToLeaveProcess(Intent.java:11812)at android.content.Intent.prepareToLeaveProcess(Intent.java:11764)at android.app.Instrumentation.execStartActivity(Instrumentation.java:1765)at android.app.Activity.startActivityForResult(Activity.java:5730)at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:767)at android.app.Activity.startActivityForResult(Activity.java:5654)at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:754)at android.app.Activity.startActivity(Activity.java:6152)at android.app.Activity.startActivity(Activity.java:6105)at com.FJDZYG.GIS.Tools.Base.OpenFileUtil.openFile(OpenFileUtil.java:107)at com.FJDZYG.GIS.Common.homePage.view.SearchFileActivity.openfile(SearchFileActivity.java:68)at com.FJDZYG.GIS.Common.homePage.view.SearchFileActivity.access$300(SearchFileActivity.java:33)at com.FJDZYG.GIS.Common.homePage.view.SearchFileActivity$6.false}lifecycleStateRequest PauseActivityItem{finished=true,userLeaving=false,configChanges=0,dontReport=false}
I/Process: Sending signal. PID: 10997 SIG: 9
Process 10997 terminated.
这个错误是因为在Android 7.0及以上版本中,直接使用file://URI可能会导致FileUriExposedException。为了解决这个问题,你可以将文件路径转换为content://URI,然后使用Intent来打开文件。
解决步骤:
1、在AndroidManifest.xml中添加FileProvider
在你的AndroidManifest.xml文件中添加一个<provider>
元素,用于声明FileProvider和其相关的元数据。这里需要注意的是android:authorities
属性的值:
authorities:app的包名.fileProvider
grantUriPermissions:必须是true,表示授予 URI 临时访问权限
exported:必须是false
resource:中的@xml/file_paths是我们接下来要添加的文件
<application ... >...<!--authorities="你的包名+fileprovider" --><providerandroid:authorities="${applicationId}.fileprovider"android:name="android.support.v4.content.FileProvider"android:grantUriPermissions="true"android:exported="false"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/filepaths"/></provider>...
</application>
如果是AndroidX的库,则可以如下:
<!--authorities="你的包名+fileprovider" --><providerandroid:authorities="${applicationId}.fileprovider"android:name="androidx.core.content.FileProvider"android:grantUriPermissions="true"android:exported="false"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/filepaths"/></provider>
2、创建file_paths.xml文件
在你的Android项目的res/xml
目录下创建一个名为file_paths.xml
的文件(如果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>
完整的:
files-path代表的根目录: Context.getFilesDir().getPath()
external-path代表的根目录: Environment.getExternalStorageDirectory().getPath()
cache-path代表的根目录: getCacheDir().getPath()
path 代表需要共享的目录
name 只是一个标识,随便取
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android"><!-- external-path:sd ;path:你的应用保存文件的根目录;name随便定义--><!-- root-path 手机存储根目录 --><root-path path="" name="arcgis" /><external-path name="external_files" path="." /><files-path name="picture" path="internal/pic/"/><files-path name="database" path="internal/db/"/><external-files-path name="picture" path="picture/"/>
</paths>
3、在代码中使用FileProvider
在需要构建文件Uri的地方,使用FileProvider来构建Uri,而不是直接使用Uri.fromFile()。
完整代码:
/*** 打开文件* 兼容7.0* @param context activity* @param file File* @param contentType 文件类型如:文本(text/html)* 当手机中没有一个app可以打开file时会抛ActivityNotFoundException*/public static void startActionFile(Context context, File file, String contentType) throws ActivityNotFoundException {if (context == null) {return;}Intent intent = new Intent(Intent.ACTION_VIEW);intent.addCategory(Intent.CATEGORY_DEFAULT);intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);//打开只要读取的权限就够了。写的权限会导致失败intent.setDataAndType(getUriForFile(context, file), contentType);if (!(context instanceof Activity)) {intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);}context.startActivity(intent);}
/**** Android7.0以上文件在应用间打开方式* @param context* @param file*/public static void openFile_new(Context context, File file) {try {if (context == null) {return;}Intent intent = new Intent(Intent.ACTION_VIEW);intent.addCategory(Intent.CATEGORY_DEFAULT);intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);//打开只要读取的权限就够了。写的权限会导致失败String contentType = getMIMEType(file);intent.setDataAndType(getUriForFile(context, file), contentType);intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);context.startActivity(intent);}catch (ActivityNotFoundException e) {// TODO: handle exceptionToast.makeText(context, "sorry附件不能打开,请下载相关软件!", Toast.LENGTH_LONG).show();}}public static Uri getUriForFile(Context context, File file) {if (context == null || file == null) {throw new NullPointerException();}Uri uri;if (Build.VERSION.SDK_INT >= 24) {uri = FileProvider.getUriForFile(context.getApplicationContext(), context.getPackageName()+".fileprovider", file);} else {uri = Uri.fromFile(file);}return uri;}
至此,问题解决。