適配Android7.0應用間檔案共享FileProvider
阿新 • • 發佈:2019-01-03
android編譯版本升級到7.0以後,會出現很多適配方面的工作,從android官方文件對於android7.0行為變更可以瞭解到,android7.0的應用禁止傳遞類似file:// URI這樣的連結,否則應用會丟擲FileUriExposedException異常,比較典型的場景就是我們專案中呼叫攝像頭拍照,如果不對這個進行適配,我們按照以前的程式碼呼叫攝像頭拍照的時候,會出現以下錯誤:
android.os.FileUriExposedException: file:///storage/emulated/0/photoTest/photo.jpeg exposed beyond app through ClipData.Item.getUri()
接下來開始對這個進行適配,適配的方案主要參考了 鴻洋大神 Android 7.0 行為變更 通過FileProvider在應用間共享檔案吧 這篇部落格
1.首先在專案res目錄下新建xml目錄,並新建file_paths.xml,這個檔案主要用來配置應用共享檔案的路徑
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <root-path name="root" path="" /> <files-path name="files" path="" /> <cache-path name="cache" path="" /> <external-path name="external" path="" /> <external-files-path name="external_file_path" path="" /> <external-cache-path name="external_cache_path" path="" /> </paths>
在paths節點下支援以下幾個子節點:
- :代表裝置的根目錄new File("/")
- : 代表context.getFilesDir()
- : 代表context.getCacheDir()
- : 代表Environment.getExternalStorageDirectory()
- : 代表context.getExternalFilesDirs()
- : 代表getExternalCacheDirs()
path節點支援name和path兩個屬性,配置了path屬性就相當於在相應路徑下子目錄,例如:
<external-path name="external" path="phototest" />
這樣配置就代表應用可以使用Environment.getExternalStorageDirectory()/phototest 目錄以及其子目錄的檔案
2. 在AndroidManifest.xml的application節點下增加FileProvider的宣告
<application>
...
...
<!--適配android 7.0檔案訪問
com.hua.phototest是應用的包名
-->
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.hua.phototest.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
3.FileProvider工具類,參考自鴻洋大神部落格
public class FileProvider7 {
public static Uri getUriForFile(Context context, File file) {
Uri fileUri = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
fileUri = getUriForFile24(context, file);
} else {
fileUri = Uri.fromFile(file);
}
return fileUri;
}
private static Uri getUriForFile24(Context context, File file) {
Uri fileUri = android.support.v4.content.FileProvider.getUriForFile(context,
context.getPackageName() + ".fileprovider",
file);
return fileUri;
}
public static void setIntentDataAndType(Context context,
Intent intent,
String type,
File file,
boolean writeAble) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setDataAndType(getUriForFile(context, file), type);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
if (writeAble) {
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
} else {
intent.setDataAndType(Uri.fromFile(file), type);
chmod("777", file.getAbsolutePath());//apk放在cache檔案中,需要獲取讀寫許可權
}
}
public static void chmod(String permission, String path) {
try {
String command = "chmod " + permission + " " + path;
Runtime runtime = Runtime.getRuntime();
runtime.exec(command);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void setIntentData(Context context,
Intent intent,
File file,
boolean writeAble) {
if (Build.VERSION.SDK_INT >= 24) {
intent.setData(getUriForFile(context, file));
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
if (writeAble) {
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
} else {
intent.setData(Uri.fromFile(file));
}
}
public static void grantPermissions(Context context, Intent intent, Uri uri, boolean writeAble) {
int flag = Intent.FLAG_GRANT_READ_URI_PERMISSION;
if (writeAble) {
flag |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
}
intent.addFlags(flag);
List<ResolveInfo> resInfoList = context.getPackageManager()
.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
context.grantUriPermission(packageName, uri, flag);
}
}
}
4.最後在傳遞URI的時候呼叫相應的方法獲取URI即可,例如下面程式碼是呼叫攝像頭拍照:
private String mTempPhotoPath;
private Uri imageUri;
private void takePhoto() {
Intent intentToTakePhoto = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File fileDir = new File(Environment.getExternalStorageDirectory() + File.separator + "photoTest" + File.separator);
if (!fileDir.exists()) {
fileDir.mkdirs();
}
File photoFile = new File(fileDir, "photo.jpeg");
mTempPhotoPath = photoFile.getAbsolutePath();
//適配android7.0應用間檔案共享
imageUri = FileProvider7.getUriForFile(this, photoFile);
intentToTakePhoto.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intentToTakePhoto, RC_TAKE_PHOTO);
}