2024-07-16升级问题:调用手机自带软件打开文件时,出现以下问题:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: rs.tabletcropland, PID: 10997
android.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" -->
<provider
android:authorities="${applicationId}.fileprovider"
android:name="android.support.v4.content.FileProvider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths"/>
</provider>
...
</application>
如果是AndroidX的库,则可以如下:
<!--authorities="你的包名+fileprovider" -->
<provider
android:authorities="${applicationId}.fileprovider"
android:name="androidx.core.content.FileProvider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android: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 exception
Toast.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;
}
至此,问题解决。