12 Gallery 源码-AlbumSetPage 数据加载和渲染过程分析

0. 原文拜读

https://blog.csdn.net/lgglkk/article/details/54918441

1. AlbumSetPage 界面

AlbumSetPage 是我们进入Gallery2后的第一个页面,作用是显示所有相册专辑。其结构主要由一个 SlotView 和Slot组成(如下图)。一个 Slot 代表一个相册专辑。Slot中包含的内容有:一张封面图片(一般是相册中首张图片资源),相册标题,相册大小等,如果这是一个特殊的相册,例如是一个保存Camera拍照后的相册,在相册封面上还会有Camera图标表示。

package com.android.gallery3d.app;

public class AlbumSetPage extends ActivityState implements OnClickListener,..{
    private SlotView mSlotView;

}

package com.android.gallery3d.ui;

public class SlotView extends GLView {
    ...
}

资源图片.png

2. AlbumSetPage.initializeViews

初始化 mConfig 获得有关 AlbumSetPage 的运行参数,包括 Slot 的组成元素的颜色,初始化每个 Slot 在 SlotView 中的大小,间隔等参数;

初始化 AlbumSetSlotRenderer,SlotView 的渲染器,实例名为 mAlbumSetView;

根据 mConfig 和 AlbumSetSlotRenderer 初始化 mSlotView,并给 mSlotView 注册事件监听; 初始化 mActionModeHandler 处理 ActionBar 事件;

最终将 SlotView 添加到 RootPane 中;

    private void initializeViews() {
        // mActivity -> mFragment
        if (null != mFragment && mFragment.isAdded()) {
            ...
        } else {
            ...

            //mConfig是用来设置SlotView的参数,而SlotView就是一个相册
            mConfig = Config.AlbumSetPage.get(mActivity);
            mSlotView = new SlotView(mActivity, mConfig.slotViewSpec);

            //mAlbumSetView是mSlotView的渲染器,控制mSlotView的显示
            mAlbumSetView = new AlbumSetSlotRenderer(
                    mActivity, mSelectionManager, mSlotView, mConfig.labelSpec,
                    mConfig.slotViewSpec,
                    mConfig.placeholderColor);

            //将mAlbumSetView设置给mSlotView        
            mSlotView.setSlotRenderer(mAlbumSetView);
            
            //监听SlotView事件,根据手势操作做出相应的响应,这个监听事件的原理后面再分析
            mSlotView.setListener(new SlotView.SimpleListener() {
                ...
            });

            mActionModeHandler = new ActionModeHandler(mActivity, mSelectionManager);
            mActionModeHandler.setActionModeListener(new ActionModeListener() {
                @Override
                public boolean onActionItemClicked(MenuItem item) {
                    return onItemSelected(item);
                }
            });
            
            //把这个SlotView作为一个子控件传给GLView,mRootPane是GLView类
            mRootPane.addComponent(mSlotView);
        }
    }

资源图片.png

3. AlbumSetPage.initializeData 目的是实例化数据适配器,并为渲染器提供数据接口

获得 GalleryActivity 传过来的 mediaPath,并通过 DataManager 的 getMediaSet 方法获得对应的数据源,由于传过来的 mediaPath 为
/combo/{/local/all,/picasa/all},因此得到的是一个 ComboSource 数据源,此数据源另外包含一个 LocalSource 和 EmptySource,后面这 个mMediaSet 所有的实现都可以在 ComboAlbumSet 中找到;

根据获得的数据源(mediaSet)来实例化名为 mAlbumSetDataAdapter 的 AlbumSetDataLoader 对象,作为数据适配器。数据源和适配 器都准备完毕后,调用 setMode 方法将数据适配器添加到 SlotView 的渲染器 mAlbumSetView 中;

    public static final String KEY_MEDIA_PATH = "media-path";

    private void initializeData(Bundle data) {
        // 获取data传入的value
        // /combo/{/local/all,/picasa/all}
        String mediaPath = data.getString(AlbumSetPage.KEY_MEDIA_PATH);

        //获取MediaSet,前面讲了每个ActivityState页面都需要一个数据源MediaSource来获取显示数据,而MediaSource是由MediaObject组成,MediaObject相当于MediaSource的单位,MediaSet就是一个MediaObject的子类,管理一组媒体数据
        //获取MediaSet来管理一组媒体数据
        mMediaSet = getDataManager().getMediaSet(mediaPath);

        //mSelectionManager用于管理选择事件
        mSelectionManager.setSourceMediaSet(mMediaSet);

        //mAlbumSetDataAdapter类似于桥梁来连接页面和数据源
        // 装载数据源
        // mActivity -> mFragment
        if (null != mFragment && mFragment.isAdded()) {
            mAlbumSetDataAdapter = new AlbumSetDataLoader(
                    mFragment, mMediaSet, DATA_CACHE_SIZE);
        } else {
            mAlbumSetDataAdapter = new AlbumSetDataLoader(
                    mActivity, mMediaSet, DATA_CACHE_SIZE);
        }
        
        mSelectionManager.setAlbumSetDataLoader(mAlbumSetDataAdapter);
        
        //设置数据加载的监听接口
        mAlbumSetDataAdapter.setLoadingListener(new MyLoadingListener());
        // 将数据适配器添加到 SlotView 的渲染器 mAlbumSetView 中
        mAlbumSetView.setModel(mAlbumSetDataAdapter);
    }

4. 调用数据适配器加载媒体数据,为后面渲染提供数据支持

AlbumSetPage 的 onCreate 方法执行完后,在随后执行 onResume 方法中,首先调用数据适配器(AlbumSetDataLoader)的 resume 方法开始执行数据加载操作。AlbumSetDataLoader 开启一个 ReloadTask 执行数据源 reload 工作。通过层层的 reload,并调用 BucketHelper 查询 MediaProvider 数据库,获得专辑数据并返回。

ReloadTask 的主要功能是循环调用数据源的 reload 方法,进而更新获得专辑数据,一旦获得专辑数量变化后,通过监听器回调的 方式通知 SlotView,告知专辑数,创建相应数量的 slot,这也是我们平时看到的专辑。

package com.android.gallery3d.app;

public class AlbumSetPage extends ActivityState implements OnClickListener,
        SelectionManager.SelectionListener, GalleryActionBar.ClusterRunner,
        EyePosition.EyePositionListener, MediaSet.SyncListener, ButtonControlsHandler.Delegate {

    private AlbumSetDataLoader mAlbumSetDataAdapter;

    @Override
    public void onResume() {
        super.onResume();
        ...
        //数据加载就是这一步完成的
        mAlbumSetDataAdapter.resume();
        ...
    }
    
    public void resume() {
        //这个接口是数据变化的监听接口,当完成数据加载时会回调mSourceListener的onContentDirty方法
        mSource.addContentListener(mSourceListener);
        //ReloadTask就是完成数据加载任务的子线程,将onCreate中initData的数据加载上
        mReloadTask = new ReloadTask();
        mReloadTask.start();
    }
    
    // TODO: load active range first
    private class ReloadTask extends Thread {
    
        // 上述 getDataManager().getMediaSet(path) 中获取
        private final MediaSet mSource;
    
        @Override
        public void run() {
            ......
            //这里执行数据加载.多态
            long version = mSource.reload();
            ......
        }
    }
    
package com.android.gallery3d.data;

public class LocalAlbumSet extends MediaSet
        implements FutureListener<ArrayList<MediaSet>> {
        
    @Override
    // synchronized on this function for
    //   1. Prevent calling reload() concurrently.
    //   2. Prevent calling onFutureDone() and reload() concurrently
    public synchronized long reload() {
        if (mNotifier.isDirty()) {
            if (mLoadTask != null) mLoadTask.cancel();
            mIsLoading = true;

            //通过ThreadPool线程池执行专辑数据的加载
            mLoadTask = mApplication.getThreadPool().submit(new AlbumsLoader(), this);
        }
        if (mLoadBuffer != null) {
            // mLoadBuffer就是相册的集合,是AlbumsLoader这个callable执行结束后,在//onFutureDone()方法里,通过mLoadBuffer = future.get()返回的。
            mAlbums = mLoadBuffer;
            mLoadBuffer = null;
            //这里就是对每个专辑进行数据加载
            for (MediaSet album : mAlbums) {
                album.reload();
            }
            mDataVersion = nextVersionNumber();
        }
        return mDataVersion;
    }

资源图片.png

5. 获得 AlbumSet 数据后,渲染器开始渲染 SlotView

数据适配器 AlbumSetDataLoader 中的 ReloadTask 执行完后,发送 MSG_LOAD_FINISH 广播通知到 AlbumSetPage 更新界面,并完成

package com.android.gallery3d.app;

public class AlbumSetDataLoader {

    @Override
    public void run() {
        while (mActive) {
            ......
            //这一块很重要,用来更新界面的
            //获取需要更新的数据信息,包括专辑数量等
            UpdateInfo info = executeAndWait(new GetUpdateInfo(version));
            ......
            //根据数据信息更新界面,这个方法最终会执行UpdateContent的call方法
            executeAndWait(new UpdateContent(info));
            .....
        }

        // 这个方法发送 MSG_LOAD_FINISH消息通知数据加载完成
        updateLoading(false);
    }
    
    private LoadingListener mLoadingListener;

    case MSG_LOAD_FINISH:
        if (mLoadingListener != null) mLoadingListener.onLoadingFinished(false);
        return;
        
package com.android.gallery3d.app;

public class AlbumSetDataLoader {

    @Override
    public void run() {
        ...
        //根据数据信息更新界面,这个方法最终会执行UpdateContent的call方法
        executeAndWait(new UpdateContent(info));
        ...
        
    private class UpdateContent implements Callable<Void> {
        private final UpdateInfo mUpdateInfo;

        @Override
        public Void call() {
            ...
            for (DataListener l : mDataListener) {
                //更新内容
                l.onContentChanged(info.index);
            }
            return null;
        }
    }
    
    @Override
    public void onContentChanged(int index) {
        if (!mIsActive) {
            // paused, ignore slot changed event
            return;
        }

        // If the updated content is not cached, ignore it
        if (index < mContentStart || index >= mContentEnd) {
            Log.w(TAG, String.format(
                    "invalid update: %s is outside (%s, %s)",
                    index, mContentStart, mContentEnd) );
            return;
        }

        // 更新图像
        AlbumSetEntry entry = mData[index % mData.length];
        updateAlbumSetEntry(entry, index);
        updateAllImageRequests();
        updateTextureUploadQueue();
        if (mListener != null && isActiveSlot(index)) {
            //onContentChanged方法就是执行mSlotView.invalidate()刷新界面
            mListener.onContentChanged();
    }

资源图片.png

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值