现在的位置: 首页 > 综合 > 正文

Android高效显示图片详解(三)

2018年03月21日 ⁄ 综合 ⁄ 共 5943字 ⁄ 字号 评论关闭

http://blog.csdn.net/zhiying201039/article/details/8682419

用户在使用ListView或GridView时,控件会自动把用户滑过的已不在当前显示区域的ChildView回收掉,当然也会把该子视图上的bitmap回收掉以释放内存,因此,为了保证一个流畅,快速的操作体验,我们应当避免反复的对同一张图片进行加载,比如说用户在往下看图的过程中又向上滑回去看图,这时对于已经上面已经加载过的图片我们就没有必要让它再加载一遍了,应该能很快的把图片显示出来,这里我们要使用缓存来达到这一目的。

一,使用Memory Cache:

内存缓存速度快,同时为了更加适应实际应用的场景,我们使用LruCache来达到按使用频率缓存的目的,把最近使用的加入缓存,较长时间不用的则会剔除掉释放出空间。

缓存的代码如下:

[html] view plaincopy

  1. private LruCache<String, Bitmap> mMemoryCache;  
  2.   
  3. @Override  
  4. protected void onCreate(Bundle savedInstanceState) {  
  5.     ...  
  6.     // Get max available VM memory, exceeding this amount will throw an  
  7.     // OutOfMemory exception. Stored in kilobytes as LruCache takes an  
  8.     // int in its constructor.  
  9.     final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);  
  10.   
  11.     // Use 1/8th of the available memory for this memory cache.  
  12.     final int cacheSize = maxMemory / 8;  
  13.   
  14.     mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {  
  15.         @Override  
  16.         protected int sizeOf(String key, Bitmap bitmap) {  
  17.             // The cache size will be measured in kilobytes rather than  
  18.             // number of items.  
  19.             return bitmap.getByteCount() / 1024;  
  20.         }  
  21.     };  
  22.     ...  
  23. }  
  24.   
  25. public void addBitmapToMemoryCache(String key, Bitmap bitmap) {  
  26.     if (getBitmapFromMemCache(key) == null) {  
  27.         mMemoryCache.put(key, bitmap);  
  28.     }  
  29. }  
  30.   
  31. public Bitmap getBitmapFromMemCache(String key) {  
  32.     return mMemoryCache.get(key);  
  33. }  

那么我们在loadBitmap的时候就可以先检查下缓存中保存的是否有该图片,有则直接取出使用,不再进行加载。

新的代码如下:

[html] view plaincopy

  1. public void loadBitmap(int resId, ImageView imageView) {  
  2.     final String imageKey = String.valueOf(resId);  
  3.   
  4.     final Bitmap bitmap = getBitmapFromMemCache(imageKey);  
  5.     if (bitmap != null) {  
  6.         mImageView.setImageBitmap(bitmap);  
  7.     } else {  
  8.         mImageView.setImageResource(R.drawable.image_placeholder);  
  9.         BitmapWorkerTask task = new BitmapWorkerTask(mImageView);  
  10.         task.execute(resId);  
  11.     }  
  12. }  

当然,我们也要在加载图片是及时的维护缓存,把刚使用到的图片add进缓存中去。

新的代码如下:

[html] view plaincopy

  1. class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {  
  2.     ...  
  3.     // Decode image in background.  
  4.     @Override  
  5.     protected Bitmap doInBackground(Integer... params) {  
  6.         final Bitmap bitmap = decodeSampledBitmapFromResource(  
  7.                 getResources(), params[0], 100, 100));  
  8.         addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);  
  9.         return bitmap;  
  10.     }  
  11.     ...  
  12. }  

在使用内存做缓存的基础上,我们还可以使用Disk控件做为缓存,构成一种二级缓存的结构,设想这种情况,如果App在使用的过程被突然来电打断,那么此时有可能就会引起系统内存的回收,当用户再次切换到App时,App就要进行次很明显的图片再次加载的过程。这个时候,我们就需要用到Disk了,因为足够持久。

下面是是原来的基础上增加使用Disk Cache 的例子:

[html] view plaincopy

  1. private DiskLruCache mDiskLruCache;  
  2. private final Object mDiskCacheLock = new Object();  
  3. private boolean mDiskCacheStarting = true;  
  4. private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB  
  5. private static final String DISK_CACHE_SUBDIR = "thumbnails";  
  6.   
  7. @Override  
  8. protected void onCreate(Bundle savedInstanceState) {  
  9.     ...  
  10.     // Initialize memory cache  
  11.     ...  
  12.     // Initialize disk cache on background thread  
  13.     File cacheDir = getDiskCacheDir(this, DISK_CACHE_SUBDIR);  
  14.     new InitDiskCacheTask().execute(cacheDir);  
  15.     ...  
  16. }  
  17.   
  18. class InitDiskCacheTask extends AsyncTask<File, Void, Void> {  
  19.     @Override  
  20.     protected Void doInBackground(File... params) {  
  21.         synchronized (mDiskCacheLock) {  
  22.             File cacheDir = params[0];  
  23.             mDiskLruCache = DiskLruCache.open(cacheDir, DISK_CACHE_SIZE);  
  24.             mDiskCacheStarting = false; // Finished initialization  
  25.             mDiskCacheLock.notifyAll(); // Wake any waiting threads  
  26.         }  
  27.         return null;  
  28.     }  
  29. }  
  30.   
  31. class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {  
  32.     ...  
  33.     // Decode image in background.  
  34.     @Override  
  35.     protected Bitmap doInBackground(Integer... params) {  
  36.         final String imageKey = String.valueOf(params[0]);  
  37.   
  38.         // Check disk cache in background thread  
  39.         Bitmap bitmap = getBitmapFromDiskCache(imageKey);  
  40.   
  41.         if (bitmap == null) { // Not found in disk cache  
  42.             // Process as normal  
  43.             final Bitmap bitmap = decodeSampledBitmapFromResource(  
  44.                     getResources(), params[0], 100, 100));  
  45.         }  
  46.   
  47.         // Add final bitmap to caches  
  48.         addBitmapToCache(imageKey, bitmap);  
  49.   
  50.         return bitmap;  
  51.     }  
  52.     ...  
  53. }  
  54.   
  55. public void addBitmapToCache(String key, Bitmap bitmap) {  
  56.     // Add to memory cache as before  
  57.     if (getBitmapFromMemCache(key) == null) {  
  58.         mMemoryCache.put(key, bitmap);  
  59.     }  
  60.   
  61.     // Also add to disk cache  
  62.     synchronized (mDiskCacheLock) {  
  63.         if (mDiskLruCache != null && mDiskLruCache.get(key) == null) {  
  64.             mDiskLruCache.put(key, bitmap);  
  65.         }  
  66.     }  
  67. }  
  68.   
  69. public Bitmap getBitmapFromDiskCache(String key) {  
  70.     synchronized (mDiskCacheLock) {  
  71.         // Wait while disk cache is started from background thread  
  72.         while (mDiskCacheStarting) {  
  73.             try {  
  74.                 mDiskCacheLock.wait();  
  75.             } catch (InterruptedException e) {}  
  76.         }  
  77.         if (mDiskLruCache != null) {  
  78.             return mDiskLruCache.get(key);  
  79.         }  
  80.     }  
  81.     return null;  
  82. }  
  83.   
  84. // Creates a unique subdirectory of the designated app cache directory. Tries to use external  
  85. // but if not mounted, falls back on internal storage.  
  86. public static File getDiskCacheDir(Context context, String uniqueName) {  
  87.     // Check if media is mounted or storage is built-in, if so, try and use external cache dir  
  88.     // otherwise use internal cache dir  
  89.     final String cachePath =  
  90.             Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) ||  
  91.                     !isExternalStorageRemovable() ? getExternalCacheDir(context).getPath() :  
  92.                             context.getCacheDir().getPath();  
  93.   
  94.     return new File(cachePath + File.separator + uniqueName);  
  95. }  


抱歉!评论已关闭.