青春须早为,岂能长少年。这篇文章主要讲述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}" 3String mediaPath = data.getString(AlbumSetPage.KEY_MEDIA_PATH); 4//获取MediaSet来管理一组媒体数据 5mMediaSet = mActivity.getDataManager().getMediaSet(mediaPath); 6/mSelectionManager用于管理选择事件 7mSelectionManager.setSourceMediaSet(mMediaSet); 8//mAlbumSetDataAdapter类似于桥梁来连接页面和数据源 9mAlbumSetDataAdapter = new AlbumSetDataLoader( 10mActivity, mMediaSet, DATA_CACHE_SIZE); 11//设置数据加载的监听接口 12mAlbumSetDataAdapter.setLoadingListener(new MyLoadingListener()); 13mAlbumSetView.setModel(mAlbumSetDataAdapter); 14}
mActivity.getDataManager()就是获取Application(GalleryAppImpl)的DataManager,我们接着看getMediaSet方法,
1 //根据路径获取MediaObject,s为"/combo/{/local/all,/picasa/all}" 2 public MediaSet getMediaSet(String s) { 3return (MediaSet) getMediaObject(s); 4} 5 6 public MediaObject getMediaObject(String s) { 7return 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) { 15synchronized (Path.class) { 16String[] segments = split(s); 17//segments为["combo", "{/local/all,/picasa/all}"] 18Path current = sRoot; 19for (int i = 0; i < segments.length; i++) { 20current = current.getChild(segments[i]); 21} 22//经过for循环,current会持有两条路径,"combo"为父PATH,"{/local/all,/picasa/all}"为子PATH 23return current; 24} 25} 26 27 //获取PATH对应得MediaObject 28 public MediaObject getMediaObject(Path path) { 29synchronized (LOCK) { 30//根据PATH获取MediaObject,不为空直接返回 31MediaObject obj = path.getObject(); 32if (obj != null) return obj; 33 34//根据PATH的前缀获取mSourceMap对应的MediaSource,mSourceMap初始化在源码分析2中讲过,这里返回的就是ComboSource 35MediaSource source = mSourceMap.get(path.getPrefix()); 36...... 37 38try { 39//走到这里说明MediaObject为空,所以需要创建MediaObject 40MediaObject object = source.createMediaObject(path); 41return object; 42...... 43} 44}
我们接着看下ComboSource的createMediaObject方法
1 public MediaObject createMediaObject(Path path) { 2//segments为["combo", "{/local/all,/picasa/all}"] 3String[] segments = path.split(); 4...... 5//match结果为COMBO_ALBUMSET 6switch (mMatcher.match(path)) { 7//创建一个ComboAlbumSet并返回,dataManager.getMediaSetsFromString(segments[1])
//这个方法就是根据"{/local/all,/picasa/all}"创建LocalSource实例和PicasaSource实例以及对应的LocalAlbumSet实例和EmptyAlbumSet实例,这个过程就是重复上述步骤 8case COMBO_ALBUMSET: 9return new ComboAlbumSet(path, mApplication, 10dataManager.getMediaSetsFromString(segments[1])); 11...... 12}
创建好后,最终返回给AlbumSetPage的initializeData方法中的mMediaSet
1 private void initializeData(Bundle data) { 2...... 3//mMediaSet就是ComboAlbumSet,也就是数据源,它管理着LocalAlbumSet和EmptyAlbumSet 4mMediaSet = mActivity.getDataManager().getMediaSet(mediaPath); 5 6mSelectionManager.setSourceMediaSet(mMediaSet); 7//mAlbumSetDataAdapter类似于桥梁来连接页面和数据源 8mAlbumSetDataAdapter = new AlbumSetDataLoader( 9mActivity, mMediaSet, DATA_CACHE_SIZE); 10mAlbumSetDataAdapter.setLoadingListener(new MyLoadingListener()); 11将mAlbumSetDataAdapter传给界面显示的渲染器 12mAlbumSetView.setModel(mAlbumSetDataAdapter); 13}
setModel这个方法挺重要的,它在AlbumSetSlotRenderer中,我们具体看一下
1 public void setModel(AlbumSetDataLoader model) { 2...... 3if (model != null) { 4//根据model创建AlbumSetSlidingWindow,它是负责滑动显示图片的,比如解码专辑缩略图等 5mDataWindow = new AlbumSetSlidingWindow( 6mActivity, model, mLabelSpec, CACHE_SIZE); 7//设置监听接口,处理尺寸改变或内容改变的事件 8mDataWindow.setListener(new MyCacheListener()); 9mSlotView.setSlotCount(mDataWindow.size()); 10} 11}
到这里数据源和数据源适配器都创建好了,并且也传给了AlbumSetPage页面,这样数据加载的准备工作就做好了,也就是onCreate方法执行结束,下面分析onResume方法,这里完成数据的实际加载过程。
数据加载过程首先查看GalleryActivity的OnResume方法,
1 protected void onResume() { 2//调用其父类的OnResume方法 3super.onResume(); 4} 5}
我们接着查看AbstractGalleryActivity的的OnResume方法
1 protected void onResume() { 2...... 3try { 4//数据加载的核心在这里 5getStateManager().resume(); 6//这个方法只有LocalSource获取ContentProvider,别的都是什么操作都没有 7getDataManager().resume(); 8} 9mGLRootView.onResume(); 10mOrientationManager.resume(); 11}
StateManager().resume的方法如下:
1 public void resume() { 2//我们是从桌面图标进的应用,所以getTopState获取的是AlbumSetPage 3if (!mStack.isEmpty()) getTopState().resume(); 4}
我们看一下AlbumSetPage的resume方法,AlbumSetPage没有重写resume方法,所以调用的是其父类ActivityState的resume方法,我们先看一下
1 void resume() { 2...... 3//这里就是调用AlbumSetPage的onResume方法 4onResume(); 5...... 6} 7 8public void onResume() { 9...... 10//数据加载就是这一步完成的 11mAlbumSetDataAdapter.resume(); 12......
前面讲了mAlbumSetDataAdapter是一个AlbumSetDataLoader类,所以我们去看AlbumSetDataLoader的resume方法
1 public void resume() { 2//这个接口是数据变化的监听接口,当完成数据加载时会回调mSourceListener的onContentDirty方法 3mSource.addContentListener(mSourceListener); 4//ReloadTask就是完成数据加载任务的子线程 5mReloadTask = new ReloadTask(); 6mReloadTask.start(); 7}
【Android 7.0 Gallery图库源码分析3 - 数据加载及显示流程】我们看一下ReloadTask的run方法
1 public void run() { 2...... 3//这里执行数据加载 4long version = mSource.reload(); 5...... 6 } 7 8 mSource是new AlbumSetDataLoader( 9mActivity, mMediaSet, DATA_CACHE_SIZE) 10 //传入的mMediaSet,前面讲了mMediaSet就是ComboAlbumSet, 11 //它包含一个LocalAlbumSet和EmptyAlbumSet
我们去ComboAlbumSet类中查看它的reload方法
1public long reload() { 2//mSets即为ComboAlbumSet所包含的LocalAlbumSet和EmptyAlbumSet,这里也就是分别调用LocalAlbumSet和EmptyAlbumSet的reload方法 3for (int i = 0, n = mSets.length; i < n; ++i) { 4long version = mSets[i].reload(); 5......
因为EmptyAlbumSet的reload方法就是返回数据版本,所以暂且不管它。下面只分析LocalAlbumSet的reload方法。
1 public synchronized long reload() { 2...... 3//通过ThreadPool线程池执行专辑数据的加载,AlbumsLoader方法看下面讲述 4mLoadTask = mApplication.getThreadPool().submit(new AlbumsLoader(), this); 5//这里就是对每个专辑进行数据加载,这之后的就不讲了 6for (MediaSet album : mAlbums) { 7album.reload(); 8}
submit方法就是把job和listener封装成一个Worker,然后传给ThreadPoolExecutor执行
1 public < T> Future< T> submit(Job< T> job, FutureListener< T> listener) { 2Worker< T> w = new Worker< T> (job, listener); 3mExecutor.execute(w); 4return w; 5}
ThreadPoolExecutor的execute方法最终也是执行Worker的run方法,现在看下Worker的run方法
1 public void run() { 2...... 3//mJob就是submit传进来的new AlbumsLoader() 4result = mJob.run(this); 5...... 6//mListener是FutureListener接口,这里也就是LocalAlbumSet自身 7if (mListener != null) mListener.onFutureDone(this); 8 }
接着看下AlbumsLoader的run方法,这里主要是获取专辑的信息
1 private class AlbumsLoader implements ThreadPool.Job< ArrayList< MediaSet> > { 2 3@Override 4@SuppressWarnings("unchecked") 5public ArrayList< MediaSet> run(JobContext jc) { 6...... 7//通过BucketHelper获取所有的专辑信息 8BucketEntry[] entries = BucketHelper.loadBucketEntries( 9jc, mApplication.getContentResolver(), mType); 10...... 11for (BucketEntry entry : entries) { 12//获取LocalAlbum并保存到ArrayList(albums)中,albums对应于每个专辑 13MediaSet album = getLocalAlbum(dataManager, 14mType, mPath, entry.bucketId, entry.bucketName); 15albums.add(album); 16}
当AlbumsLoader的run方法执行完后,接着执行mListener.onFutureDone回调接口,通过这个接口通知MediaSet内容有变化。最终会走到AlbumSetDataLoader的onContentDirty方法
1 public void onContentDirty() { 2//这个方法会唤醒所以wait的线程 3mReloadTask.notifyDirty(); 4}
现在我们回到AlbumSetDataLoader的ReloadTask中,reload方法执行之后会通过updateLoading发送MSG_LOAD_FINISH消息
1 while (mActive) { 2...... 3//这一块很重要,用来更新界面的 4//获取需要更新的数据信息,包括专辑数量等,这里我不细讲了,自己看代码 5UpdateInfo info = executeAndWait(new GetUpdateInfo(version)); 6...... 7//根据数据信息更新界面,这个方法最终会执行UpdateContent的call方法 8executeAndWait(new UpdateContent(info)); 9 } 10 11 //这个方法发送 MSG_LOAD_FINISH消息通知数据加载完成,这里不细讲了 12 updateLoading(false);
更新界面
1 private class UpdateContent implements Callable< Void> { 2public Void call() { 3//这里是更新Slot数目 4if (mDataListener != null) mDataListener.onSizeChanged(mSize); 5...... 6//更新内容 7mDataListener.onContentChanged(info.index); 8} 9 }
mDataListener是实例化AlbumSetSlidingWindow是设置的,也就是AlbumSetSlidingWindow自身
source.setModelListener(this);
接着看AlbumSetSlidingWindow的onSizeChanged和onContentChanged方法
1 public void onSizeChanged(int size) { 2if (mIsActive & & mSize != size) { 3mSize = size; 4//mListener是AlbumSetSlotRenderer的,MyCacheListener,onSizeChanged就是执行mSlotView.setSlotCount(size) 5if (mListener != null) mListener.onSizeChanged(mSize); 6if (mContentEnd > mSize) mContentEnd = mSize; 7if (mActiveEnd > mSize) mActiveEnd = mSize; 8} 9} 10 11public void onContentChanged(int index) { 12//更新图像 13AlbumSetEntry entry = mData[index % mData.length]; 14updateAlbumSetEntry(entry, index); 15updateAllImageRequests(); 16updateTextureUploadQueue(); 17//onContentChanged方法就是执行mSlotView.invalidate()刷新界面 18if (mListener != null & & isActiveSlot(index)) { 19mListener.onContentChanged(); 20} 21}
到这里就完成了SlotView的渲染准备工作,至于怎么渲染到屏幕上见Gallery图库源码分析6。
推荐阅读
- Android 7.0 Gallery图库源码分析2 - 分析启动流程
- Android 7.0 Gallery图库源码分析4 - SlotView手势监听及页面跳转
- Android 7.0 Gallery图库源码分析1 - 初识Gallery源码
- App压力测试MonkeyRunner整理
- rhel6.5搭建mantis时报错“INTERNAL APPLICATION ERROR”
- Qt Widgets Application可执行程序发布方式
- spring boot application.properties文件外部配置
- 微信空气质量如何查询?微信空气质量查询办法介绍_微信
- 微信邀请函怎样做?微信邀请函自制办法列表_微信