天天看点

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

}
           

继续阅读