Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android中圖片的三級cache策略(內存、文件、網絡) 一

android中圖片的三級cache策略(內存、文件、網絡) 一

編輯:關於Android編程

1. 簡介

現在android應用中不可避免的要使用圖片,有些圖片是可以變化的,需要每次啟動時從網絡拉取,這種場景在有廣告位的應用以及純圖片應用(比如百度美拍)中比較多。

現在有一個問題:假如每次啟動的時候都從網絡拉取圖片的話,勢必會消耗很多流量。在當前的狀況下,對於非wifi用戶來說,流量還是很貴的,一個很耗流量的應用,其用戶數量級肯定要受到影響。當然,我想,向百度美拍這樣的應用,必然也有其內部的圖片緩存策略。總之,圖片緩存是很重要而且是必須的。

 

2.圖片緩存的原理

實現圖片緩存也不難,需要有相應的cache策略。這裡我采用 內存-文件-網絡 三層cache機制,其中內存緩存包括強引用緩存和軟引用緩存(SoftReference),其實網絡不算cache,這裡姑且也把它劃到緩存的層次結構中。當根據url向網絡拉取圖片的時候,先從內存中找,如果內存中沒有,再從緩存文件中查找,如果緩存文件中也沒有,再從網絡上通過http請求拉取圖片。在鍵值對(key-value)中,這個圖片緩存的key是圖片url的hash值,value就是bitmap。所以,按照這個邏輯,只要一個url被下載過,其圖片就被緩存起來了。

 


關於Java中對象的軟引用(SoftReference),如果一個對象具有軟引用,內存空間足夠,垃 圾回收器就不會回收它;如果內存空間不足了,就會回收這些對象的內存。只要垃圾回收器沒有回收它,該對象就可以被程序使用。軟引用可用來實現內存敏感的高 速緩存。使用軟引用能防止內存洩露,增強程序的健壯性。 

 


從代碼上來說,采用一個ImageManager來負責圖片的管理和緩存,函數接口為public void loadBitmap(String url, Handler handler) ;其中url為要下載的圖片地址,handler為圖片下載成功後的回調,在handler中處理message,而message中包含了圖片的信息以及bitmap對象。ImageManager中使用的ImageMemoryCache(內存緩存)、ImageFileCache(文件緩存)以及LruCache(最近最久未使用緩存)會在後續文章中介紹。

 


3.代碼ImageManager.java

 

[java]  /*
 * 圖片管理
 * 異步獲取圖片,直接調用loadImage()函數,該函數自己判斷是從緩存還是網絡加載
 * 同步獲取圖片,直接調用getBitmap()函數,該函數自己判斷是從緩存還是網絡加載
 * 僅從本地獲取圖片,調用getBitmapFromNative()
 * 僅從網絡加載圖片,調用getBitmapFromHttp()
 * 
 */ 
 
public class ImageManager implements IManager 

    private final static String TAG = "ImageManager"; 
     
    private ImageMemoryCache imageMemoryCache; //內存緩存  
     
    private ImageFileCache   imageFileCache; //文件緩存  
     
    //正在下載的image列表  
    public static HashMap<String, Handler> ongoingTaskMap = new HashMap<String, Handler>(); 
     
    //等待下載的image列表  
    public static HashMap<String, Handler> waitingTaskMap = new HashMap<String, Handler>(); 
     
    //同時下載圖片的線程個數  
    final static int MAX_DOWNLOAD_IMAGE_THREAD = 4; 
     
    private final Handler downloadStatusHandler = new Handler(){ 
        public void handleMessage(Message msg) 
        { 
            startDownloadNext(); 
        } 
    }; 
     
    public ImageManager() 
    { 
        imageMemoryCache = new ImageMemoryCache(); 
        imageFileCache = new ImageFileCache(); 
    } 
     
    /**
     * 獲取圖片,多線程的入口
     */ 
    public void loadBitmap(String url, Handler handler)  
    { 
        //先從內存緩存中獲取,取到直接加載  
        Bitmap bitmap = getBitmapFromNative(url); 
         
        if (bitmap != null) 
        { 
            Logger.d(TAG, "loadBitmap:loaded from native"); 
            Message msg = Message.obtain(); 
            Bundle bundle = new Bundle(); 
            bundle.putString("url", url); 
            msg.obj = bitmap; 
            msg.setData(bundle); 
            handler.sendMessage(msg); 
        }  
        else 
        { 
            Logger.d(TAG, "loadBitmap:will load by network"); 
            downloadBmpOnNewThread(url, handler); 
        } 
    } 
     
    /**
     * 新起線程下載圖片
     */ 
    private void downloadBmpOnNewThread(final String url, final Handler handler) 
    { 
        Logger.d(TAG, "ongoingTaskMap'size=" + ongoingTaskMap.size()); 
         
        if (ongoingTaskMap.size() >= MAX_DOWNLOAD_IMAGE_THREAD)  
        { 
            synchronized (waitingTaskMap)  
            { 
                waitingTaskMap.put(url, handler); 
            } 
        }  
        else  
        { 
            synchronized (ongoingTaskMap)  
            { 
                ongoingTaskMap.put(url, handler); 
            } 
 
            new Thread()  
            { 
                public void run()  
                { 
                    Bitmap bmp = getBitmapFromHttp(url); 
 
                    // 不論下載是否成功,都從下載隊列中移除,再由業務邏輯判斷是否重新下載  
                    // 下載圖片使用了httpClientRequest,本身已經帶了重連機制  
                    synchronized (ongoingTaskMap)  
                    { 
                        ongoingTaskMap.remove(url); 
                    } 
                     
                    if(downloadStatusHandler != null) 
                    { 
                        downloadStatusHandler.sendEmptyMessage(0); 
                     
                    } 
 
                    Message msg = Message.obtain(); 
                    msg.obj = bmp; 
                    Bundle bundle = new Bundle(); 
                    bundle.putString("url", url); 
                    msg.setData(bundle); 
                     
                    if(handler != null) 
                    { 
                        handler.sendMessage(msg); 
                    } 
 
                } 
            }.start(); 
        } 
    } 
 
     
    /**
     * 依次從內存,緩存文件,網絡上加載單個bitmap,不考慮線程的問題
     */ 
    public Bitmap getBitmap(String url) 
    { 
        // 從內存緩存中獲取圖片  
        Bitmap bitmap = imageMemoryCache.getBitmapFromMemory(url); 
        if (bitmap == null)  
        { 
            // 文件緩存中獲取  
            bitmap = imageFileCache.getImageFromFile(url); 
            if (bitmap != null)  
            {                
                // 添加到內存緩存  
                imageMemoryCache.addBitmapToMemory(url, bitmap); 
            }  
            else  
            { 
                // 從網絡獲取  
                bitmap = getBitmapFromHttp(url); 
            } 
        } 
        return bitmap; 
    } 
     
    /**
     * 從內存或者緩存文件中獲取bitmap
     */ 
    public Bitmap getBitmapFromNative(String url) 
    { 
        Bitmap bitmap = null; 
        bitmap = imageMemoryCache.getBitmapFromMemory(url); 
         
        if(bitmap == null) 
        { 
            bitmap = imageFileCache.getImageFromFile(url); 
            if(bitmap != null) 
            { 
                // 添加到內存緩存  
                imageMemoryCache.addBitmapToMemory(url, bitmap); 
            } 
        } 
        return bitmap; 
    } 
     
    /**
     * 通過網絡下載圖片,與線程無關
     */ 
    public Bitmap getBitmapFromHttp(String url) 
    { 
        Bitmap bmp = null; 
         
        try 
        { 
            byte[] tmpPicByte = getImageBytes(url); 
     
            if (tmpPicByte != null)  
            { 
                bmp = BitmapFactory.decodeByteArray(tmpPicByte, 0, 
                        tmpPicByte.length); 
            } 
            tmpPicByte = null; 
        } 
        catch(Exception e) 
        { 
            e.printStackTrace(); 
        } 
         
        if(bmp != null) 
        { 
            // 添加到文件緩存  
            imageFileCache.saveBitmapToFile(bmp, url); 
            // 添加到內存緩存  
            imageMemoryCache.addBitmapToMemory(url, bmp); 
        } 
 
        return bmp; 
    } 
     
    /**
     * 下載鏈接的圖片資源
     * 
     * @param url
     *            
     * @return 圖片
     */ 
    public byte[] getImageBytes(String url)  
    { 
        byte[] pic = null; 
        if (url != null && !"".equals(url))  
        { 
            Requester request = RequesterFactory.getRequester( 
                    Requester.REQUEST_REMOTE, RequesterFactory.IMPL_HC); 
            // 執行請求  
            MyResponse myResponse = null; 
            MyRequest mMyRequest; 
            mMyRequest = new MyRequest(); 
            mMyRequest.setUrl(url); 
            mMyRequest.addHeader(HttpHeader.REQ.ACCEPT_ENCODING, "identity"); 
            InputStream is = null; 
            ByteArrayOutputStream baos = null; 
            try { 
                myResponse = request.execute(mMyRequest); 
                is = myResponse.getInputStream().getImpl(); 
                baos = new ByteArrayOutputStream(); 
                byte[] b = new byte[512]; 
                int len = 0; 
                while ((len = is.read(b)) != -1)  
                { 
                    baos.write(b, 0, len); 
                    baos.flush(); 
                } 
                pic = baos.toByteArray(); 
                Logger.d(TAG, "icon bytes.length=" + pic.length); 
 
            }  
            catch (Exception e3)  
            { 
                e3.printStackTrace(); 
                try  
                { 
                    Logger.e(TAG, 
                            "download shortcut icon faild and responsecode=" 
                                    + myResponse.getStatusCode()); 
                }  
                catch (Exception e4)  
                { 
                    e4.printStackTrace(); 
                } 
            }  
            finally  
            { 
                try  
                { 
                    if (is != null)  
                    { 
                        is.close(); 
                        is = null; 
                    } 
                }  
                catch (Exception e2)  
                { 
                    e2.printStackTrace(); 
                } 
                try  
                { 
                    if (baos != null)  
                    { 
                        baos.close(); 
                        baos = null; 
                    } 
                }  
                catch (Exception e2)  
                { 
                    e2.printStackTrace(); 
                } 
                try  
                { 
                    request.close(); 
                }  
                catch (Exception e1)  
                { 
                    e1.printStackTrace(); 
                } 
            } 
        } 
        return pic; 
    } 
     
    /**
     * 取出等待隊列第一個任務,開始下載
     */ 
    private void startDownloadNext() 
    { 
        synchronized(waitingTaskMap) 
        {    
            Logger.d(TAG, "begin start next"); 
            Iterator iter = waitingTaskMap.entrySet().iterator();  
         
            while (iter.hasNext())  
            { 
                 
                Map.Entry entry = (Map.Entry) iter.next(); 
                Logger.d(TAG, "WaitingTaskMap isn't null,url=" + (String)entry.getKey()); 
                 
                if(entry != null) 
                { 
                    waitingTaskMap.remove(entry.getKey()); 
                    downloadBmpOnNewThread((String)entry.getKey(), (Handler)entry.getValue()); 
                } 
                break; 
            } 
        } 
    } 
     
    public String startDownloadNext_ForUnitTest() 
    { 
        String urlString = null; 
        synchronized(waitingTaskMap) 
        { 
            Logger.d(TAG, "begin start next"); 
            Iterator iter = waitingTaskMap.entrySet().iterator();  
         
            while (iter.hasNext())  
            { 
                Map.Entry entry = (Map.Entry) iter.next(); 
                urlString = (String)entry.getKey(); 
                waitingTaskMap.remove(entry.getKey()); 
                break; 
            } 
        } 
        return urlString; 
    } 
     
    /**
     * 圖片變為圓角
     * @param bitmap:傳入的bitmap
     * @param pixels:圓角的度數,值越大,圓角越大
     * @return bitmap:加入圓角的bitmap
     */ 
    public static Bitmap toRoundCorner(Bitmap bitmap, int pixels)  
    {  
        if(bitmap == null) 
            return null; 
         
        Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888);  
        Canvas canvas = new Canvas(output);  
  
        final int color = 0xff424242;  
        final Paint paint = new Paint();  
        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());  
        final RectF rectF = new RectF(rect);  
        final float roundPx = pixels;  
  
        paint.setAntiAlias(true);  
        canvas.drawARGB(0, 0, 0, 0);  
        paint.setColor(color);  
        canvas.drawRoundRect(rectF, roundPx, roundPx, paint);  
  
        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));  
        canvas.drawBitmap(bitmap, rect, rect, paint);  
  
        return output;  
    } 
     
    public byte managerId()  
    { 
        return IMAGE_ID; 
    } 

/*
 * 圖片管理
 * 異步獲取圖片,直接調用loadImage()函數,該函數自己判斷是從緩存還是網絡加載
 * 同步獲取圖片,直接調用getBitmap()函數,該函數自己判斷是從緩存還是網絡加載
 * 僅從本地獲取圖片,調用getBitmapFromNative()
 * 僅從網絡加載圖片,調用getBitmapFromHttp()
 *
 */

public class ImageManager implements IManager
{
 private final static String TAG = "ImageManager";
 
 private ImageMemoryCache imageMemoryCache; //內存緩存
 
 private ImageFileCache   imageFileCache; //文件緩存
 
 //正在下載的image列表
 public static HashMap<String, Handler> ongoingTaskMap = new HashMap<String, Handler>();
 
 //等待下載的image列表
 public static HashMap<String, Handler> waitingTaskMap = new HashMap<String, Handler>();
 
 //同時下載圖片的線程個數
 final static int MAX_DOWNLOAD_IMAGE_THREAD = 4;
 
 private final Handler downloadStatusHandler = new Handler(){
  public void handleMessage(Message msg)
  {
   startDownloadNext();
  }
 };
 
 public ImageManager()
 {
  imageMemoryCache = new ImageMemoryCache();
  imageFileCache = new ImageFileCache();
    }
 
    /**
     * 獲取圖片,多線程的入口
     */
    public void loadBitmap(String url, Handler handler)
    {
        //先從內存緩存中獲取,取到直接加載
        Bitmap bitmap = getBitmapFromNative(url);
       
        if (bitmap != null)
        {
            Logger.d(TAG, "loadBitmap:loaded from native");
         Message msg = Message.obtain();
            Bundle bundle = new Bundle();
            bundle.putString("url", url);
            msg.obj = bitmap;
            msg.setData(bundle);
            handler.sendMessage(msg);
        }
        else
        {
         Logger.d(TAG, "loadBitmap:will load by network");
         downloadBmpOnNewThread(url, handler);
        }
    }
   
    /**
     * 新起線程下載圖片
     */
    private void downloadBmpOnNewThread(final String url, final Handler handler)
    {
  Logger.d(TAG, "ongoingTaskMap'size=" + ongoingTaskMap.size());
     
  if (ongoingTaskMap.size() >= MAX_DOWNLOAD_IMAGE_THREAD)
  {
   synchronized (waitingTaskMap)
   {
    waitingTaskMap.put(url, handler);
   }
  }
  else
  {
   synchronized (ongoingTaskMap)
   {
    ongoingTaskMap.put(url, handler);
   }

   new Thread()
   {
    public void run()
    {
     Bitmap bmp = getBitmapFromHttp(url);

     // 不論下載是否成功,都從下載隊列中移除,再由業務邏輯判斷是否重新下載
     // 下載圖片使用了httpClientRequest,本身已經帶了重連機制
     synchronized (ongoingTaskMap)
     {
      ongoingTaskMap.remove(url);
     }
     
     if(downloadStatusHandler != null)
     {
      downloadStatusHandler.sendEmptyMessage(0);
     
     }

     Message msg = Message.obtain();
     msg.obj = bmp;
     Bundle bundle = new Bundle();
     bundle.putString("url", url);
     msg.setData(bundle);
     
     if(handler != null)
     {
      handler.sendMessage(msg);
     }

    }
   }.start();
  }
 }

   
 /**
     * 依次從內存,緩存文件,網絡上加載單個bitmap,不考慮線程的問題
     */
 public Bitmap getBitmap(String url)
 {
     // 從內存緩存中獲取圖片
     Bitmap bitmap = imageMemoryCache.getBitmapFromMemory(url);
     if (bitmap == null)
     {
         // 文件緩存中獲取
      bitmap = imageFileCache.getImageFromFile(url);
         if (bitmap != null)
         {            
          // 添加到內存緩存
          imageMemoryCache.addBitmapToMemory(url, bitmap);
         }
         else
         {
             // 從網絡獲取
          bitmap = getBitmapFromHttp(url);
         }
     }
     return bitmap;
 }
 
 /**
  * 從內存或者緩存文件中獲取bitmap
  */
 public Bitmap getBitmapFromNative(String url)
 {
  Bitmap bitmap = null;
  bitmap = imageMemoryCache.getBitmapFromMemory(url);
  
  if(bitmap == null)
  {
   bitmap = imageFileCache.getImageFromFile(url);
   if(bitmap != null)
   {
    // 添加到內存緩存
    imageMemoryCache.addBitmapToMemory(url, bitmap);
   }
  }
  return bitmap;
 }
 
 /**
  * 通過網絡下載圖片,與線程無關
  */
 public Bitmap getBitmapFromHttp(String url)
 {
  Bitmap bmp = null;
  
  try
  {
   byte[] tmpPicByte = getImageBytes(url);
 
   if (tmpPicByte != null)
   {
    bmp = BitmapFactory.decodeByteArray(tmpPicByte, 0,
      tmpPicByte.length);
   }
   tmpPicByte = null;
  }
  catch(Exception e)
  {
   e.printStackTrace();
  }
  
  if(bmp != null)
  {
   // 添加到文件緩存
   imageFileCache.saveBitmapToFile(bmp, url);
   // 添加到內存緩存
   imageMemoryCache.addBitmapToMemory(url, bmp);
  }

  return bmp;
 }
 
 /**
  * 下載鏈接的圖片資源
  *
  * @param url
  *           
  * @return 圖片
  */
 public byte[] getImageBytes(String url)
 {
  byte[] pic = null;
  if (url != null && !"".equals(url))
  {
   Requester request = RequesterFactory.getRequester(
     Requester.REQUEST_REMOTE, RequesterFactory.IMPL_HC);
   // 執行請求
   MyResponse myResponse = null;
   MyRequest mMyRequest;
   mMyRequest = new MyRequest();
   mMyRequest.setUrl(url);
   mMyRequest.addHeader(HttpHeader.REQ.ACCEPT_ENCODING, "identity");
   InputStream is = null;
   ByteArrayOutputStream baos = null;
   try {
    myResponse = request.execute(mMyRequest);
    is = myResponse.getInputStream().getImpl();
    baos = new ByteArrayOutputStream();
    byte[] b = new byte[512];
    int len = 0;
    while ((len = is.read(b)) != -1)
    {
     baos.write(b, 0, len);
     baos.flush();
    }
    pic = baos.toByteArray();
    Logger.d(TAG, "icon bytes.length=" + pic.length);

   }
   catch (Exception e3)
   {
    e3.printStackTrace();
    try
    {
     Logger.e(TAG,
       "download shortcut icon faild and responsecode="
         + myResponse.getStatusCode());
    }
    catch (Exception e4)
    {
     e4.printStackTrace();
    }
   }
   finally
   {
    try
    {
     if (is != null)
     {
      is.close();
      is = null;
     }
    }
    catch (Exception e2)
    {
     e2.printStackTrace();
    }
    try
    {
     if (baos != null)
     {
      baos.close();
      baos = null;
     }
    }
    catch (Exception e2)
    {
     e2.printStackTrace();
    }
    try
    {
     request.close();
    }
    catch (Exception e1)
    {
     e1.printStackTrace();
    }
   }
  }
  return pic;
 }
 
 /**
  * 取出等待隊列第一個任務,開始下載
  */
 private void startDownloadNext()
 {
  synchronized(waitingTaskMap)
  { 
   Logger.d(TAG, "begin start next");
   Iterator iter = waitingTaskMap.entrySet().iterator();
  
   while (iter.hasNext())
   {
    
    Map.Entry entry = (Map.Entry) iter.next();
    Logger.d(TAG, "WaitingTaskMap isn't null,url=" + (String)entry.getKey());
    
    if(entry != null)
    {
     waitingTaskMap.remove(entry.getKey());
     downloadBmpOnNewThread((String)entry.getKey(), (Handler)entry.getValue());
    }
    break;
   }
  }
 }
 
 public String startDownloadNext_ForUnitTest()
 {
  String urlString = null;
  synchronized(waitingTaskMap)
  {
   Logger.d(TAG, "begin start next");
   Iterator iter = waitingTaskMap.entrySet().iterator();
  
   while (iter.hasNext())
   {
    Map.Entry entry = (Map.Entry) iter.next();
    urlString = (String)entry.getKey();
    waitingTaskMap.remove(entry.getKey());
    break;
   }
  }
  return urlString;
 }
 
 /**
  * 圖片變為圓角
  * @param bitmap:傳入的bitmap
  * @param pixels:圓角的度數,值越大,圓角越大
  * @return bitmap:加入圓角的bitmap
  */
 public static Bitmap toRoundCorner(Bitmap bitmap, int pixels)
 {
        if(bitmap == null)
         return null;
       
  Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888);
        Canvas canvas = new Canvas(output);
 
        final int color = 0xff424242;
        final Paint paint = new Paint();
        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
        final RectF rectF = new RectF(rect);
        final float roundPx = pixels;
 
        paint.setAntiAlias(true);
        canvas.drawARGB(0, 0, 0, 0);
        paint.setColor(color);
        canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
 
        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
        canvas.drawBitmap(bitmap, rect, rect, paint);
 
        return output;
    }
 
 public byte managerId()
 {
  return IMAGE_ID;
 }
}

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved