不得不说一下,android适配真的是一个大坑。
在使用文件管理器打开本地文件时,刚开始以为超级简单,就像下面:
// 打开文件管理器选择文件
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
// intent.setType("image/*");//选择图片
// intent.setType("audio/*"); //选择音频
// intent.setType("video/*"); //选择视频 (mp4 3gp 是android支持的视频格式)
// intent.setType("video/*;image/*");//同时选择视频和图片
intent.setType("*/*");//无类型限制
intent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(intent, mTag);
在回调方法,获取选中文件的路径:
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == mTag) {
Uri uri = data.getData();
if (uri != null) {
try {
String[] filePathColumn = {MediaStore.Images.Media.DATA};
@SuppressLint("Recycle") Cursor cursor = this.getContentResolver().query(uri,
filePathColumn, null, null, null);//从系统表中查询指定Uri对应的照片
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
String path = cursor.getString(columnIndex); //获取照片路径
if (path == null) {
/**
* 2022年6月14日12:11:56
* 加入这个是当选址最近使用的文件的时候,上面的会没有找到这个文件路径,所以加上这个
*/
if (Build.VERSION.SDK_INT >= 19) {
path = handleImageOnKitKat(this, data);
} else {
path = handleImageBeforeKitKat(this, data);
}
}
} catch (Exception e){
}
}
}
}
其中 handleImageOnKitKat 方法为:
@TargetApi(Build.VERSION_CODES.KITKAT)
public static String handleImageOnKitKat(Context context, Intent data) {
Uri uri = data.getData();
if (DocumentsContract.isDocumentUri(context, uri)) {
String docId = DocumentsContract.getDocumentId(uri);
if ("com.android.externalstorage.documents".equals(uri.getAuthority())) {
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
} else if ("com.android.providers.media.documents".equals(uri.getAuthority())) {
String id = docId.split(":")[1];
String selection = MediaStore.Images.Media._ID + "=" + id;
String type = docId.split(":")[0];
Uri contentUri = null;
if (type.equalsIgnoreCase("image")) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if (type.equalsIgnoreCase("video")) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if (type.equalsIgnoreCase("audio")) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
return getPath(context, contentUri, selection);
} else if ("com.android.providers.media.downloads.documents".equals(uri.getAuthority())) {
Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"),
Long.valueOf(docId));
return getPath(context, contentUri, null);
} else if ("content".equals(uri.getAuthority())) {
return getPath(context, uri, null);
} else if ("file".equals(uri.getAuthority())){
return uri.getPath();
}
} else {
return uri.getPath();
}
return "";
}
handleImageBeforeKitKat 方法为:
public static String handleImageBeforeKitKat(Context context, Intent data) {
Uri uri = data.getData();
return getPath(context, uri, null);
}
到此,就可以拿到本地文件的路径了。
结果在不同手机上就出现了适配问题。有的手机在文件管理器的最近文件中可以看到 音频、视频、图片以外的文件,当我们去选择这些文件时,上面的代码就会报错。
debug调试了许久,发现了问题是所在:
DocumentsContract.getDocumentId(uri);
该行代码在大多数情况下返回的是:image、video、audio,但是在我们这里返回的却是 document。所在我们在解析时,就会找不到对应的,就会报错。
解决方法:
可能有些人已经注意到了,我们在判断类型时,用的都是同一个对象 MediaStore。
MediaStore是android系统提供的一个多媒体数据库,专门用于存放多媒体信息的,通过ContentResolver即可对数据库进行操作。
- MediaStore.Files: 共享的文件,包括多媒体和非多媒体信息;
- MediaStore.Audio: 存放音频信息;
- MediaStore.Image: 存放图片信息;
- MediaStore.Vedio: 存放视频信息;
我们就可以去使用 MediaStore.Files 来获取刚刚返回 document 时的问题。具体代码:
Uri contentUri = null;
if (type.equalsIgnoreCase("image")) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if (type.equalsIgnoreCase("video")) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if (type.equalsIgnoreCase("audio")) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
} else {
contentUri = MediaStore.Files.getContentUri("external");
}
这样就可以正常的解析文件路径了。