Android 内存+文件+网络三级缓存

Posted ljbguanli

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 内存+文件+网络三级缓存的主要内容,如果未能解决你的问题,请参考以下文章

Android三级缓存机制工具类的实现

关于Android中的三级缓存

关于Android中的三级缓存

Android中图片的三级缓存策略

Android中图片的三级缓存策略

Glide图片加载的用法介绍和三级缓存实现