图1 页面AlbumPage在Activity resume时,数据模型的时序图数据库
图2 在AlbumPage滑动时数据模型的时序图缓存
图3 AlbumPage启动时建立MediaSet实例的时序图ide
图4 数据模型类图1spa
图5 数据模型类图2线程
Path设计
Path是MediaObject体系能成为组合模式的关键。组合模式中,MediaObject体系依赖Path才能够向上遍历和向下遍历,path的存在还容许用户根据以String表明的路径或者segment来寻找来找到对应的MediaObject。3d
mChildren : 以segment:String做为key,Path做为value。其实IdentityCache是一个HashMap。mChildren中的Path是该Path的子Path。code
mParent:是该Path的父Path。这决定了Path组成的树是能够向上遍历的。server
mSegment:就是该Path的在父Path中的惟一标志,该Path在全局中的惟一标识则由父Path和segment组成。对象
sRoot:这是个final静态变量。是Path树的根。
fromString( String ) : 将路径从根部sRoot开始向下找到对应的Path,若是没有就根据这个String,新建这个路径,就像文件同样,文件的路径是由文件名和文件夹名组成的,若是该路径中的文件夹不存在,会先去新建
文件夹,根据String去新间Path也是一样的道理。
getChild( String segment) : 根据直接下级的名字去获取该Path下的child。没有就new一个放到mChildren中。
getPrefix() : 获取这个路径的根路径,就是sRoot的直接下级。每一个MediaSource都对应一个特定prefix。每一个MediaSource都是一个工厂模式,根据Path或者Path的String生产一类产品,这些产品就是MediaObject的子类。
setObject (MediaObject): 为该Path节点设置节点数据,就是对应的MediaObject,能够是MediaItem(如:LocalImage,LocalVideo),能够是MediaSet(如:LocalAlbum),也能够是MediaSet的集合(如: LocalAlbumSet)
MediaSource
MediaSource体系组成了参数化工厂方法模式
MediaSource的持有者是Datamanager
mPrefix : String
每一个MediaSource子类都一个特殊前缀,如:LocalSource的前缀是“local”,而传入工厂方法的参数(path)的首segment都是local才能正确匹配,如:/local/image/item/*
createMediaObject(Path) : MediaObject
根据Path对应String路径去匹配,而后返回不一样的MediaObject,多是MediaItem(表明单张照片),或者MediaSet(表明一个相册),又或是AlbumSet(表明一个相册集合)。
因为MediaSource是MediaObject的制造工厂,因此MediaObject的的构造方法不该该是public,而是应该是默认权限,这样更能对MediaObject的实例化进行监控起来。
DataManager
mSourceMap : HashMap<String, MediaSource>key是MediaSource的prefix; 保存了图库的全部MediaSource子类的实例,这是MediaSource惟一被初始化的i地方,并且MediaSource各子类的实例也之应只有一个,而后却没有设计成单例模式。
mNotifierMap : HashMap<Uri, NotifyBroker>
NotifyBroker继承了ContentObserver,用于监听key中的Uri。每一个MediaSet持有一个NotifyBroker。就是说每一个MediaSet都会独立去监听感兴趣的数据的状态
getMediaObject(Path) : MediaObject
尝试从Path中获取MediaObject,没有的话就根据Path的prefix去选择对应的MediaSource去createMediaObject(path),此时Path就和这个MediaObject绑定在一块儿了。
registerChangeNotifier(Uri, ChangeNotifier)
向ContentResolver注册ContentObserver,而ChangeNotifier并非ContentObserver,算是一个被适配的对象(adaptee)。
DataLoader(以AlbumDataLoader为例)
|
|---------------------------------------------------------------|A
|Dataloader的content部分 |
|在AlbumLoader中为mData.length=1000 |
|------------------------------------------| |B
| slidingwindow的 content和 | |
|-------------------------| | |C
|可见部分 (active) | | |
|-------------------------| | |D
|dataloader的active部分 | |
|------------------------------------------| |E
| |
| |
|--------------------------------------------------------------|F
|
mActiveStart : int
DataLoader的活跃段的起始index(在对应MediaSet中的index),由SlidingWindow#setContentWindow(int, int)调用DataLoader的setActiveWindow(int, int)时设置
mActiveEnd : int
和mActiveStart相对
mContentStart : int
DataLoader的数据段的起始index,数据段包含了活跃段,活跃段在数据段的中心段,DataLoader的活跃段SlidingWindow的数据段是一致的,而SlidingWindow的活跃段就是可见的数据。SlidingWindow的
的数据段和DataLoader的数据段都是以可见部分为中心,随着可见部分的不断移动而变化。
mContentEnd
和mContentStart相对
mData : MediaItem[ ]
全部MediaItem都会在以Path#sRoot : Path为根的树中。可经过路径快速查找到。可是只有在DataLoader中的MediaItem会被及时的更新对应图片数据的元数据,如大小,宽高。这涉及到了数据的版本管理。
每一个MediaSet都会监听其对应的Uri,可是不会去更新MediaItem,只是会通知对其感兴趣的DataLoader,而后DataLoader会对ReloadTask的mDirty置为true。
mItemVersion : int[ ]
对应mData中的MediaItem的version。MediaItem有一个Item属性。mItemVersion主要用于和MediaItem中的version属性对比,若是mItemVersion中的值和MediaItem的不等,则说明这个MediaItem刚更新过,须要通知
SlidingWindow,而后SlidingWindow根据是否其content范围的数据决定是否更新其mData中的图片数据(注意和图片数据的元数据的区分)。
mSetVersion : int[ ]
对应mData中的MediaItem最近被更新时对应的mSouce(LocalAlbum)的version,每次更新mData前,都会调用MediaSet#reload()获取新的版本号。只有其ChangeNotifier#mDirty为true时才会返回新版本号
————————————————————————————————————————————————————————————————————————
mReloadTask : DataLoader$ReloadTask
ReloadTask extends Thread
更新mData中MediaItem的核心内部类,mActive : boolean
调用resume后就为true,调用pause后就为false。控制ReloadTask#run()的主循环。
mDirty : boolean
控制着ReloadTask#run()的主循环是否进入等待队列,为false则进入等待。
notifyDirty()
将mDirty设为true,而后中断主循环。
terminate()
将mActive置为false,而后中断主循环。
run() : 依赖DataLoader$UpdateContent、DataLoader$UpdateInfo、LocalAlbum(DataLoader#mSource : MediaSet 对应的实例)去更新mData中MediaItem的信息或更换mData中的MediaItem,并更新对应mItemVersion和mSetVersion中的version,获取更新MediaItem是调用LocalAlbum#getMediaItem(start, count) : List,其中就去查询了数据库,后面说MediaObject体系时再详细说。
—————————————————————————————————————————————————————————————————————————
——————————————————————————————————————————————————————————————————————————
DataLoader$UpdateInfo
version : long
这是DataLoad#mSource : LocalAlbum的version
reloadStart: int
从哪一个index开始更新
reloadCount : int
须要更新多少个MediaItem
size : int
表示mSource:MediaSet(LocalAlbum)的getCount()
items :ListArray<MediaItem>
指向已经更新的MediaItem,是ReloadTask#run()中调用(LocalAlbum)mSource#getMediaItem(reloadStart, reloadCount)获取的
++++++++++++++++++++++++++++++++
DataLoader$GetUpdateInfo
mVersion : int
call() : UpdateInfo
主要就是肯定上面UpdateInfo的几个成员参数。i的选定范围是mContentStart至mContentEnd,从mContentStart开始扫描,条件是对应mSetVersion[ i ] ≠ version;count = min(MAX_LOAD_COUNT, mContentEnd - i)
———————————————————————————————————————————————————————————————————————————
————————————————————————————————————————————————————————————————————————————
DataLoader$UpdateContent
mUpdateInfo UpdateInfo
就是已经初始化完成UpdateInfo
call() :Void
根据已有的mUpdateInfo去更新reloadStart~reloadStart+reloadCount 的 mData[]、mItemVersion、mSetVersion[].若是是在DataLoader的Active范围,即SlidingWindow的Content范围
则经过DataLoader$DataListener#onContentChanged(int)通知SlidingWindow,固然只有itemversion改变时(即对应index指向的图片数据发生了变化),才去通知SlidingWindow,由于SlidingWindow没法根据version判断数据是否发生改变,若是SlidingWindow去从新加载没有更新图片则是浪费资源。
——————————————————————————————————————————————————————————————————————————————
SlidingWindow(以AlbumSlidingWindow为例)
mActiveStart : int
屏幕可见的起始item的index
mActivEnd : int
屏幕可见的最后item的index
mContentStart : int
缓存texture的起始index
mContentEnd:int
缓存texture的最后的index
mData :AlbumEntry[ ]
是SlidingWindow的数据集合,mData.length == mContentEnd - mContentStart
mSource : AlbumDataLoader
mThreadPool : JobLimiter
其中JobLimiter是封装了ThreadPool,是执行图片请求的
mVideoMicroThumbDecoder : JobLimiter
这是执行video请求的
mTileUploader : TiledTexture.Uploader
用于将AlbumEntry中TiledTexture上传到GLOpen可使用的地方
——————————————————————————————————————————————————————————————
AlbumSlidingWindow$AlbumEntry
item : MediaItem
isWaitDisplayed : boolean
mBitmapTexture : TiledTexture
content : Texture
contentLoader : BitmapLoader
_____________________________________________________________________________________________________________________
——————————————————————————————————————————————————————————————
BitmapLoader implements FutureTask<Bitmap>
mState : int
描述任务的状态
mTask : Future<Bitmap>
mBitmap : Bitmap
startLoad()
将本身提交到TheadPool中
recycle()
将mBitmap放入缓存GalleryBitmapPool中,其中GalleryBitmapPool是一个单例
+++++++++++++++++++++++++++++++++++++++++++++
AlbumSlidingWindow$ThumbnailLoader extends BitmapLoader
mItem : MediaItem
mSlotIndex
updateEntry()
利用mBitmap去实例化Texture并更新mData[mSlotIndex]
onLoadComplete(Bitmap)
在子线程中调用该方法,而后使用Handler,而后在主线程中调用updateEntry()
++++++++++++++++++++++++++++++++++++++++++++++
AlbumSlidingWindow$Listener
这是一个interface,用于让Render实现并监听AlbumSlidingWindow。
onSizeChanged(int)
AlbumSlidingWindow在size发生改变时通知Renderer
onContentChanged()
AlbumSlidingWindow在其onContentChanged(int)就会调用该方法通知Renderer去invalidate
——————————————————————————————————————————————————————————————
setActiveWindow(int, int)
在图片的滑动过程当中,active index必然会改变。须要调用这方法去带动一连串的更新逻辑。带动content index的改变还有带动DataLoader的active index 和 content index的改变。
将须要显示的新的图片的texture经过mTileUploader上传,更新active范围内须要更新的AlbumEntry,更新AlbumEntry中的成员就须要使用updateAllImageRequests()。若是没有active范围内须要更新的,就去更新content范围的,
setContentWindow(int, int)
在setActiveWindow(int, int)中被调用,clear mData 中滑出content范围的,调用DataLoader#setActiveWindow(int, int)启动DataLoader的mData更新数据,而后为滑进content范围的MediaItem建立并初始化AlbumEntry。
updateAllImageRequests()
更新AlbumEntry中的须要更新的Entry。若是没有active范围内须要更新的,就去更新content范围的。由于这样就能够在快速滑动时,从content中加入active的item不用去加载图片,立刻就能够显示出来。
freeSlotContent(int)
清空mData中滑出content范围的MediaItem,并调用的AlbumEntry#contentLoader#recycle()将AlbumEntry对应的Bitmap放入GalleryBitmapPool。GalleryBitmapPool是一个以图片宽和高做为数据惟一性的依据,不过普通图片(相机拍出来的)
是不会放进缓存区的。其中int参数就是滑出content范围的index。而后调用TiledTexture#recycle(),这将TiledTexture的Tile[]放入一个sFreeTileHeader : Tile的链表中。在建立TiledTexture须要Tile[]时就去这个链表拿,并从链表中移除该Tile,
若是链表中没有则实例化一个Tile。存入和获取分别对应freeTile(Tile)和 obtainTile():Tile。而后一个Bitmap对应的TiledTexture的Tile[]保存下来了,可是Tile#bitmap被置空,即并无保存图片数据。Tile没有Bitmap是不能绘制到屏幕上的。
prepareSlotContent(int)
和freeSlotContent(int)对应
MediaItem(以LocaAlbum为例)
sThumbnailTargetSize : int
通常缩略图的目标大小,即最终会压缩到的大小
sMicrothumbnailTargetSize : int
最小缩略图的目标大小
get 各类 detail的方法,这里就不一一列举了
requestImage(int) : Job<Bitmap>
返回一个相似runnable的接口类型的对象,调用该对象的run()方法能够返回一个已经处理好的Bitmap(包括按指定规格压缩,裁剪等),返回的是缩略图。这个requestImage(int)方法和Job都由各个子类去实现。
requestLargeImage() : Job<BitmapRegionDecoder>
和requestImage(int)不一样的是,这个Job的run()方法会返回一个BitmapRegionDecoder,而不是一个直接的Bitmap,由于这是用于加载原图的方法。因为通常原图比较大,须要将一个图片分红几个Region来加载。
MediaSet(以LocalAlbum为例)
每一个Album都有一个ChangeNotifier,可是MediaSet这个类并无。这个ChangeNotifier是用于监听该Album对应的Uri的。这个也能够看DataManager
isLeafAlbum() : boolean
是否Album,否的话就是AlbumSet了
getIndexOf(Path path, ArrayList<MediaItem> list) : int
获取对应item的index
getMediaItemCount() : int
获取给MediaSet的item数
getMediaItem(int start, int count) : ArrayList<MediaItem>
从start开始获取,获取count个item
getSubMediaSet(int index) : MediaSet
获取子MediaSet的数目
getSubMediaSet(int index) : MediaSet
获取index对应的MediaSet
addContentListener(ContentListener listener)
设置监听器,上面说到了,每一个Album都有一个ChangeNotifier,当有变化时这个Notifier回去调用这些被add进来的ContentListener的onContentDirty()方法,都是DataLoader监听MediaSet的,也就是说是DataLoader add 进来的。
reload() : long
返回一个版本好。根据是否dirty(即监听到了Uri指向的数据改变了),重载一些更新查询MediaItem须要用到的参数,若是where字句,bucketId等。不一样MediaSet子类有不一样的须要。
reloadWhereClause() : 更新查询数据库时须要用到的where子句
而做为MediaSet重要子类,LocalAlbum还有如下一些重要成员:
mItemPath : Path
就是该LocalAlbum的Path,分为Video和Image
mBaseUri : Uri
也是分别有Video和Image的
mBucketId : int
BucketId是MediaStore中的一个column字段,大概每一个包含图片的文件夹都会对应一个BucketId,即你去查询“bucket_id”=mBucketId时,会返回mBucketId对应文件的全部图片在多媒体数据库中的信息
mProjection : String[ ]
感兴趣的列
mOrderClause : String
按时间递减,ImageColumns.DATE_TAKEN + " DESC, "