[转]Android平台Gallery2应用分析(七)---PhotoPage图片解码

PhotoPage图片解码

从前文可知,PhotoPage的图片解码始于PhotoPage的onResume()调用updateImageRequests()。先看下代码:

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. private void updateImageRequests() {  
  2.     ……  
  3.     int currentIndex = mCurrentIndex;  
  4.     MediaItem item = mData[currentIndex % DATA_CACHE_SIZE];  
  5.     ……  
  6.     // 1. 遍历sImageFetchSeq,查看当前图片符合哪种类型,调用startTaskIfNeeded  
  7.     Future<?> task = null;  
  8.     for (int i = 0; i < sImageFetchSeq.length; i++) {  
  9.         int offset = sImageFetchSeq[i].indexOffset;  
  10.         int bit = sImageFetchSeq[i].imageBit;  
  11.         if (bit == BIT_FULL_IMAGE && !mNeedFullImage) continue;  
  12.         task = startTaskIfNeeded(currentIndex + offset, bit);  
  13.         if (task != null) break;  
  14.     }  
  15.   
  16.     // 2. 释放任务和内存  
  17.     for (ImageEntry entry : mImageCache.values()) {  
  18.         if (entry.screenNailTask != null && entry.screenNailTask != task) {  
  19.             entry.screenNailTask.cancel();  
  20.             entry.screenNailTask = null;  
  21.             entry.requestedScreenNail = MediaObject.INVALID_DATA_VERSION;  
  22.         }  
  23.         if (entry.fullImageTask != null && entry.fullImageTask != task) {  
  24.             entry.fullImageTask.cancel();  
  25.             entry.fullImageTask = null;  
  26.             entry.requestedFullImage = MediaObject.INVALID_DATA_VERSION;  
  27.         }  
  28.     }  

 

接下来,重点分析startTaskIfNeeded(),看它是如何对一张图片做解析的。还是先看下面代码:

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. private Future<?> startTaskIfNeeded(int index, int which) {  
  2.     if (index < mActiveStart || index >= mActiveEnd) return null;  
  3.   
  4.     ImageEntry entry = mImageCache.get(getPath(index));  
  5.     if (entry == null) return null;  
  6.     // 先得到当前图片,类型为LocalImage  
  7.     MediaItem item = mData[index % DATA_CACHE_SIZE];  
  8.     Utils.assertTrue(item != null);  
  9.     long version = item.getDataVersion();  
  10.       
  11.     // 第一次代码执行暂时screenNailTask和fullImageTask为null  
  12.     if (which == BIT_SCREEN_NAIL && entry.screenNailTask != null  
  13.             && entry.requestedScreenNail == version) {  
  14.         return entry.screenNailTask;  
  15.     } else if (which == BIT_FULL_IMAGE && entry.fullImageTask != null  
  16.             && entry.requestedFullImage == version) {  
  17.         return entry.fullImageTask;  
  18.     }  
  19.     // 匹配到格式后,先创建ScreenNailJob、ScreenNailListener并返回screenNailTask  
  20.     if (which == BIT_SCREEN_NAIL && entry.requestedScreenNail != version) {  
  21.         entry.requestedScreenNail = version;  
  22.         entry.screenNailTask = mThreadPool.submit(  
  23.                 new ScreenNailJob(item),  
  24.                 new ScreenNailListener(item));  
  25.         // request screen nail  
  26.         return entry.screenNailTask;  
  27.     }  
  28. 配到格式后,先创建FullImageJob、FullImageListener并返回fullImageTask  
  29.     if (which == BIT_FULL_IMAGE && entry.requestedFullImage != version  
  30.             && (item.getSupportedOperations()  
  31.             & MediaItem.SUPPORT_FULL_IMAGE) != 0) {  
  32.         entry.requestedFullImage = version;  
  33.         entry.fullImageTask = mThreadPool.submit(  
  34.                 new FullImageJob(item),  
  35.                 new FullImageListener(item));  
  36.         // request full image  
  37.         return entry.fullImageTask;  
  38.     }  
  39.     return null;  

参数which就是静态数组sImageFetchSeq中的图片类型,Android原生代码默认两种BIT_SCREEN_NAIL和BIT_FULL_IMAGE。当然我们也可以自己加入一种解析图片的格式,例如BIT_GIF_IMAGE。在详细分析下面的代码后,详细你自己加入一种图片格式应该问题不大。流程大致如下:

1) 从传入参数index获取当前图片MediaItem,图片为LocalImage类型。第一次执行时screenNailTask和fullImageTask为null,匹配到格式后,创建ScreenNailJob、ScreenNailListener(或者screenNailTask和fullImageTask为null),并最终返回screenNailTask或者fullImageTask。

2) 以FullImage为例。ThreadPool的机制这里再大致讲述一下。看下mThreadPool.submit的代码:

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  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. }  

由前文分析,execute会启动Worker的线程进入run()函数:

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. @Override  
  2.  public void run() {  
  3.      T result = null;  
  4.   
  5.      // A job is in CPU mode by default. setMode returns false  
  6.      // if the job is cancelled.  
  7.      if (setMode(MODE_CPU)) {  
  8.          try {  
  9.              // 1、执行job的run()  
  10.              result = mJob.run(this);  
  11.          } catch (Throwable ex) {  
  12.              Log.w(TAG, "Exception in running a job", ex);  
  13.          }  
  14.      }  
  15.   
  16.      synchronized(this) {  
  17.          setMode(MODE_NONE);  
  18.          mResult = result;  
  19.          mIsDone = true;  
  20.          notifyAll();  
  21.      }  
  22.      // 2、执行完毕,调用listener的onFutureDone  
  23.      if (mListener != null) mListener.onFutureDone(this);  
  24.  }  

这里面分两步:

        2.1)执行job的run()。这里会调用传入参数new FullImageJob (item).run()。

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. private class FullImageJob implements Job<BitmapRegionDecoder> {  
  2.     private MediaItem mItem;  
  3.   
  4.     public FullImageJob(MediaItem item) {  
  5.         mItem = item;  
  6.     }  
  7.   
  8.     @Override  
  9.     public BitmapRegionDecoder run(JobContext jc) {  
  10.         if (isTemporaryItem(mItem)) {  
  11.             return null;  
  12.         }  
  13.         return mItem.requestLargeImage().run(jc);  
  14.     }  
  15. }  

这段代码实际就是在线程池的某个线程中执行LocalImage的requestLargeImage().run(jc)。而该函数会创建一个LocalLargeImageRequest对象,而run(jc)实际就是DecodeUtils.createBitmapRegionDecoder(jc, mLocalFilePath, false),最终创建一个BitmapRegionDecoder实例。该实例会调用JNI层的BitmapRegionDecoder中的nativeNewInstanceFromStream接口,在doBuildTileIndex(JNIEnv* env, SkStream* stream)里可以看到,图片会根据stream的header判断解码器是SkJPEGImageDecoder,SkPNGImageDecoder,SkBMPImageDecoder还是SkWEBPImageDecoder等,最后得到解码数据。

        2.2) 调用FullImageListener的onFutureDone。此时会将2.1)中创建好的BitmapRegionDecoder实例返回传给mFuture。而FullImageListener则再发送一个MSG_RUN_OBJECT消息给MainThread,MainThread再执行FullImageListener的run(),即再执行updateFullImage(mPath, mFuture)。

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1.     private void updateFullImage(Path path, Future<BitmapRegionDecoder> future) {  
  2.         ImageEntry entry = mImageCache.get(path);  
  3.         ……  
  4.         entry.fullImageTask = null;  
  5.         entry.fullImage = future.get();  
  6.         if (entry.fullImage != null) {  
  7.             if (path == getPath(mCurrentIndex)) {  
  8.                 updateTileProvider(entry);  
  9.                 mPhotoView.notifyImageChange(0);  
  10.             }  
  11.         }  
  12.         updateImageRequests();  
  13. }  

 

其中mPhotoView.notifyImageChange(0)取到当前图片后并reload,其中mPictures当前为FullPicture。

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public void notifyImageChange(int index) {  
  2.     ……  
  3.     mPictures.get(index).reload();  
  4.     ……  
  5.     invalidate();  

那么再看看FullPicture的reload(), 该函数会对mTileView的screenNail做更新。

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. @Override  
  2. public void reload() {  
  3.     // mImageWidth and mImageHeight will get updated  
  4.     mTileView.notifyModelInvalidated();  
  5.     ……  
  6.     setScreenNail(mModel.getScreenNail(0));  
  7.     ……  
  8. }  

代码段中的mModel就是PhotoDataAdapter。mModel.getScreenNail(0)得到当前图片的ScreenNail以做更新。由此可知,我们看到的全屏的图片,就是TileImageView类型的。

欢迎转载和技术交流,转载请帮忙注明出处,http://blog.csdn.net/discovery_by_joseph,谢谢!

posted @ 2017-02-16 16:55  得即高歌失即休  阅读(548)  评论(0)    收藏  举报