1:网络的底层环境 采用apache 的httpClient 链接池框架
2:图片缓存采用基于LRU 的算法
3:网络接口采用监听者模式
4 包含图片的OOM 处理(及时回收处理技术的应用)
package xiaogang.enif.image;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.concurrent.RejectedExecutionException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;
import xiaogang.enif.utils.HttpManager;
import xiaogang.enif.utils.IOUtils;
import xiaogang.enif.utils.LogUtils;
import xiaogang.enif.utils.LruCache;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.os.AsyncTask;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.widget.ImageView;
public class CacheView extends ImageView {
private static final int DEFAULT_RES_ID = 0;
private int mDefaultImage = DEFAULT_RES_ID;
private static LruCache<String, Bitmap> mLruCache;
private static HashMap<Integer, SoftReference<Bitmap>> mResImage;
private Context mContext;
private LogUtils mLog = LogUtils.getLog(CacheView.class);
public CacheView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
public CacheView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public CacheView(Context context) {
super(context);
init(context);
}
private void init(Context context) {
mContext = context;
if (mLruCache == null) {
final int cacheSize = getCacheSize(context);
mLruCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in bytes rather than
// number of items.
return bitmap.getRowBytes() * bitmap.getHeight();
}
@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue,
Bitmap newValue) {
if (evicted && oldValue != null && !oldValue.isRecycled()) {
oldValue.recycle();
oldValue = null;
}
}
};
}
if (mResImage == null) {
mResImage = new HashMap<Integer, SoftReference<Bitmap>>();
}
}
@Override
protected void onDraw(Canvas canvas) {
BitmapDrawable drawable = (BitmapDrawable)getDrawable();
if (drawable == null) {
setDefaultImage();
} else {
if (drawable.getBitmap() == null || drawable.getBitmap().isRecycled()) {
setDefaultImage();
}
}
try {
super.onDraw(canvas);
} catch(RuntimeException ex) {
}
}
public void setImageUrl(String url, int resId) {
setTag(url);
Bitmap bitmap = getBitmapFromCache(url);
if (bitmap == null || bitmap.isRecycled()) {
mDefaultImage = resId;
setDefaultImage();
try {
new DownloadTask().execute(url);
} catch (RejectedExecutionException e) {
// do nothing, just keep not crash
}
} else {
setImageBitmap(bitmap);
}
}
private void setDefaultImage() {
if (mDefaultImage != DEFAULT_RES_ID) {
setImageBitmap(getDefaultBitmap(mContext));
}
}
private Bitmap getDefaultBitmap(Context context) {
SoftReference<Bitmap> loading = mResImage.get(mDefaultImage);
if (loading == null || loading.get() == null || loading.get().isRecycled()) {
loading = new SoftReference<Bitmap>(BitmapFactory.decodeResource(
context.getResources(), mDefaultImage));
mResImage.put(mDefaultImage, loading);
}
return loading.get();
}
private class DownloadTask extends AsyncTask<String, Void, Bitmap> {
private String mParams;
@Override
public Bitmap doInBackground(String... params) {
mParams = params[0];
final Bitmap bm = download(mParams);
addBitmapToCache(mParams, bm);
return bm;
}
@Override
public void onPostExecute(Bitmap bitmap) {
String tag = (String)getTag();
if (!TextUtils.isEmpty(tag) && tag.equals(mParams)) {
if (bitmap != null) {
setImageBitmap(bitmap);
}
}
}
};
/*
* An InputStream that skips the exact number of bytes provided, unless it
* reaches EOF.
*/
static class FlushedInputStream extends FilterInputStream {
public FlushedInputStream(InputStream inputStream) {
super(inputStream);
}
@Override
public long skip(long n) throws IOException {
long totalBytesSkipped = 0L;
while (totalBytesSkipped < n) {
long bytesSkipped = in.skip(n - totalBytesSkipped);
if (bytesSkipped == 0L) {
int b = read();
if (b < 0) {
break; // we reached EOF
} else {
bytesSkipped = 1; // we read one byte
}
}
totalBytesSkipped += bytesSkipped;
}
return totalBytesSkipped;
}
}
private Bitmap download(String url) {
InputStream in = null;
HttpEntity entity = null;
Bitmap bmp = null;
try {
final HttpGet get = new HttpGet(url);
final HttpResponse response = HttpManager.execute(mContext, get);
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
entity = response.getEntity();
in = entity.getContent();
try {
bmp = getDecodeBitmap(in, url);
} catch (OutOfMemoryError err) {
Runtime.getRuntime().gc();
bmp = getDecodeBitmap(in, url);
}
} else {
get.abort();
return bmp;
}
addBitmapToCache(url, bmp);
} catch (IOException e) {
return bmp;
} finally {
IOUtils.closeStream(in);
}
return bmp;
}
private final Bitmap getDecodeBitmap(InputStream in, String url) {
Options options = new Options();
options.inPurgeable = true;
options.inInputShareable = true;
return BitmapFactory.decodeStream(new FlushedInputStream(in), null, options);
}
private final void addBitmapToCache(String url, Bitmap bitmap) {
if (bitmap != null) {
mLruCache.put(url, bitmap);
Runtime.getRuntime().gc();
}
}
private final Bitmap getBitmapFromCache(String url) {
return mLruCache.get(url);
}
private int getCacheSize(Context context) {
// According to the phone memory, set a proper cache size for LRU cache
// dynamically.
final ActivityManager am = (ActivityManager)context
.getSystemService(Context.ACTIVITY_SERVICE);
final int memClass = am.getMemoryClass();
int cacheSize;
if (memClass <= 24) {
cacheSize = (memClass << 20) / 24;
} else if (memClass <= 36) {
cacheSize = (memClass << 20) / 18;
} else if (memClass <= 48) {
cacheSize = (memClass << 20) / 12;
} else {
cacheSize = (memClass << 20) >> 3;
}
mLog.debug("cacheSize == "+cacheSize);
System.out.println("cacheSize == "+cacheSize);
return cacheSize;
}
public static void recycle() {
if (mLruCache != null && !mLruCache.isEmpty()) {
mLruCache.evictAll();
mLruCache = null;
}
if (mResImage != null) {
for (SoftReference<Bitmap> reference : mResImage.values()) {
Bitmap bitmap = reference.get();
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
bitmap = null;
}
}
mResImage = null;
}
}
}
说明:
1)entryRemoved 在做Bitmap recyle 的时候的三个条件缺一不可
2)onDraw 里面判断图片是否被回收,如果回收 需要设置默认的图片
3)add bitmap 到cache 的时候 Runtime.getRuntime().gc 的调用
转载 http://blog.csdn.net/androidzhaoxiaogang/article/details/8211649