原文地址:http://blog.youkuaiyun.com/nbalichaoq
android中调用系统图库本来是一个很基本的东西,几乎每个app都用的到(最基本的更换用户头像),网上的相关
容很多,本来找了几篇看了一下,拿几台测试机试了一下感觉就没什么问题了,但是适配问题慢慢就来了。
一.打开图库的基本方法。
通过查询资料,调用系统图库基本有3种方法。
1.使用Intent.ACTION_PICK
Intent i = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media. EXTERNAL_CONTENT_URI); //调用android的图库
//i.setType("image/*");//不可设置type,否则noactivityfound
startActivityForResult(Intent. createChooser(i,null) , 2) ;
这个intent在android6.0以下是可以用的,但是到了6.0就无效了,具体打印log忘记了,官方文档也只用了这
Intent来获取留联系人,所以pass掉。
2.使用Intent.ACTION_GET_CONTENT
Intent innerIntent = new Intent(Intent.ACTION_GET_CONTENT) ; //"android.intent.action.GET_CONTENT"
innerIntent.setType( "image/*"); //查看类型 String IMAGE_UNSPECIFIED = "image/*";
innerIntent.addCategory(Intent. CATEGORY_OPENABLE );
startActivityForResult(Intent. createChooser(innerIntent, null) , 2) ;
并且有个bool型的extra EXTRA_ALLOW_MULTIPLE来支持多选功能(4.3及以上版本支持)。
官方文档上说,这个intent是用来“检索一个特定类型的文件”并且回返回检索到的文件的一个引用(文件的copy)
这个貌似是可以用的,而且也是大多数人选择。
3.使用Intent.ACTION_OPEN_DOCUMENT
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT) ;
intent.addCategory(Intent. CATEGORY_OPENABLE);
intent.setType("image/*") ;
startActivityForResult(intent , 2) ;
这个action只支持4.4以上的版本。官方文档上说,这个intent是用来“打开一个特定类型的文件”,与检索相比
感觉跟适合我们的应用场景。同样可以使用EXTRA_ALLOW_MULTIPLE来支持多选。这个貌似更适合,只是需要判断sd
版本,4.4及以上使用ACTION_OPEN_DOCUMENT,4.3及以下使用ACTION_OPEN_DOCUMENT,这也是一些比较好的攻略里
使用方法。
二.选择图片后,图片文件的解析
当在系统图库中选择好图片后,可以在进入图库的Activity的onActivityResult()方法中通过返回的Uri进行解析
并且理论上Uri是content形式。而android的存储框架在4.4进行了一次改变。
(1)4.3及以下返回的uri是content://media/external/images/media/3951的形式,文件解析方式如下:
protected void onActivityResult( int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data) ;
if(requestCode== 2&&Activity.RESULT_OK==resultCode&& null!=data){
Uri selectedImage = data.getData();
String picturePath = null;
try {
String[] filePathColumns={MediaStore.Images.Media.DATA} ;
Cursor c = this.getContentResolver().query(selectedImage, filePathColumns, null,null, null) ;
c.moveToFirst() ;
int columnIndex = c.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);//getColumnIndex(filePathColumns[0]);
picturePath= c.getString(columnIndex) ;
c.close() ;
} catch (Exception e) {
picturePath = selectedImage.getPath();
}
Bundle bdl = new Bundle();
if(picturePath != null){
bdl.putString("image", picturePath);
UIUtil. openActivity(this, PicCutActivity. class, bdl, 3);
}
4.4及以上返回的uri是content://com.android.providers.media.documents/document/image:3952的形式,
析方式如下:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode==2&&Activity.RESULT_OK==resultCode&&null!=data){
Uri selectedImage = data.getData();
String picturePath = selectedImage.getPath();
try {
final String docId = DocumentsContract.getDocumentId(selectedImage);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("primary".equalsIgnoreCase(type)) {
picturePath = Environment.getExternalStorageDirectory() + "/" + split[1];
} else {
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[]{
split[1]
};
picturePath = FileUtils.getDataColumn(this, contentUri, selection, selectionArgs);
}
} catch (Exception e) {
picturePath = selectedImage.getPath();
}
}
三.结论,最终的适配方案
1.出现问题前
最开始,我使用的方法打开系统图库的方法是第三种方法中的适配方法。
(1)打开图库:
if(sdk版本<4.4)
使用Intent.ACTION_GET_CONTENT打开图库
else
使用Intent.ACTION_OPEN_DOCUMENT打开图库
(2)解析uri获取文件路径:
if(sdk版本<4.4)
使用第一种解析方式
else
使用第二种解析方法
貌似一切都逻辑都很合理,但最终都被碎片化的各个android手机厂商的定制系统给打破了。
2.问题出现
(1)第一个出现的问题是小米手机,测试的各种版本的小米手机都是4.4以上的系统,使
Intent.ACTION_OPEN_DOCUMENT,哪怕是通过setYpe("image/*")
设置了MIME type为图片,小米系统却还是给你打开了一个树形目录的文件管理器。虽然理论上我们应该去尽快适
android版本的更新,使用最新的api,但是现实中,我们只有使用Intent.ACTION_GET_CONTENT。
(2)
现在确定使用了Intent.ACTION_GET_CONTENT,而使用该Intent返回的Uri类型可以是content:// ;file:// ;
http://中的一种(Intent.ACTION_OPEN_DOCUMENT只有content://一种,虽然不用,但还是了解一下的好),虽然
论上打开图库返回的都应该是content://的类型,但测试中确实发现有有个别机型直接就返回了file://形式的文
路径,因此需要先判断是否直接返回了文件路径,是的文件路径的话就不用解析,否则进行解析。
(3)随着测试的机型的增多,理论上4.4以上的机型返回的content形式的Uri都是应该第二种解析方法适用的格式,
某些机型却会返回第一种解析方法适用的格式,并在解析过程中抛出异常。由于两种解析方式的原理我并没有仔细
研究,于是就在catch语句块中再进行了一次解析。
3.综上所述,我的打开图库的策略如下
(1)打开图库使用Intent.ACTION_GET_CONTENT
(2)选择图片文件解析:
Uri selectedImage = data.getData();//data是onActivityResult返回的intent
String picturePath = selectedImage.getPath();
if(!new File(picturePath).exists){//1.判断返回的uri是否是file:// 类型
try{
if(sdk < 4.4)
第一种解析方式//2. 4.4以下版本使用第一种方式
else
第二种解析方式//3. 4.4及以上使用第二种方式
}catch(Exception e){
try{
if(sdk >= 4.4)
第一种解析方式//4. 4.4及以上使用第二种方式抛出异常,则用第一种方式再解析一次
}catch(..){..}
}
}
若使用第一种解析方式抛出异常(虽然我没有遇见过),则picturePath = selectedImage.getPath();。在使
picturePath时最好再判断一下文件是否存在。以上就是我自己总结的逻辑,虽然实现方式也许不是很好,但逻辑
算是比较周全了,欢迎指正。