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>} }