1. 程式人生 > >Android 內存+文件+網絡三級緩存

Android 內存+文件+網絡三級緩存

util andro final map.entry 邏輯 getent factory 文件名 canvas

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 內存+文件+網絡三級緩存