您的位置:首页 > 理论基础 > 计算机网络

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

2015-03-19 13:39 274 查看
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>}}

                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐