Android图片处理神器BitmapFun源码分析

 尊重源码http://blog.csdn.net/yuanzeyao/article/details/38355719


作为一名Android开发人员,相信大家对图片OOM的问题已经耳熟能详了,关于图片缓存和解决OOM的开源项目也是相当的多,被大家熟知的就是Universal_image_loader和Volley了,Volley在前面的文章中已经有介绍。Universal_image_loader在图片缓存功能方面应该算功能最强的,但是感觉很多功能用不上,所以在项目中我一般不太喜欢使用Universal_image_loader(因为本身自己的App源码非常多,加入这些开源库就就更大了,容易出现无法编译的问题,因为Android貌似对一个应用中的方法个数好像有限制,貌似是655**个吧,具体多少我也记不清)。

关于处理图片缓存上,我接触的两个播放器项目中,使用的都是BitmapFun,BitmapFun 是Google为Android开发提供了一个培训教程,既然是Google提供的,那么我觉得作为一名合格的Android开发人员很有必要学习学习,而且BitmapFun非常简单,基本可以满足我们项目中对于图片缓存处理需求了。

对于开源项目的学习,我通常很少在应用层面来学习的,因为如何使用一个开源项目的相关博客已经相当多了,而且写得都非常详细,对于大多数开源项目它都是自带sample的,所以如果想学习如何使用某个开源项目,好好研究sample就行了,但是我始终认为,熟悉经典开源项目源码才是王道。好了废话不多说,我们开始学习BitmapFun源码吧。

1、BitmapFun结构
BitmapFun和其他开源库的结构稍有不同,因为它仅仅是Google的培训教程,所以BitmapFun和它的sample放在了一个工程里面,结构图如下:上面部分是BitmapFun的应用,下面部分是BitmapFun的源码。





2、相关类介绍
在BitmapFun中最重要的一个类就是ImageFetcher,请求图片主要就是调用loadImage方法,但是这个类是继承ImageResizer,而ImageResizser是继承ImageWorker,所以我们就从ImageWorker开始学习吧

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. ImageWorker.java  
  2. /** 
  3.     这个类用来封装一次图片的加载过程,包括使用从缓存中加载 
  4.  */  
  5. public abstract class ImageWorker {  
  6.     private static final String TAG = "ImageWorker";  
  7.     //这个变量用于动画效果,没有实际意义  
  8.     private static final int FADE_IN_TIME = 200;  
  9.     //缓存,包括磁盘缓存和内存缓存  
  10.     private ImageCache mImageCache;  
  11.     //创建缓存需要的参数  
  12.     private ImageCache.ImageCacheParams mImageCacheParams;  
  13.     //加载过程中,ImageView显示的图片  
  14.     private Bitmap mLoadingBitmap;  
  15.     //是否使用渐变效果  
  16.     private boolean mFadeInBitmap = true;  
  17.     //是否提前退出任务,如果true,那么图片请求回来后是不会显示出来的  
  18.     private boolean mExitTasksEarly = false;  
  19.     //是否暂停任务  
  20.     protected boolean mPauseWork = false;  
  21.     private final Object mPauseWorkLock = new Object();  
  22.   
  23.     protected Resources mResources;  
  24.   
  25.     private static final int MESSAGE_CLEAR = 0;  
  26.     private static final int MESSAGE_INIT_DISK_CACHE = 1;  
  27.     private static final int MESSAGE_FLUSH = 2;  
  28.     private static final int MESSAGE_CLOSE = 3;  
  29.   
  30.     protected ImageWorker(Context context) {  
  31.         mResources = context.getResources();  
  32.     }  
  33.   
  34.     /** 
  35.      * 请求一张图片的接口 
  36.      * @param 图片url 
  37.      * @param 要显示这种图片的ImageView 
  38.      */  
  39.     public void loadImage(Object data, ImageView imageView) {  
  40.         if (data == null) {  
  41.             return;  
  42.         }  
  43.   
  44.         BitmapDrawable value = null;  
  45.         //如果缓存对象不为空,那么从内存缓存中读取对象  
  46.         if (mImageCache != null) {  
  47.             value = mImageCache.getBitmapFromMemCache(String.valueOf(data));  
  48.         }  
  49.   
  50.         if (value != null) {  
  51.             // 内存缓存命中,那么直接显示  
  52.             imageView.setImageDrawable(value);  
  53.         } else if (cancelPotentialWork(data, imageView)) {  
  54.             //内存缓存没有命中,那么创建一个图片请求Task,将imageView作为参数  
  55.             final BitmapWorkerTask task = new BitmapWorkerTask(imageView);  
  56.             //AsyncDrawable 是BitmapDrawable子类,主要用来存放当前任务的弱应用  
  57.             final AsyncDrawable asyncDrawable =  
  58.                     new AsyncDrawable(mResources, mLoadingBitmap, task);  
  59.             //将asyncDrawable设置到imageView中,这样imageView和当前任务就一一对应了  
  60.             imageView.setImageDrawable(asyncDrawable);  
  61.   
  62.             //调用AsyncTask的executeOnExecutor方法,这个AsyncTask和Android系统中的AsyncTask有些区别,但是使用上一样的  
  63.             task.executeOnExecutor(AsyncTask.DUAL_THREAD_EXECUTOR, data);  
  64.         }  
  65.     }  
  66.   
  67.     /** 
  68.      * 设置加载过程中的默认图片 
  69.      * 
  70.      * @param bitmap 
  71.      */  
  72.     public void setLoadingImage(Bitmap bitmap) {  
  73.         mLoadingBitmap = bitmap;  
  74.     }  
  75.   
  76.     /** 
  77.      * 将本地图片设置为默认图片 
  78.      * 
  79.      * @param resId 
  80.      */  
  81.     public void setLoadingImage(int resId) {  
  82.         mLoadingBitmap = BitmapFactory.decodeResource(mResources, resId);  
  83.     }  
  84.   
  85.     /** 
  86.      * 添加一个缓冲对象,创建磁盘缓存时需要子线程中完成 
  87.      * @param fragmentManager 
  88.      * @param cacheParams The cache parameters to use for the image cache. 
  89.      */  
  90.     public void addImageCache(FragmentManager fragmentManager,  
  91.             ImageCache.ImageCacheParams cacheParams) {  
  92.         mImageCacheParams = cacheParams;  
  93.         mImageCache = ImageCache.getInstance(fragmentManager, mImageCacheParams);  
  94.         //完成磁盘缓存初始化  
  95.         new CacheAsyncTask().execute(MESSAGE_INIT_DISK_CACHE);  
  96.     }  
  97.   
  98.     /** 
  99.      * Adds an {@link ImageCache} to this {@link ImageWorker} to handle disk and memory bitmap 
  100.      * caching. 
  101.      * @param activity 
  102.      * @param diskCacheDirectoryName See 
  103.      * {@link ImageCache.ImageCacheParams#ImageCacheParams(Context, String)}. 
  104.      */  
  105.     public void addImageCache(FragmentActivity activity, String diskCacheDirectoryName) {  
  106.         mImageCacheParams = new ImageCache.ImageCacheParams(activity, diskCacheDirectoryName);  
  107.         mImageCache = ImageCache.getInstance(activity.getSupportFragmentManager(), mImageCacheParams);  
  108.         new CacheAsyncTask().execute(MESSAGE_INIT_DISK_CACHE);  
  109.     }  
  110.   
  111.     /** 
  112.      * 设置是否使用渐变效果 
  113.      */  
  114.     public void setImageFadeIn(boolean fadeIn) {  
  115.         mFadeInBitmap = fadeIn;  
  116.     }  
  117.   
  118.     //是否提前退出任务  
  119.     public void setExitTasksEarly(boolean exitTasksEarly) {  
  120.         mExitTasksEarly = exitTasksEarly;  
  121.         setPauseWork(false);  
  122.     }  
  123.   
  124.     /** 
  125.      * Subclasses should override this to define any processing or work that must happen to produce 
  126.      * the final bitmap. This will be executed in a background thread and be long running. For 
  127.      * example, you could resize a large bitmap here, or pull down an image from the network. 
  128.      * 
  129.      * @param data The data to identify which image to process, as provided by 
  130.      *            {@link ImageWorker#loadImage(Object, ImageView)} 
  131.      * @return The processed bitmap 
  132.      */  
  133.     protected abstract Bitmap processBitmap(Object data);  
  134.   
  135.     /** 
  136.      * @return The {@link ImageCache} object currently being used by this ImageWorker. 
  137.      */  
  138.     protected ImageCache getImageCache() {  
  139.         return mImageCache;  
  140.     }  
  141.   
  142.     /** 
  143.      * Cancels any pending work attached to the provided ImageView. 
  144.      * @param imageView 
  145.      */  
  146.     public static void cancelWork(ImageView imageView) {  
  147.         //通过ImageView找到task,为什么可以找到?因为imageView和task一一对应  
  148.         final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);  
  149.         //如果task不为空,那么取消  
  150.         if (bitmapWorkerTask != null) {  
  151.             bitmapWorkerTask.cancel(true);  
  152.             if (BuildConfig.DEBUG) {  
  153.                 final Object bitmapData = bitmapWorkerTask.data;  
  154.                 Log.d(TAG, "cancelWork - cancelled work for " + bitmapData);  
  155.             }  
  156.         }  
  157.     }  
  158.   
  159.     /** 
  160.      * Returns true if the current work has been canceled or if there was no work in 
  161.      * progress on this image view. 
  162.      * Returns false if the work in progress deals with the same data. The work is not 
  163.      * stopped in that case. 
  164.      */  
  165.     public static boolean cancelPotentialWork(Object data, ImageView imageView) {  
  166.         //通过imageView找到task  
  167.         final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);  
  168.   
  169.         if (bitmapWorkerTask != null) {  
  170.             //如果找到的task不为null,并且task的url和给定的url相同,那么取消任务  
  171.             final Object bitmapData = bitmapWorkerTask.data;  
  172.             if (bitmapData == null || !bitmapData.equals(data)) {  
  173.                 bitmapWorkerTask.cancel(true);  
  174.                 if (BuildConfig.DEBUG) {  
  175.                     Log.d(TAG, "cancelPotentialWork - cancelled work for " + data);  
  176.                 }  
  177.             } else {  
  178.                 // The same work is already in progress.  
  179.                 return false;  
  180.             }  
  181.         }  
  182.         return true;  
  183.     }  
  184.   
  185.     /** 
  186.      * 通过iamgeView找到对应的Task 
  187.      */  
  188.     private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {  
  189.         if (imageView != null) {  
  190.             final Drawable drawable = imageView.getDrawable();  
  191.             if (drawable instanceof AsyncDrawable) {  
  192.                 final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;  
  193.                 return asyncDrawable.getBitmapWorkerTask();  
  194.             }  
  195.         }  
  196.         return null;  
  197.     }  
  198.   
  199.     /** 
  200.      * 一个请求图片的异步任务, 
  201.      */  
  202.     private class BitmapWorkerTask extends AsyncTask<Object, Void, BitmapDrawable> {  
  203.         //请求图片的url  
  204.         private Object data;  
  205.         //持有ImageView的弱引用  
  206.         private final WeakReference<ImageView> imageViewReference;  
  207.   
  208.         public BitmapWorkerTask(ImageView imageView) {  
  209.             imageViewReference = new WeakReference<ImageView>(imageView);  
  210.         }  
  211.   
  212.         /** 
  213.          * Background processing. 
  214.          */  
  215.         @Override  
  216.         protected BitmapDrawable doInBackground(Object... params) {  
  217.             if (BuildConfig.DEBUG) {  
  218.                 Log.d(TAG, "doInBackground - starting work");  
  219.             }  
  220.   
  221.             data = params[0];  
  222.             final String dataString = String.valueOf(data);  
  223.             Bitmap bitmap = null;  
  224.             BitmapDrawable drawable = null;  
  225.   
  226.             // 如果work已经暂停并且图片请求没有取消,那么就等待  
  227.             synchronized (mPauseWorkLock) {  
  228.                 while (mPauseWork && !isCancelled()) {  
  229.                     try {  
  230.                         mPauseWorkLock.wait();  
  231.                     } catch (InterruptedException e) {}  
  232.                 }  
  233.             }  
  234.   
  235.             //如果有缓存,并且没有取消,当前弱引用中的imageView对应的task还是自己(task),那么从磁盘缓存中读取  
  236.             //为什么在这里读磁盘缓存?因为磁盘缓存只能在异步线程读取,doingbackground就是在异步线程执行  
  237.             if (mImageCache != null && !isCancelled() && getAttachedImageView() != null  
  238.                     && !mExitTasksEarly) {  
  239.                 bitmap = mImageCache.getBitmapFromDiskCache(dataString);  
  240.             }  
  241.   
  242.             //如果没有命中,并且没有取消,并且当前弱引用中的ImageView对应的task还是自己,那么请求网络图片,  
  243.             //调用processBitmap方法,这个方法是个抽象的,在ImageFecter中实现  
  244.             if (bitmap == null && !isCancelled() && getAttachedImageView() != null  
  245.                     && !mExitTasksEarly) {  
  246.                 bitmap = processBitmap(params[0]);  
  247.             }  
  248.   
  249.             // If the bitmap was processed and the image cache is available, then add the processed  
  250.             // bitmap to the cache for future use. Note we don't check if the task was cancelled  
  251.             // here, if it was, and the thread is still running, we may as well add the processed  
  252.             // bitmap to our cache as it might be used again in the future  
  253.             if (bitmap != null) {  
  254.                 if (Utils.hasHoneycomb()) {  
  255.                     // Running on Honeycomb or newer, so wrap in a standard BitmapDrawable  
  256.                     drawable = new BitmapDrawable(mResources, bitmap);  
  257.                 } else {  
  258.                     // Running on Gingerbread or older, so wrap in a RecyclingBitmapDrawable  
  259.                     // which will recycle automagically  
  260.                     drawable = new RecyclingBitmapDrawable(mResources, bitmap);  
  261.                 }  
  262.                 //将图片加入缓存  
  263.                 if (mImageCache != null) {  
  264.                     mImageCache.addBitmapToCache(dataString, drawable);  
  265.                 }  
  266.             }  
  267.   
  268.             if (BuildConfig.DEBUG) {  
  269.                 Log.d(TAG, "doInBackground - finished work");  
  270.             }  
  271.   
  272.             return drawable;  
  273.         }  
  274.   
  275.         /** 
  276.          * Once the image is processed, associates it to the imageView 
  277.          */  
  278.         @Override  
  279.         protected void onPostExecute(BitmapDrawable value) {  
  280.             // 如果取消了或者提前退出,那么不显示这个图片,直接设置null  
  281.             if (isCancelled() || mExitTasksEarly) {  
  282.                 value = null;  
  283.             }  
  284.   
  285.             final ImageView imageView = getAttachedImageView();  
  286.             if (value != null && imageView != null) {  
  287.                 if (BuildConfig.DEBUG) {  
  288.                     Log.d(TAG, "onPostExecute - setting bitmap");  
  289.                 }  
  290.                 //将图片显示出来  
  291.                 setImageDrawable(imageView, value);  
  292.             }  
  293.         }  
  294.   
  295.         @Override  
  296.         protected void onCancelled(BitmapDrawable value) {  
  297.             super.onCancelled(value);  
  298.             //任务取消了,必须通知后台线程停止等待  
  299.             synchronized (mPauseWorkLock) {  
  300.                 mPauseWorkLock.notifyAll();  
  301.             }  
  302.         }  
  303.   
  304.          
  305.         private ImageView getAttachedImageView() {  
  306.             final ImageView imageView = imageViewReference.get();  
  307.             final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);  
  308.   
  309.             if (this == bitmapWorkerTask) {  
  310.                 return imageView;  
  311.             }  
  312.   
  313.             return null;  
  314.         }  
  315.     }  
  316.   
  317.     /** 
  318.      *用于实现imageView和task一一对应的类 
  319.      */  
  320.     private static class AsyncDrawable extends BitmapDrawable {  
  321.         private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;  
  322.   
  323.         public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {  
  324.             super(res, bitmap);  
  325.             bitmapWorkerTaskReference =  
  326.                 new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);  
  327.         }  
  328.   
  329.         public BitmapWorkerTask getBitmapWorkerTask() {  
  330.             return bitmapWorkerTaskReference.get();  
  331.         }  
  332.     }  
  333.   
  334.     /** 
  335.      * 显示图片,渐变显示或者普通显示 
  336.      * 
  337.      * @param imageView 
  338.      * @param drawable 
  339.      */  
  340.     private void setImageDrawable(ImageView imageView, Drawable drawable) {  
  341.         if (mFadeInBitmap) {  
  342.             // Transition drawable with a transparent drawable and the final drawable  
  343.             final TransitionDrawable td =  
  344.                     new TransitionDrawable(new Drawable[] {  
  345.                             new ColorDrawable(android.R.color.transparent),  
  346.                             drawable  
  347.                     });  
  348.             // Set background to loading bitmap  
  349.             imageView.setBackgroundDrawable(  
  350.                     new BitmapDrawable(mResources, mLoadingBitmap));  
  351.   
  352.             imageView.setImageDrawable(td);  
  353.             td.startTransition(FADE_IN_TIME);  
  354.         } else {  
  355.             imageView.setImageDrawable(drawable);  
  356.         }  
  357.     }  
  358. }  

分析完ImageWorker之后,我们发现在ImageWorker中已经提供了获取网络图片的方法loadImage,当我调用了此方法后,首先会试图从内存缓存获取图片,如果获取成功,直接返回,如果没有获取成功,则启动一个BitmapWorkerTask,使用异步线程获取图片,在异步线程中,首先到磁盘中获取,如果磁盘没有获取,最后才从网络获取,我们发现在BitmapWorkerTask中是通过调用processBitmap方法完成图片获取的,但是这个方法是一个抽象方法,需要子类去实现,那我们到它的子类ImageResizer中


[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2.     protected Bitmap processBitmap(Object data) {  
  3.         return processBitmap(Integer.parseInt(String.valueOf(data)));  
  4.     }  

它调用的是另外一个重载的processBitmap方法,我们看看另外一个方法吧

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. private Bitmap processBitmap(int resId) {  
  2.         if (BuildConfig.DEBUG) {  
  3.             Log.d(TAG, "processBitmap - " + resId);  
  4.         }  
  5.         return decodeSampledBitmapFromResource(mResources, resId, mImageWidth,  
  6.                 mImageHeight, getImageCache());  
  7.     }  

我们发现这个方法仅仅是用来加载本地图片的,那它是如何实现网络图片的加载的呢,如果你把ImageResizer源码通读一边,你会发现ImageResizer这个类的主要功能如下:
1、设置显示图片的sizse
2、从磁盘缓存中加载图片

所以从网络加载图片根本不是这个类的功能,聪明的同学马上就应该想到了ImageFetcher这个类,对!,我们就直接看看ImageFetcher这个类吧

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. private Bitmap processBitmap(String data) {  
  2.   
  3.         final String key = ImageCache.hashKeyForDisk(data);  
  4.         FileDescriptor fileDescriptor = null;  
  5.         FileInputStream fileInputStream = null;  
  6.         DiskLruCache.Snapshot snapshot;  
  7.         //检查mHttpDiskCache是否已经初始化,这里一定要注意,mHttpDiskCache这个磁盘缓存是在ImageFetcher调用addImageCache时初始化的,如果你没有调用addImageCache  
  8.         //那么这里就会阻塞,从而无法获取图片,具体情况还请大家自己分析代码吧  
  9.         synchronized (mHttpDiskCacheLock) {  
  10.             // Wait for disk cache to initialize  
  11.             while (mHttpDiskCacheStarting) {  
  12.                 try {  
  13.                     mHttpDiskCacheLock.wait();  
  14.                 } catch (InterruptedException e) {}  
  15.             }  
  16.             //下面这段代码就是从mHttpDiskCache里面写入图片  
  17.             if (mHttpDiskCache != null) {  
  18.                 try {  
  19.                     snapshot = mHttpDiskCache.get(key);  
  20.                     if (snapshot == null) {  
  21.                         if (BuildConfig.DEBUG) {  
  22.                             Log.d(TAG, "processBitmap, not found in http cache, downloading...");  
  23.                         }  
  24.                         DiskLruCache.Editor editor = mHttpDiskCache.edit(key);  
  25.                         if (editor != null) {  
  26.                             //下载图片逻辑在这里  
  27.                             if (downloadUrlToStream(data,  
  28.                                     editor.newOutputStream(DISK_CACHE_INDEX))) {  
  29.                                 editor.commit();  
  30.                             } else {  
  31.                                 editor.abort();  
  32.                             }  
  33.                         }  
  34.                         snapshot = mHttpDiskCache.get(key);  
  35.                     }  
  36.                     if (snapshot != null) {  
  37.                         fileInputStream =  
  38.                                 (FileInputStream) snapshot.getInputStream(DISK_CACHE_INDEX);  
  39.                         fileDescriptor = fileInputStream.getFD();  
  40.                     }  
  41.                 } catch (IOException e) {  
  42.                     Log.e(TAG, "processBitmap - " + e);  
  43.                 } catch (IllegalStateException e) {  
  44.                     Log.e(TAG, "processBitmap - " + e);  
  45.                 } finally {  
  46.                     if (fileDescriptor == null && fileInputStream != null) {  
  47.                         try {  
  48.                             fileInputStream.close();  
  49.                         } catch (IOException e) {}  
  50.                     }  
  51.                 }  
  52.             }  
  53.         }  
  54.   
  55.         Bitmap bitmap = null;  
  56.         if (fileDescriptor != null) {  
  57.             //调用ImageResizer中的方法来将mHttpDiskCache中的缓存生成指定大小的图片  
  58.             bitmap = decodeSampledBitmapFromDescriptor(fileDescriptor, mImageWidth,  
  59.                     mImageHeight, getImageCache());  
  60.         }  
  61.         if (fileInputStream != null) {  
  62.             try {  
  63.                 fileInputStream.close();  
  64.             } catch (IOException e) {}  
  65.         }  
  66.         return bitmap;  
  67.     }  
  68.   
  69.   
  70.   
  71.     /** 
  72.      * 从网络通过HttpURLConnection下载图片,并写入到磁盘缓存 
  73.      * 
  74.      * @param urlString The URL to fetch 
  75.      * @return true if successful, false otherwise 
  76.      */  
  77.     public boolean downloadUrlToStream(String urlString, OutputStream outputStream) {  
  78.         disableConnectionReuseIfNecessary();  
  79.         HttpURLConnection urlConnection = null;  
  80.         BufferedOutputStream out = null;  
  81.         BufferedInputStream in = null;  
  82.   
  83.         try {  
  84.             final URL url = new URL(urlString);  
  85.             urlConnection = (HttpURLConnection) url.openConnection();  
  86.             in = new BufferedInputStream(urlConnection.getInputStream(), IO_BUFFER_SIZE);  
  87.             out = new BufferedOutputStream(outputStream, IO_BUFFER_SIZE);  
  88.   
  89.             int b;  
  90.             while ((b = in.read()) != -1) {  
  91.                 out.write(b);  
  92.             }  
  93.             return true;  
  94.         } catch (final IOException e) {  
  95.             Log.e(TAG, "Error in downloadBitmap - " + e);  
  96.         } finally {  
  97.             if (urlConnection != null) {  
  98.                 urlConnection.disconnect();  
  99.             }  
  100.             try {  
  101.                 if (out != null) {  
  102.                     out.close();  
  103.                 }  
  104.                 if (in != null) {  
  105.                     in.close();  
  106.                 }  
  107.             } catch (final IOException e) {}  
  108.         }  
  109.         return false;  
  110.     }  

好了,对于Bitmapfun的整个代码逻辑我就简单的分析到这里吧,其实了解了Bitmapfun的代码逻辑后,我们完全可以对其进行优化,我在这里仅仅提出一点可以优化的地方,优化的方法就交给大家完成吧

比如BitmapWorkerTask在获取图片的时候先是读取磁盘缓存,然后从网络获取,也就是说如果读取本地和读取网络图片时在同一条线程中完成的,这个时候就有可能出现一个问题,本地图片存在却无法加载出来:例如:在网络条件不好的情况下,前面的五个图片请求刚好用完了所有的线程,由于网络条件不好,一直没有返回,而第六个图片刚好有缓存,那么它是无法加载出来的,因为没有线程了,所以解决方案就是学习Volley(我前面的文章对于Volley已经介绍了)中的解决方案,让一条线程专门处理本地图片,其他线程用于处理网络图片。

就写到这里吧,如果大家有什么没看明白或者我写错了的,欢迎留言.....
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值