本篇将会介绍媒体选择器,包括图片选择器和视频选择器,更进一步的扩展到根据目录显示不同的图片。
2.1 图片选择器
具体开发图片选择器之前,先说明一下MediaStore类,之后所有的图片操作,查询都和这个类有关系。官网上对这个类的定义为:
The Media provider contains meta data for all available media on both internal and external storage devices.
简单理解就是对外提供设备上所有的媒体,它在android的包目录为:
package android.provider;
这个类属于provider,即ContentProvider组件之一,对外提供接口查询自身的数据。MediaStore这个类就是android系统提供的一个多媒体数据库类,这个类包括了多媒体数据库的所有信息,包括音频,视频和图像。因此用户直接利用ContentResolver调用相关的query方法去获取相关数据。在这个类中定义了较多的内部类,查看官网的API接口可看出包含MediaStore.Audio、MediaStore.Video、MediaStore.Images、MediaStore.Files4个内部类;同时包含了一个接口MediaStore.MediaColumes,该接口定义了所有与媒体相关的数据库标都会用到的数据库字段。见图1:
图1:MediaStore类的结构图
图1显示了MediaStore类的结构图,包含了相关的内部类和MediaColumns接口,这里展开了Images内部类。Images类定义了一个名为ImageColumns的接口,该接口定义了单独针对Image的数据库字段。同时Images类定义类一个名为Media的内部类用于查询和Image相关的信息,此外还定义了一个名为Thumbnails的内部类用于查询和Image相关的缩略图的信息。
下面来看Image.Media的query函数,其代码非常简单,如下所示:
code 1:Image.Media query函数
说明:可见Image.Media的query函数直接调用ContentResolver的query函数
ContentResolver的query函数分析如下code 2:
code 2:ContentResolver query函数
说明:
- uri:指明要查询的数据库名称加上表的名称,从MediaStore类中可以找到相应信息的参数
- projection:指定查询数据库表中的哪几列
- selection:指定查询条件
- selectionArgs:参数selection里有 ?这个符号是,这里可以以实际值代替这个问号。如果selection这个没有?的话,那么这个String数组可以为null。
- sortOrder:指定查询结果的排列顺序
接下来新建一个包component存放基础的组件,在component里新建一个类MediaChooseComponent。这里思考一下,既然作为一个组件,那么必然是提供所有上层业务所使用的,那么这个类也就存在唯一的实例,因此这个类要设计成单例类。这里详细说明一下单例设计模式。
设计模式-单例
定义:一个类有且仅有一个实例,并且自行实例化向整个系统提供
在app整个生命周期中,往往需要唯一的一个类的实例,那么就应该使用单例模式,因此就类似上文中MediaChooseComponent,具体实现方式有如下几点:
- 将类的构造方法私有化,使其不能在类的外部通过new来实例化对象
- 在该类内部定义一个该类的static对象,声明private类型
- 定义一个静态方法返回这个唯一的对象
实现方法分为饿汉模式,懒汉模式,双重判空
饿汉模式
直接定义并初始化static声明的变量,优点是不存在线程安全问题,缺点是不管你具体用不用这个类,它都会被加载到内存中,如下code3:
code 3:单例—饿汉模式
懒汉模式
延迟初始化,在需要的时候初始化,存在多线程安全问题,因为Java对象的创建过程不是原子操作,分为好几个步骤,具体可参看Java虚拟机,因此当多个线程同时访问的时候,就会出现线程安全问题,例如线程1判断instance == null,执行实例化过程;这个时候实例化还没完成,线程2同时也判断,此时instance==null 为true,因此也会去实例化对象,就会实例化2个对象出来。如下code4:
code 4:单例—懒汉模式
双重判空
如下code5,首先判断为空,然后加了锁,再判断下是否为空,如果为空则实例化对象。
code 5:单例—双重判空
说明:这里使用了final和volatile关键字,final关键字在修饰类的时候,该类不可被继承,因此不会被继承它的类所改变行为;volatile关键字修饰变量的时候表明该变量是对多线程可见的,满足一致性,即各个线程的工作内存能立即发现该变量的改变并保证一致性。
单例模式还有一些其他的实现方案,比如用枚举之类的,就不一一说明了,以上是最基础的实现方法。
现在来修改MediaChooseComponent为如下:首先定义了一个volatile static变量,通过getIns获取,在私有构造方法中初始化mediaResolver;定义了imageMediasCache作为image的缓存,同时定义了2个接口OnMediaChangeListener和OnMediaDirChangListener。从命名可看出是在Media发生改变或者MediaDir发生改变的时候发出通知,这里先忽略Dir相关的逻辑。
接下来就是获取Image的核心函数,在loadImageMedias方法中使用一个线程异步查询image media;在具体查询过程中,使用ContentResolve结合MediaStore最后生成MediaItem类型的对象加入到ArrayList中,最终得到需要到image数据。finally里面要close cursor。
总结
此篇初步介绍了MediaStore基础,并结合MediaChooseComponent组件详细介绍了单例设计模式,最终我们通过异步调用queryImageList()获取所有到Image media,并最终存放在数组中。下一篇将会完整实现图片选择器,并加入按照目录选取功能。
更多精彩技术分享请扫码关注小码时间(XiaoMaTime)