0. 原文拜讀
- 作者:lb377463323
- 出處:http://blog.csdn.net/lb377463323
- 原文連結:http://blog.csdn.net/lb377463323/article/details/70890461
1. AlbumSetPage界面資料裝載和界面重新整理,為加載專輯縮略圖準備
1. 資料裝載
package com.android.gallery3d.app;
public class AlbumSetPage extends ActivityState implements OnClickListener,...{
private AlbumSetDataLoader mAlbumSetDataAdapter;
@Override
public void onResume() {
super.onResume();
...
mAlbumSetDataAdapter.resume();
...
}
}
- 上接mAlbumSetDataAdapter.resume()
package com.android.gallery3d.app;
public class AlbumSetDataLoader {
public void resume() {
mSource.addContentListener(mSourceListener);
mReloadTask = new ReloadTask();
mReloadTask.start();
}
- 上接 ReloadTask()
// TODO: load active range first
private class ReloadTask extends Thread {
@Override
public void run() {
while (mActive) {
...
// 資料加載完成,進入界面重新整理
executeAndWait(new UpdateContent(info));
}
}
}
1.2 界面重新整理的回調事件
- 上接 executeAndWait(new UpdateContent(info));
private class UpdateContent implements Callable<Void> {
private final UpdateInfo mUpdateInfo;
@Override
public Void call() {
...
for (DataListener l : mDataListener) {
l.onSizeChanged(mSize);
}
...
for (DataListener l : mDataListener) {
l.onContentChanged(info.index);
}
}
}
- 檢視下接口定義
package com.android.gallery3d.app;
public static interface DataListener {
public void onContentChanged(int index);
public void onSizeChanged(int size);
}
-
grep指令檢視實作該接口
目前隻有 AlbumSetSlidingWindow 實作
src/com/android/gallery3d/ui/AlbumSetSlidingWindow.java:40:public class AlbumSetSlidingWindow implements AlbumSetDataLoader.DataListener {
具體如下
package com.android.gallery3d.ui;
public class AlbumSetSlidingWindow implements AlbumSetDataLoader.DataListener {
@Override
public void onContentChanged(int index) {
....
//mData是容量為96的AlbumSetEntry數組,index是代表加載哪一個專輯,範圍是0-(n-1),n為專輯數
AlbumSetEntry entry = mData[index % mData.length];
//專輯縮略圖和label主要由下面三個方法完成
updateAlbumSetEntry(entry, index);
updateAllImageRequests();
updateTextureUploadQueue();
//onContentChanged方法就是執行mSlotView.invalidate()重新整理界面
if (mListener != null && isActiveSlot(index)) {
mListener.onContentChanged();
}
}
2. 加載專輯縮略圖
package com.android.gallery3d.ui;
public class AlbumSetSlidingWindow implements AlbumSetDataLoader.DataListener {
@Override
public void onContentChanged(int index) {
....
//mData是容量為96的AlbumSetEntry數組,index是代表加載哪一個專輯,範圍是0-(n-1),n為專輯數
AlbumSetEntry entry = mData[index % mData.length];
//專輯縮略圖和label主要由下面三個方法完成
updateAlbumSetEntry(entry, index);
updateAllImageRequests();
updateTextureUploadQueue();
...
}
2.1 AlbumSetSlidingWindow.updateAlbumSetEntry
縮略圖和标簽的準備
private void updateAlbumSetEntry(AlbumSetEntry entry, int slotIndex) {
...
//cover: 從專輯裡面擷取的一個圖檔,用來作為專輯縮略圖
MediaItem cover = mSource.getCoverItem(slotIndex);
...
entry.coverItem = cover;
if (getDataVersion(cover) != entry.coverDataVersion) {
...
if (cover != null) {
//AlbumLabelLoader就是用來加載縮略圖下面的label,如專輯名、此專輯有多少張圖檔等
entry.coverLoader = new AlbumCoverLoader(slotIndex, cover);
}
}
}
上接 new AlbumCoverLoader(slotIndex, cover);
private class AlbumCoverLoader extends BitmapLoader implements EntryUpdater {
public AlbumCoverLoader(int slotIndex, MediaItem item) {
mSlotIndex = slotIndex;
mMediaItem = item;
}
}
上述便于 AlbumSetSlidingWindow.updateAllImageRequests 中 startLoadBitmap 的調用
2.2 AlbumSetSlidingWindow.updateAllImageRequests
加載縮略圖和标簽
private void updateAllImageRequests() {
mActiveRequestCount = 0;
for (int i = mActiveStart, n = mActiveEnd; i < n; ++i) {
AlbumSetEntry entry = mData[i % mData.length];
// 開始執行縮略圖和标簽加載
if (startLoadBitmap(entry.coverLoader)) ++mActiveRequestCount;
if (startLoadBitmap(entry.labelLoader)) ++mActiveRequestCount;
}
...
}
上接 startLoadBitmap
private static boolean startLoadBitmap(BitmapLoader loader) {
if (loader == null) return false;
loader.startLoad();
return loader.isRequestInProgress();
}
上接 loader.startLoad
package com.android.gallery3d.ui;
// We use this class to
// 1.) load bitmaps in background.
// 2.) as a place holder for the loaded bitmap
public abstract class BitmapLoader implements FutureListener<Bitmap> {
public synchronized void startLoad() {
if (mState == STATE_INIT) {
mState = STATE_REQUESTED;
if (mTask == null) mTask = submitBitmapTask(this);
}
}
abstract protected Future<Bitmap> submitBitmapTask(FutureListener<Bitmap> l);
}
根據多态,這裡的 loader 實作在 AlbumCoverLoader 和 AlbumLabelLoader
private class AlbumCoverLoader extends BitmapLoader implements EntryUpdater {
...
@Override
protected Future<Bitmap> submitBitmapTask(FutureListener<Bitmap> l) {
//mMediaItem為LocalImage或LocalVideo,線程池
return mThreadPool.submit(mMediaItem.requestImage(
MediaItem.TYPE_MICROTHUMBNAIL), l);
}
...
}
public AlbumLabelLoader(
int slotIndex, String title, int totalCount, int totalImageCount,
int totalVideoCount,int albumCount, int sourceType) {
...
@Override
protected Future<Bitmap> submitBitmapTask(FutureListener<Bitmap> l) {
// 标簽
return mThreadPool.submit(mLabelMaker.requestLabel(
mTitle, String.valueOf(mTotalCount),mTotalImageCount,
mTotalVideoCount, mAlbumCount, mSourceType), l);
}
...
}
2.2.1 MediaItem.requestImage
- 上接 mMediaItem.requestImage(MediaItem.TYPE_MICROTHUMBNAIL)
package com.android.gallery3d.data;
// MediaItem represents an image or a video item.
public abstract class MediaItem extends MediaObject {
public abstract Job<Bitmap> requestImage(int type);
}
檢視多态發現 LocalImage 實作了上述的 requestImage
package com.android.gallery3d.data;
// LocalMediaItem is an abstract class captures those common fields
// in LocalImage and LocalVideo.
//
public abstract class LocalMediaItem extends MediaItem {
}
// LocalImage represents an image in the local storage.
public class LocalImage extends LocalMediaItem {
@Override
public Job<Bitmap> requestImage(int type) {
// 傳回加載Bitmap的Job工作任務,也就是ImageCacheRequest類
return new LocalImageRequest(mApplication, mPath, dateModifiedInSec,
type, filePath, mimeType);
}
}
- 上接
public static class LocalImageRequest extends ImageCacheRequest {
LocalImageRequest(GalleryApp application, Path path, long timeModified,
int type, String localFilePath, String mimeType) {
super(application, path, timeModified, type,
MediaItem.getTargetSize(type), localFilePath, mimeType);
mLocalFilePath = localFilePath;
}
2.2.2 ThreadPool.submit 線程池
private class AlbumCoverLoader extends BitmapLoader implements EntryUpdater {
...
@Override
protected Future<Bitmap> submitBitmapTask(FutureListener<Bitmap> l) {
//mMediaItem為LocalImage或LocalVideo,線程池
return mThreadPool.submit(mMediaItem.requestImage(
MediaItem.TYPE_MICROTHUMBNAIL), l);
}
...
}
-
- ThreadPool.submit
package com.android.gallery3d.util;
public class ThreadPool {
private final Executor mExecutor;
public ThreadPool(int initPoolSize, int maxPoolSize) {
mExecutor = new ThreadPoolExecutor(
initPoolSize, maxPoolSize, KEEP_ALIVE_TIME,
TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
new PriorityThreadFactory("thread-pool",
android.os.Process.THREAD_PRIORITY_BACKGROUND));
}
// Submit a job to the thread pool. The listener will be called when the
// job is finished (or cancelled).
public <T> Future<T> submit(Job<T> job, FutureListener<T> listener) {
Worker<T> w = new Worker<T>(job, listener);
mExecutor.execute(w);
return w;
}
}
-
- ThreadPool.run
package com.android.gallery3d.util;
public class ThreadPool {
private class Worker<T> implements Runnable, Future<T>, JobContext {
// This is called by a thread in the thread pool.
@Override
public void run() {
...
// 完成縮略圖加載任務後,回調到 AlbumCoverLoader 本身
if (mListener != null) mListener.onFutureDone(this);
}
}
- 檢視AlbumCoverLoader沒有onFutureDone回調方法,發現存在和BitmapLoader的繼承關系,即onFutureDone在BitmapLoader回調
private class AlbumCoverLoader extends BitmapLoader implements EntryUpdater {
-
- BitmapLoader.onFutureDone
package com.android.gallery3d.ui;
public abstract class BitmapLoader implements FutureListener<Bitmap> {
@Override
public void onFutureDone(Future<Bitmap> future) {
...
synchronized (this) {
...
// 縮略圖
mBitmap = future.get();
...
}
// 完成縮略圖
onLoadComplete(mBitmap);
}
}
-
- BitmapLoader.onLoadComplete
abstract protected void onLoadComplete(Bitmap bitmap);
-
- AlbumSetSlidingWindow.AlbumCoverLoader.onLoadComplete
package com.android.gallery3d.ui;
public class AlbumSetSlidingWindow implements AlbumSetDataLoader.DataListener {
private class AlbumCoverLoader extends BitmapLoader implements EntryUpdater {
...
@Override
protected void onLoadComplete(Bitmap bitmap) {
mHandler.obtainMessage(MSG_UPDATE_ALBUM_ENTRY, this).sendToTarget();
}
...
}
public AlbumSetSlidingWindow(AbstractGalleryActivity activity,
AlbumSetDataLoader source, AlbumSetSlotRenderer.LabelSpec labelSpec,
SlotView.Spec slotSpec, int cacheSize) {
mHandler = new SynchronizedHandler(activity.getGLRoot()) {
@Override
public void handleMessage(Message message) {
Utils.assertTrue(message.what == MSG_UPDATE_ALBUM_ENTRY);
((EntryUpdater) message.obj).updateEntry();
}
};
}
private static interface EntryUpdater {
public void updateEntry();
}
-
- 回調事件 AlbumSetSlidingWindow.AlbumCoverLoader.updateEntry
package com.android.gallery3d.ui;
public class AlbumSetSlidingWindow implements AlbumSetDataLoader.DataListener {
// 回調事件
private class AlbumCoverLoader extends BitmapLoader implements EntryUpdater {
@Override
public void updateEntry() {
// 擷取縮略圖
Bitmap bitmap = getBitmap();
if (bitmap == null) return; // error or recycled
AlbumSetEntry entry = mData[mSlotIndex % mData.length];
TiledTexture texture = new TiledTexture(bitmap);
entry.bitmapTexture = texture;
entry.content = texture;
if (isActiveSlot(mSlotIndex)) {
mContentUploader.addTexture(texture);
--mActiveRequestCount;
if (mActiveRequestCount == 0) requestNonactiveImages();
// packages/apps/Gallery/src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java:234: private class MyCacheListener implements AlbumSetSlidingWindow.Listener {
if (mListener != null) mListener.onContentChanged();
} else {
mContentUploader.addTexture(texture);
}
}
}
上述的 getBitmap() 方法
package com.android.gallery3d.ui;
public abstract class BitmapLoader implements FutureListener<Bitmap> {
@Override
public void onFutureDone(Future<Bitmap> future) {
...
mBitmap = future.get();
...
}
public synchronized Bitmap getBitmap() {
return mBitmap;
}
根據上述代碼可知,縮略圖加載是通過線程池來完成的。至于此處線程池的原理,submit之後會線上程池中執行任務加載縮略圖,從ThreadPool的run()方法中知道完成縮略圖加載任務後,會調用mListener.onFutureDone(this),這裡mListener就是BitmapLoader自身,從future.get中得到Bitmap傳給onLoadComplete(mBitmap)。 之後發送Handler消息MSG_UPDATE_ALBUM_ENTRY調用AlbumCoverLoader的updateEntry()。
-
- mListener.onContentChanged
package com.android.gallery3d.ui;
public class AlbumSetSlotRenderer extends AbstractSlotRenderer {
private class MyCacheListener implements AlbumSetSlidingWindow.Listener {
@Override
public void onSizeChanged(int size) {
mSlotView.setSlotCount(size);
}
@Override
public void onContentChanged() {
mSlotView.invalidate();
}
}
-
- SlotView 的 render 方法, 完成界面的渲染
package com.android.gallery3d.ui;
public class SlotView extends GLView {
@Override
protected void render(GLCanvas canvas) {
super.render(canvas);
...
int r = renderItem(canvas,
requestedSlot[i], pass, paperActive);
...
}
}