zoukankan      html  css  js  c++  java
  • Android 7.0 Gallery图库源码分析3

    前面分析Gallery启动流程时,说了传给DataManager的data的key是AlbumSetPage.KEY_MEDIA_PATH,value值,是”/combo/{/local/all,/picasa/all}”,下面分析具体怎么加载数据的。

    数据加载的准备阶段

    数据初始化是在AlbumSetPage的initializeData方法中。

     1 private void initializeData(Bundle data) {
     2         //mediaPath即为"/combo/{/local/all,/picasa/all}"
     3         String mediaPath = data.getString(AlbumSetPage.KEY_MEDIA_PATH);
     4         //获取MediaSet来管理一组媒体数据
     5         mMediaSet = mActivity.getDataManager().getMediaSet(mediaPath);
     6         /mSelectionManager用于管理选择事件
     7         mSelectionManager.setSourceMediaSet(mMediaSet);
     8         //mAlbumSetDataAdapter类似于桥梁来连接页面和数据源
     9         mAlbumSetDataAdapter = new AlbumSetDataLoader(
    10                 mActivity, mMediaSet, DATA_CACHE_SIZE);
    11         //设置数据加载的监听接口
    12         mAlbumSetDataAdapter.setLoadingListener(new MyLoadingListener());
    13         mAlbumSetView.setModel(mAlbumSetDataAdapter);
    14     }

    mActivity.getDataManager()就是获取Application(GalleryAppImpl)的DataManager,我们接着看getMediaSet方法,

     1 //根据路径获取MediaObject,s为"/combo/{/local/all,/picasa/all}"
     2 public MediaSet getMediaSet(String s) {
     3         return (MediaSet) getMediaObject(s);
     4     }
     5 
     6 public MediaObject getMediaObject(String s) {
     7         return getMediaObject(Path.fromString(s));
     8     }
     9 
    10 //进到PATH类中
    11 private WeakReference<MediaObject> mObject;
    12 private IdentityCache<String, Path> mChildren;
    13 
    14 public static Path fromString(String s) {
    15         synchronized (Path.class) {
    16             String[] segments = split(s);
    17             //segments为["combo", "{/local/all,/picasa/all}"]
    18             Path current = sRoot;
    19             for (int i = 0; i < segments.length; i++) {
    20                 current = current.getChild(segments[i]);
    21             }
    22             //经过for循环,current会持有两条路径,"combo"为父PATH,"{/local/all,/picasa/all}"为子PATH
    23             return current;
    24         }
    25     }
    26 
    27 //获取PATH对应得MediaObject
    28 public MediaObject getMediaObject(Path path) {
    29         synchronized (LOCK) {
    30             //根据PATH获取MediaObject,不为空直接返回
    31             MediaObject obj = path.getObject();
    32             if (obj != null) return obj;
    33 
    34             //根据PATH的前缀获取mSourceMap对应的MediaSource,mSourceMap初始化在源码分析2中讲过,这里返回的就是ComboSource
    35             MediaSource source = mSourceMap.get(path.getPrefix());
    36             ......
    37 
    38             try {
    39                 //走到这里说明MediaObject为空,所以需要创建MediaObject
    40                 MediaObject object = source.createMediaObject(path);
    41                 return object;
    42             ......
    43         }
    44     }

    我们接着看下ComboSource的createMediaObject方法

     1 public MediaObject createMediaObject(Path path) {
     2         //segments为["combo", "{/local/all,/picasa/all}"]
     3         String[] segments = path.split();
     4         ......
     5         //match结果为COMBO_ALBUMSET
     6         switch (mMatcher.match(path)) {
     7             //创建一个ComboAlbumSet并返回,dataManager.getMediaSetsFromString(segments[1])
             //这个方法就是根据"{/local/all,/picasa/all}"创建LocalSource实例和PicasaSource实例以及对应的LocalAlbumSet实例和EmptyAlbumSet实例,这个过程就是重复上述步骤
    8 case COMBO_ALBUMSET: 9 return new ComboAlbumSet(path, mApplication, 10 dataManager.getMediaSetsFromString(segments[1])); 11 ...... 12 }

    创建好后,最终返回给AlbumSetPage的initializeData方法中的mMediaSet

     1 private void initializeData(Bundle data) {
     2         ......
     3         //mMediaSet就是ComboAlbumSet,也就是数据源,它管理着LocalAlbumSet和EmptyAlbumSet
     4         mMediaSet = mActivity.getDataManager().getMediaSet(mediaPath);
     5 
     6         mSelectionManager.setSourceMediaSet(mMediaSet);
     7         //mAlbumSetDataAdapter类似于桥梁来连接页面和数据源
     8         mAlbumSetDataAdapter = new AlbumSetDataLoader(
     9                 mActivity, mMediaSet, DATA_CACHE_SIZE);
    10         mAlbumSetDataAdapter.setLoadingListener(new MyLoadingListener());
    11         将mAlbumSetDataAdapter传给界面显示的渲染器
    12         mAlbumSetView.setModel(mAlbumSetDataAdapter);
    13     }

    setModel这个方法挺重要的,它在AlbumSetSlotRenderer中,我们具体看一下

     1 public void setModel(AlbumSetDataLoader model) {
     2         ......
     3         if (model != null) {
     4             //根据model创建AlbumSetSlidingWindow,它是负责滑动显示图片的,比如解码专辑缩略图等
     5             mDataWindow = new AlbumSetSlidingWindow(
     6                     mActivity, model, mLabelSpec, CACHE_SIZE);
     7             //设置监听接口,处理尺寸改变或内容改变的事件
     8             mDataWindow.setListener(new MyCacheListener());
     9             mSlotView.setSlotCount(mDataWindow.size());
    10         }
    11     }

    到这里数据源和数据源适配器都创建好了,并且也传给了AlbumSetPage页面,这样数据加载的准备工作就做好了,也就是onCreate方法执行结束,下面分析onResume方法,这里完成数据的实际加载过程。

    数据加载过程

    首先查看GalleryActivity的OnResume方法,

    1 protected void onResume() {
    2         //调用其父类的OnResume方法
    3         super.onResume();
    4         }
    5     }

    我们接着查看AbstractGalleryActivity的的OnResume方法

     1 protected void onResume() {
     2         ......
     3         try {
     4             //数据加载的核心在这里
     5             getStateManager().resume(); 
     6             //这个方法只有LocalSource获取ContentProvider,别的都是什么操作都没有
     7             getDataManager().resume();
     8         } 
     9         mGLRootView.onResume();
    10         mOrientationManager.resume();
    11     }

    StateManager().resume的方法如下:

    1 public void resume() {
    2         //我们是从桌面图标进的应用,所以getTopState获取的是AlbumSetPage
    3         if (!mStack.isEmpty()) getTopState().resume();
    4     }

    我们看一下AlbumSetPage的resume方法,AlbumSetPage没有重写resume方法,所以调用的是其父类ActivityState的resume方法,我们先看一下

     1 void resume() {
     2         ......
     3         //这里就是调用AlbumSetPage的onResume方法
     4         onResume();
     5         ......
     6     }
     7 
     8     public void onResume() {
     9         ......
    10         //数据加载就是这一步完成的
    11         mAlbumSetDataAdapter.resume();
    12         ......

    前面讲了mAlbumSetDataAdapter是一个AlbumSetDataLoader类,所以我们去看AlbumSetDataLoader的resume方法

    1 public void resume() {
    2         //这个接口是数据变化的监听接口,当完成数据加载时会回调mSourceListener的onContentDirty方法
    3         mSource.addContentListener(mSourceListener);
    4         //ReloadTask就是完成数据加载任务的子线程
    5         mReloadTask = new ReloadTask();
    6         mReloadTask.start();
    7     }

    我们看一下ReloadTask的run方法

     1 public void run() {
     2     ......
     3     //这里执行数据加载
     4     long version = mSource.reload();
     5     ......
     6 }
     7 
     8 mSource是new AlbumSetDataLoader(
     9                 mActivity, mMediaSet, DATA_CACHE_SIZE)
    10 //传入的mMediaSet,前面讲了mMediaSet就是ComboAlbumSet,
    11 //它包含一个LocalAlbumSet和EmptyAlbumSet

    我们去ComboAlbumSet类中查看它的reload方法

    1  public long reload() {
    2         //mSets即为ComboAlbumSet所包含的LocalAlbumSet和EmptyAlbumSet,这里也就是分别调用LocalAlbumSet和EmptyAlbumSet的reload方法
    3         for (int i = 0, n = mSets.length; i < n; ++i) {
    4             long version = mSets[i].reload();
    5     ......

    因为EmptyAlbumSet的reload方法就是返回数据版本,所以暂且不管它。下面只分析LocalAlbumSet的reload方法。

    1 public synchronized long reload() {
    2         ......
    3         //通过ThreadPool线程池执行专辑数据的加载,AlbumsLoader方法看下面讲述
    4         mLoadTask = mApplication.getThreadPool().submit(new AlbumsLoader(), this);
    5         //这里就是对每个专辑进行数据加载,这之后的就不讲了
    6         for (MediaSet album : mAlbums) {
    7                 album.reload();
    8         }

    submit方法就是把job和listener封装成一个Worker,然后传给ThreadPoolExecutor执行

    1 public <T> Future<T> submit(Job<T> job, FutureListener<T> listener) {
    2         Worker<T> w = new Worker<T>(job, listener);
    3         mExecutor.execute(w);
    4         return w;
    5     }

    ThreadPoolExecutor的execute方法最终也是执行Worker的run方法,现在看下Worker的run方法

    1 public void run() {
    2     ......
    3     //mJob就是submit传进来的new AlbumsLoader()        
    4     result = mJob.run(this);
    5     ......
    6     //mListener是FutureListener接口,这里也就是LocalAlbumSet自身
    7     if (mListener != null) mListener.onFutureDone(this);
    8 }

    接着看下AlbumsLoader的run方法,这里主要是获取专辑的信息

     1 private class AlbumsLoader implements ThreadPool.Job<ArrayList<MediaSet>> {
     2 
     3         @Override
     4         @SuppressWarnings("unchecked")
     5         public ArrayList<MediaSet> run(JobContext jc) {
     6             ......
     7             //通过BucketHelper获取所有的专辑信息
     8             BucketEntry[] entries = BucketHelper.loadBucketEntries(
     9                     jc, mApplication.getContentResolver(), mType);
    10         ......
    11         for (BucketEntry entry : entries) {
    12                 //获取LocalAlbum并保存到ArrayList(albums)中,albums对应于每个专辑
    13                 MediaSet album = getLocalAlbum(dataManager,
    14                         mType, mPath, entry.bucketId, entry.bucketName);
    15                 albums.add(album);
    16             }

    当AlbumsLoader的run方法执行完后,接着执行mListener.onFutureDone回调接口,通过这个接口通知MediaSet内容有变化。最终会走到AlbumSetDataLoader的onContentDirty方法

    1 public void onContentDirty() {
    2             //这个方法会唤醒所以wait的线程
    3             mReloadTask.notifyDirty();
    4         }

    现在我们回到AlbumSetDataLoader的ReloadTask中,reload方法执行之后会通过updateLoading发送MSG_LOAD_FINISH消息

     1 while (mActive) {
     2     ......
     3     //这一块很重要,用来更新界面的
     4     //获取需要更新的数据信息,包括专辑数量等,这里我不细讲了,自己看代码
     5     UpdateInfo info = executeAndWait(new GetUpdateInfo(version));
     6     ......
     7     //根据数据信息更新界面,这个方法最终会执行UpdateContent的call方法
     8     executeAndWait(new UpdateContent(info));
     9 }
    10 
    11 //这个方法发送 MSG_LOAD_FINISH消息通知数据加载完成,这里不细讲了
    12 updateLoading(false);

    更新界面

    1 private class UpdateContent implements Callable<Void> {
    2     public Void call() {
    3         //这里是更新Slot数目
    4         if (mDataListener != null) mDataListener.onSizeChanged(mSize);
    5         ......
    6         //更新内容
    7         mDataListener.onContentChanged(info.index);
    8     }
    9 }

    mDataListener是实例化AlbumSetSlidingWindow是设置的,也就是AlbumSetSlidingWindow自身

    source.setModelListener(this);

    接着看AlbumSetSlidingWindow的onSizeChanged和onContentChanged方法

     1 public void onSizeChanged(int size) {
     2         if (mIsActive && mSize != size) {
     3             mSize = size;
     4             //mListener是AlbumSetSlotRenderer的,MyCacheListener,onSizeChanged就是执行mSlotView.setSlotCount(size)
     5             if (mListener != null) mListener.onSizeChanged(mSize);
     6             if (mContentEnd > mSize) mContentEnd = mSize;
     7             if (mActiveEnd > mSize) mActiveEnd = mSize;
     8         }
     9     }
    10 
    11     public void onContentChanged(int index) {
    12         //更新图像
    13         AlbumSetEntry entry = mData[index % mData.length];
    14         updateAlbumSetEntry(entry, index);
    15         updateAllImageRequests();
    16         updateTextureUploadQueue();
    17         //onContentChanged方法就是执行mSlotView.invalidate()刷新界面
    18         if (mListener != null && isActiveSlot(index)) {
    19             mListener.onContentChanged();
    20         }
    21     }

    到这里就完成了SlotView的渲染准备工作,至于怎么渲染到屏幕上见Gallery图库源码分析6

  • 相关阅读:
    8-16 不无聊序列 non-boring sequences uva1608
    8-8 Ddfense Line uva1471 优先级队列
    8-12 Erratic Expansion uva12627
    8-4 Fabled Rooks uva11134
    8-10 Coping Books uva714
    8-13 Just Finish it up uva11093
    8-11 Add All uva 10954
    8-2 Building for UN Uva1605
    css定位.py
    处理HTML5视频播放.py
  • 原文地址:https://www.cnblogs.com/zhao-shan/p/9838401.html
Copyright © 2011-2022 走看看