圖庫的數據管理模型

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

相關文章
相關標籤/搜索