图库的数据管理模型

图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, "

相关文章
相关标签/搜索