天天看點

06 Gallery 源碼-相冊集AlbumCoverLoader 加載專輯縮略圖

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);
        }
        ...
    }
           
    1. 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;
    }

}

           
    1. 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 {

           
    1. 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);
    }

}

           
    1. BitmapLoader.onLoadComplete
abstract protected void onLoadComplete(Bitmap bitmap);

           
    1. 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();
    }

           
    1. 回調事件 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()。

    1. 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();
        }
    }
           
    1. 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);
        ...
    }

}
           

繼續閱讀