Android 內存+文件+網絡三級緩存
package com.panpass.main; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.os.Bundle; import android.os.Handler; import android.os.Message; /* * 圖片管理 * 異步獲取圖片,直接調用loadImage()函數,該函數自己推斷是從緩存還是網絡載入 * 同步獲取圖片,直接調用getBitmap()函數。該函數自己推斷是從緩存還是網絡載入 * 僅從本地獲取圖片,調用getBitmapFromNative() * 僅從網絡載入圖片,調用getBitmapFromHttp() * */ public class ImageManager { public static ImageManager im = null; 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 static ImageManager getInstance(Context mContext) { if (im == null) { synchronized (ImageManager.class) { if (im == null) { im = new ImageManager(mContext); } } } return im; } public ImageManager(Context mContext) { imageMemoryCache = new ImageMemoryCache(); imageFileCache = new ImageFileCache(mContext); } /** * 獲取圖片。多線程的入口 */ public void loadBitmap(String urlStr, Handler handler) { String url = urlStr.hashCode() + ""; // 先從內存緩存中獲取。取到直接載入 Bitmap bitmap = getBitmapFromNative(urlStr); if (bitmap != null) { Message msg = Message.obtain(); Bundle bundle = new Bundle(); bundle.putString("url", url); msg.obj = bitmap; msg.setData(bundle); handler.sendMessage(msg); } else { downloadBmpOnNewThread(urlStr, handler); } } /** * 新起線程下載圖片 */ private void downloadBmpOnNewThread(final String urlStr, final Handler handler) { final String url = urlStr.hashCode() + ""; 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(urlStr); // 不論下載是否成功,都從下載隊列中移除,再由業務邏輯推斷是否又一次下載 // 下載圖片使用了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 urlStr) { String url = urlStr.hashCode() + ""; // 從內存緩存中獲取圖片 Bitmap bitmap = imageMemoryCache.getBitmapFromMemory(url); if (bitmap == null) { // 文件緩存中獲取 bitmap = imageFileCache.getImageFromFile(url); if (bitmap != null) { // 加入到內存緩存 imageMemoryCache.addBitmapToMemory(url, bitmap); } else { // 從網絡獲取 bitmap = getBitmapFromHttp(urlStr); } } return bitmap; } /** * 從內存或者緩存文件裏獲取bitmap */ public Bitmap getBitmapFromNative(String urlStr) { String url = urlStr.hashCode() + ""; 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 urlStr) { String url = urlStr.hashCode() + ""; Bitmap bmp = null; try { byte[] tmpPicByte = getImageBytes(urlStr); 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 urlStr) { if (urlStr != null && !"".equals(urlStr)) { HttpGet get = new HttpGet(urlStr); get.addHeader("Accept-Encoding", "indentity"); HttpClient client = new DefaultHttpClient(); HttpResponse response = null; try { response = client.execute(get); if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { InputStream is = response.getEntity().getContent(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buf = new byte[1024]; int len = -1; while ((len = is.read(buf)) != -1) { baos.write(buf, 0, len); } return baos.toByteArray(); } } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } return null; } /** * 取出等待隊列第一個任務,開始下載 */ private void startDownloadNext() { synchronized (waitingTaskMap) { Iterator<Entry<String, Handler>> iter = waitingTaskMap.entrySet() .iterator(); if (iter.hasNext()) { Map.Entry<String, Handler> entry = (Map.Entry<String, Handler>) iter .next(); if (entry != null) { waitingTaskMap.remove(entry.getKey()); downloadBmpOnNewThread((String) entry.getKey(), (Handler) entry.getValue()); } } } } /** * 圖片變為圓角 * * @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; } }
package com.panpass.main; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; public class ImageFileCache { <span style="white-space:pre"> </span>private File cacheDir; <span style="white-space:pre"> </span>public ImageFileCache(Context context) { <span style="white-space:pre"> </span>// 假設有SD卡則在SD卡中建一個LazyList的文件夾存放緩存的圖片 <span style="white-space:pre"> </span>// 沒有SD卡就放在系統的緩存文件夾中 <span style="white-space:pre"> </span>if (android.os.Environment.getExternalStorageState().equals( <span style="white-space:pre"> </span>android.os.Environment.MEDIA_MOUNTED)) <span style="white-space:pre"> </span>cacheDir = new File( <span style="white-space:pre"> </span>android.os.Environment.getExternalStorageDirectory(), <span style="white-space:pre"> </span>"LazyList"); <span style="white-space:pre"> </span>else <span style="white-space:pre"> </span>cacheDir = context.getCacheDir(); <span style="white-space:pre"> </span>if (!cacheDir.exists()) <span style="white-space:pre"> </span>cacheDir.mkdirs(); <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>public Bitmap getImageFromFile(String url) { <span style="white-space:pre"> </span>// 將url的hashCode作為緩存的文件名稱 <span style="white-space:pre"> </span>// Another possible solution <span style="white-space:pre"> </span>// String filename = URLEncoder.encode(url); <span style="white-space:pre"> </span>File f = new File(cacheDir, url); <span style="white-space:pre"> </span>if(f.exists()){ <span style="white-space:pre"> </span>return BitmapFactory.decodeFile(f.getAbsolutePath()); <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>return null; <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>public void saveBitmapToFile(Bitmap bm ,String url) { <span style="white-space:pre"> </span>File file=new File(cacheDir,url);//將要保存圖片的路徑 <span style="white-space:pre"> </span>try { <span style="white-space:pre"> </span>BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file)); <span style="white-space:pre"> </span>bm.compress(Bitmap.CompressFormat.JPEG, 100, bos); <span style="white-space:pre"> </span>bos.flush(); <span style="white-space:pre"> </span>bos.close(); <span style="white-space:pre"> </span>} catch (IOException e) { <span style="white-space:pre"> </span>e.printStackTrace(); <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>public void clear() { <span style="white-space:pre"> </span>File[] files = cacheDir.listFiles(); <span style="white-space:pre"> </span>if (files == null) <span style="white-space:pre"> </span>return; <span style="white-space:pre"> </span>for (File f : files) <span style="white-space:pre"> </span>f.delete(); <span style="white-space:pre"> </span>} }
package com.panpass.main; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import android.graphics.Bitmap; import android.util.Log; public class ImageMemoryCache { <span style="white-space:pre"> </span>private static final String TAG = "MemoryCache"; <span style="white-space:pre"> </span>/** <span style="white-space:pre"> </span> * 放入緩存時是個同步操作 LinkedHashMap構造方法的最後一個參數true代表這個map裏的元素將依照最 近使用次數由少到多排列, <span style="white-space:pre"> </span> * 即LRU。 <span style="white-space:pre"> </span> * 這種優點是假設要將緩存中的元素替換,則先遍歷出近期最少使用的元素來替換以提高效率 <span style="white-space:pre"> </span> */ <span style="white-space:pre"> </span>private Map<String, Bitmap> cache = Collections <span style="white-space:pre"> </span>.synchronizedMap(new LinkedHashMap<String, Bitmap>(10, 1.5f, true)); <span style="white-space:pre"> </span>// 緩存中圖片所占用的字節,初始0,將通過此變量嚴格控制緩存所占用的堆內存 <span style="white-space:pre"> </span>private long size = 0;// current allocated size <span style="white-space:pre"> </span>// 緩存僅僅能占用的最大堆內存 <span style="white-space:pre"> </span>private long limit = 1000000;// max memory in bytes <span style="white-space:pre"> </span>public ImageMemoryCache() { <span style="white-space:pre"> </span>// use 25% of available heap size <span style="white-space:pre"> </span>setLimit(Runtime.getRuntime().maxMemory() / 4); <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>public void setLimit(long new_limit) { <span style="white-space:pre"> </span>limit = new_limit; <span style="white-space:pre"> </span>Log.i(TAG, "MemoryCache will use up to " + limit / 1024. / 1024. + "MB"); <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>public Bitmap getBitmapFromMemory(String id) { <span style="white-space:pre"> </span>try { <span style="white-space:pre"> </span>if (!cache.containsKey(id)) <span style="white-space:pre"> </span>return null; <span style="white-space:pre"> </span>return cache.get(id); <span style="white-space:pre"> </span>} catch (NullPointerException ex) { <span style="white-space:pre"> </span>return null; <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>public void addBitmapToMemory(String id, Bitmap bitmap) { <span style="white-space:pre"> </span>try { <span style="white-space:pre"> </span>if (cache.containsKey(id)) <span style="white-space:pre"> </span>size -= getSizeInBytes(cache.get(id)); <span style="white-space:pre"> </span>cache.put(id, bitmap); <span style="white-space:pre"> </span>size += getSizeInBytes(bitmap); <span style="white-space:pre"> </span>checkSize(); <span style="white-space:pre"> </span>} catch (Throwable th) { <span style="white-space:pre"> </span>th.printStackTrace(); <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>/** <span style="white-space:pre"> </span> * 嚴格控制堆內存,假設超過將首先替換近期最少使用的那個圖片緩存 <span style="white-space:pre"> </span> * <span style="white-space:pre"> </span> */ <span style="white-space:pre"> </span>private void checkSize() { <span style="white-space:pre"> </span>Log.i(TAG, "cache size=" + size + " length=" + cache.size()); <span style="white-space:pre"> </span>if (size > limit) { <span style="white-space:pre"> </span>// 先遍歷近期最少使用的元素 <span style="white-space:pre"> </span>Iterator<Entry<String, Bitmap>> iter = cache.entrySet().iterator(); <span style="white-space:pre"> </span>while (iter.hasNext()) { <span style="white-space:pre"> </span>Entry<String, Bitmap> entry = iter.next(); <span style="white-space:pre"> </span>size -= getSizeInBytes(entry.getValue()); <span style="white-space:pre"> </span>iter.remove(); <span style="white-space:pre"> </span>if (size <= limit) <span style="white-space:pre"> </span>break; <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>Log.i(TAG, "Clean cache. New size " + cache.size()); <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>public void clear() { <span style="white-space:pre"> </span>cache.clear(); <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>/** <span style="white-space:pre"> </span> * 圖片占用的內存 <span style="white-space:pre"> </span> * <span style="white-space:pre"> </span> * @param bitmap <span style="white-space:pre"> </span> * @return <span style="white-space:pre"> </span> */ <span style="white-space:pre"> </span>long getSizeInBytes(Bitmap bitmap) { <span style="white-space:pre"> </span>if (bitmap == null) <span style="white-space:pre"> </span>return 0; <span style="white-space:pre"> </span>return bitmap.getRowBytes() * bitmap.getHeight(); <span style="white-space:pre"> </span>} }
package com.panpass.main; import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.Window; import android.widget.ImageView; import com.example.imgdemo.R; public class MainActivity extends Activity { <span style="white-space:pre"> </span>private Context mContext = this; <span style="white-space:pre"> </span>private ImageView img1,img2,img3,img4,img5; <span style="white-space:pre"> </span>@Override <span style="white-space:pre"> </span>protected void onCreate(Bundle savedInstanceState) { <span style="white-space:pre"> </span>super.onCreate(savedInstanceState); <span style="white-space:pre"> </span>getWindow().requestFeature(Window.FEATURE_NO_TITLE); <span style="white-space:pre"> </span>setContentView(R.layout.main_acticity); <span style="white-space:pre"> </span>img1 = (ImageView) findViewById(R.id.img1); <span style="white-space:pre"> </span>img2 = (ImageView) findViewById(R.id.img2); <span style="white-space:pre"> </span>img3 = (ImageView) findViewById(R.id.img3); <span style="white-space:pre"> </span>img4 = (ImageView) findViewById(R.id.img4); <span style="white-space:pre"> </span>img5 = (ImageView) findViewById(R.id.img5); <span style="white-space:pre"> </span>ImageManager.getInstance(mContext).loadBitmap("https://www.baidu.com/img/baidu_jgylogo3.gif?v=26599715.gif", new Handler(){ <span style="white-space:pre"> </span>@Override <span style="white-space:pre"> </span>public void handleMessage(Message msg) { <span style="white-space:pre"> </span>super.handleMessage(msg); <span style="white-space:pre"> </span>img1.setImageBitmap((Bitmap)msg.obj); <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>}); <span style="white-space:pre"> </span>ImageManager.getInstance(mContext).loadBitmap("https://www.baidu.com/img/baidu_jgylogo3.gif?v=26599715.gif", new Handler(){ <span style="white-space:pre"> </span>@Override <span style="white-space:pre"> </span>public void handleMessage(Message msg) { <span style="white-space:pre"> </span>super.handleMessage(msg); <span style="white-space:pre"> </span>img2.setImageBitmap((Bitmap)msg.obj); <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>}); <span style="white-space:pre"> </span>ImageManager.getInstance(mContext).loadBitmap("https://www.baidu.com/img/baidu_jgylogo3.gif?v=26599715.gif", new Handler(){ <span style="white-space:pre"> </span>@Override <span style="white-space:pre"> </span>public void handleMessage(Message msg) { <span style="white-space:pre"> </span>super.handleMessage(msg); <span style="white-space:pre"> </span>img3.setImageBitmap((Bitmap)msg.obj); <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>}); <span style="white-space:pre"> </span>ImageManager.getInstance(mContext).loadBitmap("https://www.baidu.com/img/baidu_jgylogo3.gif?v=26599715.gif", new Handler(){ <span style="white-space:pre"> </span>@Override <span style="white-space:pre"> </span>public void handleMessage(Message msg) { <span style="white-space:pre"> </span>super.handleMessage(msg); <span style="white-space:pre"> </span>img4.setImageBitmap((Bitmap)msg.obj); <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>}); <span style="white-space:pre"> </span>ImageManager.getInstance(mContext).loadBitmap("https://www.baidu.com/img/baidu_jgylogo3.gif?
v=26599715.gif", new Handler(){ <span style="white-space:pre"> </span>@Override <span style="white-space:pre"> </span>public void handleMessage(Message msg) { <span style="white-space:pre"> </span>super.handleMessage(msg); <span style="white-space:pre"> </span>img5.setImageBitmap((Bitmap)msg.obj); <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>}); <span style="white-space:pre"> </span>//<span style="white-space:pre"> </span>img2.setImageBitmap(ImageManager.getInstance(mContext).getBitmap("https://www.baidu.com/img/baidu_jgylogo3.gif?
v=26599715.gif")); <span style="white-space:pre"> </span>// <span style="white-space:pre"> </span>//<span style="white-space:pre"> </span>img3.setImageBitmap(ImageManager.getInstance(mContext).getBitmap("https://www.baidu.com/img/baidu_jgylogo3.gif?v=26599715.gif")); <span style="white-space:pre"> </span>//<span style="white-space:pre"> </span>img4.setImageBitmap(ImageManager.getInstance(mContext).getBitmap("https://www.baidu.com/img/baidu_jgylogo3.gif?v=26599715.gif")); <span style="white-space:pre"> </span>//<span style="white-space:pre"> </span>img5.setImageBitmap(ImageManager.getInstance(mContext).getBitmap("https://www.baidu.com/img/baidu_jgylogo3.gif?v=26599715.gif")); <span style="white-space:pre"> </span>} }
Android 內存+文件+網絡三級緩存