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节点下支持以下几个子节点:
- <root-path/> :代表设备的根目录new File("/")
- <files-path/> : 代表context.getFilesDir()
- <cache-path/> : 代表context.getCacheDir()
- <external-path/> : 代表Environment.getExternalStorageDirectory()
- <external-files-path/> : 代表context.getExternalFilesDirs()
- <external-cache-path/> : 代表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);
}
网友评论