最近在使用藍牙進行文件分享時,出現了一個奇怪的問題。一樣的代碼在android5.1上能夠順利運行,可是在android7.0上就運行失敗。出現以下的錯誤:android
Caused by: android.os.FileUriExposedException: file:///storage/emulated/0/bluetooth/data.txt exposed beyond app through ClipData.Item.getUri()
出現這個問題的時候我馬上意識到這是一個兼容的問題,因而在網上找了一些方法,並解決了這個問題,我受到啓發的網址是:安全
出現FileUriExposedException這樣的異常,緣由是Andorid7.0的「私有目錄被限制訪問」,「StrictMode API 政策」。 因爲從Android7.0開始,直接使用真實的路徑的Uri會被認爲是不安全的,會拋出一個FileUriExposedException這樣的異常。須要使用FileProvider,選擇性地將封裝過的Uri共享到外部。
即之前的共享代碼是這樣寫的:app
Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND); sharingIntent.setType("*/*"); sharingIntent.setComponent(new ComponentName("com.android.bluetooth","com.android.bluetooth.opp.BluetoothOppLauncherActivity")); sharingIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(path))); startActivity(sharingIntent);
在android7.0版本以上時,不使用Uri.fromFile(),使用如下的代碼:ide
Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND); sharingIntent.setType("*/*"); sharingIntent.setComponent(new ComponentName("com.android.bluetooth","com.android.bluetooth.opp.BluetoothOppLauncherActivity")); sharingIntent.putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(BluetoothChat.this,"你的包名" + ".fileprovider", new File(path))); sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); startActivity(sharingIntent);
若是是Andorid7.0或以上,則再也不使用Uri.fromFile()方法獲取文件的Uri,而是經過使用FileProvider(support.v4提供的類)的getUriForFile()。同時要添加多這麼一行代碼ui
sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
因爲FileProvider是繼承ContentProvider,屬於四大組件之一,須要在AndroidManifest.xml
中配置,配置以下:this
<provider android:name="android.support.v4.content.FileProvider" android:authorities="你的包名.fileprovider" android:exported="false" android:grantUriPermissions="true"> <!--元數據--> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_provider_paths"/> </provider>
其中android:resource="@xml/file_provider_paths"
的內容你能夠在res目錄下新建一個xml文件夾,在文件夾中新建一個file_provider_paths.xml文件便可,以下圖所示spa
<?xml version="1.0" encoding="utf-8"?> <resources> <paths> <external-path path="" name="myFile"></external-path> </paths> </resources>
上述代碼中path=」 「,是有特殊意義的,它代碼根目錄,也就是說你能夠向其它的應用共享根目錄及其子目錄下任何一個文件了,若是你將path設爲path=」pictures」, 那麼它表明着根目錄下的pictures目錄(eg:/storage/emulated/0/pictures),若是你向其它應用分享pictures目錄範圍以外的文件是不行的。
.net
分享文件代碼以下:code
// 調用系統方法分享文件 public static void shareFile(Context context, File file) { try{ if (null != file && file.exists()) { Log.w("ppp-params", file.getPath()); Intent share = new Intent(Intent.ACTION_SEND); share.putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(context,"app.zhaohangwuliu.com.appada" + ".fileprovider", file)); share.setType("*/*");//此處可發送多種文件 share.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); share.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); context.startActivity(Intent.createChooser(share, "分享文件")); } else { //Toast.makeText(context, "分享文件不存在", Toast.LENGTH_SHORT).show(); } }catch (Exception ex){ Log.w("ppp-params", ex); } }