android打开系统图库终极适配

原文地址:http://blog.csdn.net/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时最好再判断一下文件是否存在。以上就是我自己总结的逻辑,虽然实现方式也许不是很好,但逻辑

算是比较周全了,欢迎指正。



©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页