android上的一个网络接口和图片缓存框架enif简析
来源:互联网 发布:座机电话录音软件 编辑:程序博客网 时间:2024/05/21 07:09
android上的一个网络接口和图片缓存框架enif详细介绍:底层网络接口采用apache的httpclient连接池框架、图片缓存采用基于LRU的算法等等,需要了解的朋友可以详细参考下
1.底层网络接口采用apache的httpclient连接池框架;
2.图片缓存采用基于LRU的算法;
3.网络接口采用监听者模式;
4.包含图片的OOM处理(及时回收处理技术的应用);
图片核心处理类:CacheView.java
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 recycle的时候的3个条件缺一不可;
2)onDraw里面判断图片是否被回收,如果回收,需要设置默认图片;
3)add bitmap到cache的时候Runtime.getRuntime().gc();的调用;
4)getCacheSize可以根据手机具体的内存来动态设置我们实际需要的缓存大小;
5)退出时,记得调用recycle()方法;
网络接口核心类:WSAPI.java, WSCfg.java, WSTask.java
<STRONG>package xiaogang.enif.net;
import java.util.ArrayList;
import org.apache.http.message.BasicNameValuePair;
/**
* web service configuration file
* */
public class WSCfg {
public static final int USER_LOGIN = 0;//action
public static final int USER_LOGOUT = 1;//action
public static ArrayList<BasicNameValuePair> sValuePairs;//common vps
static {
sValuePairs = new ArrayList<BasicNameValuePair>();
sValuePairs.add(new BasicNameValuePair("v", "1.0"));
sValuePairs.add(new BasicNameValuePair("format", "json"));
}
}</STRONG>
<STRONG>package xiaogang.enif.net;
import java.util.ArrayList;
import java.util.concurrent.RejectedExecutionException;
import org.apache.http.message.BasicNameValuePair;
import xiaogang.enif.net.WSTask.TaskListener;
import android.content.Context;
public class WSAPI {
private WSAPI() {
}
public static void execute(Context context, TaskListener listener, int action,
ArrayList<BasicNameValuePair> vp) {
try {
new WSTask(context, listener, action, vp).execute();
} catch (RejectedExecutionException e) {
// do nothing, just keep not crashing.
}
}
}
</STRONG>
<STRONG>package xiaogang.enif.net;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import xiaogang.enif.utils.HttpManager;
import xiaogang.enif.utils.IOUtils;
import xiaogang.enif.utils.LogUtils;
import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask;
import android.text.TextUtils;
public class WSTask extends AsyncTask<Void, Void, Object> {
private int mAction;
private String mErrorCode;
private Object mParameter;
private Context mContext;
private TaskListener mTaskListener;
private Exception mReason;
private final LogUtils mLog = LogUtils.getLog(WSTask.class);
public WSTask(Context context, TaskListener listener, int action, Object paramObject) {
mContext = context;
mTaskListener = listener;
mParameter = paramObject;
mAction = action;
}
@Override
public Object doInBackground(Void... arg0) {
Object result = null;
try {
@SuppressWarnings("unchecked")
ArrayList<BasicNameValuePair> vps = (ArrayList<BasicNameValuePair>)mParameter;
final String jsonString = request(mContext, "your url", vps);
mLog.debug(jsonString);
result = parseJson(jsonString);
if (result != null && result instanceof String
&& TextUtils.isDigitsOnly((String)result)) {
mErrorCode = (String)result;
return null;
}
} catch (Exception e) {
mReason = e;
mLog.error(e.getMessage());
return null;
}
return result;
}
@Override
public void onPostExecute(Object result) {
if (mContext== null) {
clearTask();
return;
}
if (mContext instanceof Activity && ((Activity) mContext).isFinishing()) {
clearTask();
return;
}
if (result == null || mReason != null) {
mTaskListener.onFailed(mAction, mErrorCode, mReason);
} else {
mTaskListener.onSuccess(mAction, result);
}
clearTask();
}
private String request(Context context, String url, ArrayList<BasicNameValuePair> vp)
throws IOException {
final HttpPost post = new HttpPost(url);
post.setEntity(new UrlEncodedFormEntity(vp, "UTF_8"));
InputStream in = null;
HttpEntity entity = null;
try {
final HttpResponse response = HttpManager.execute(context, post);
final int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == HttpStatus.SC_OK) {
entity = response.getEntity();
if (entity != null) {
in = entity.getContent();
return IOUtils.stream2String(in);
}
} else {
post.abort();
mLog.error("http code: " + response.getStatusLine().getStatusCode());
}
return null;
} catch (IOException ex) {
post.abort();
throw ex;
} catch (RuntimeException ex) {
post.abort();
throw ex;
} finally {
if(entity!=null) {
entity.consumeContent();
}
IOUtils.closeStream(in);
}
}
private Object parseJson(String jsonString) throws IOException {
try {
JSONObject jobj = new JSONObject(jsonString);
if (jobj.has("errorcode")) {
return jobj.optString("errorcode");
}
if (jobj.has("resultlist")) {
ArrayList<HashMap<String, String>> arrList;
arrList = new ArrayList<HashMap<String, String>>();
JSONArray jsonArray = jobj.optJSONArray("resultlist");
final int len = jsonArray.length();
for (int i = 0; i < len; i++) {
final JSONObject obj = (JSONObject)jsonArray.opt(i);
arrList.add(parse2Map(obj));
}
return arrList;
} else {
return parse2Map(jobj);
}
} catch (JSONException e) {
IOException ioe = new IOException("Invalid json String...");
ioe.initCause(e);
throw ioe;
}
}
private HashMap<String, String> parse2Map(JSONObject jsonObj) throws IOException {
final HashMap<String, String> hashMap = new HashMap<String, String>();
@SuppressWarnings("unchecked")
final Iterator<String> keyIter = jsonObj.keys();
String key, value;
while (keyIter != null && keyIter.hasNext()) {
key = keyIter.next();
value = jsonObj.optString(key);
hashMap.put(key, value);
}
return hashMap;
}
private void clearTask() {
mTaskListener = null;
mParameter = null;
mContext = null;
}
public interface TaskListener {
public void onSuccess(int action, Object result);
public void onFailed(int action, String errcode, Exception ex);
}
}
</STRONG>
说明:
1)根据你的服务器接口实际情况,去修改parseJson方法;
2)WSCfg里面可以定义接口的action;
sample:
package xiaogang.enif.ui;
import java.util.ArrayList;
import org.apache.http.message.BasicNameValuePair;
import xiaogang.enif.R;
import xiaogang.enif.image.CacheView;
import xiaogang.enif.net.WSAPI;
import xiaogang.enif.net.WSCfg;
import xiaogang.enif.net.WSTask.TaskListener;
import xiaogang.enif.widget.ListsApdater;
import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;
public class MainActivity extends Activity implements TaskListener {
ListView mList;
ListsApdater mAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
setupViews();
}
private void setupViews() {
mList = (ListView)findViewById(R.id.list);
mAdapter = new ListsApdater(this, mUrls);
mList.setAdapter(mAdapter);
final ArrayList<BasicNameValuePair> vp = new ArrayList<BasicNameValuePair>();
vp.addAll(WSCfg.sValuePairs);
vp.add(new BasicNameValuePair("imei", "123"));
vp.add(new BasicNameValuePair("imsi", "123"));
WSAPI.execute(this, this, WSCfg.USER_LOGIN, vp);
}
@Override
protected void onDestroy() {
super.onDestroy();
mAdapter.recycle();
CacheView.recycle();
}
private String[] mUrls = {
"http://a3.twimg.com/profile_images/670625317/aam-logo-v3-twitter.png",
"http://a3.twimg.com/profile_images/740897825/AndroidCast-350_normal.png",
"http://a3.twimg.com/profile_images/121630227/Droid_normal.jpg",
"http://a1.twimg.com/profile_images/957149154/twitterhalf_normal.jpg",
"http://a1.twimg.com/profile_images/97470808/icon_normal.png",
"http://a3.twimg.com/profile_images/511790713/AG.png",
"http://a3.twimg.com/profile_images/956404323/androinica-avatar_normal.png",
"http://a1.twimg.com/profile_images/909231146/Android_Biz_Man_normal.png",
"http://a3.twimg.com/profile_images/72774055/AndroidHomme-LOGO_normal.jpg",
"http://a1.twimg.com/profile_images/349012784/android_logo_small_normal.jpg",
"http://a1.twimg.com/profile_images/841338368/ea-twitter-icon.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a3.twimg.com/profile_images/77641093/AndroidPlanet_normal.png",
"http://a1.twimg.com/profile_images/850960042/elandroidelibre-logo_300x300_normal.jpg",
"http://a1.twimg.com/profile_images/655119538/andbook.png",
"http://a3.twimg.com/profile_images/768060227/ap4u_normal.jpg",
"http://a1.twimg.com/profile_images/74724754/android_logo_normal.png",
"http://a3.twimg.com/profile_images/681537837/SmallAvatarx150_normal.png",
"http://a1.twimg.com/profile_images/63737974/2008-11-06_1637_normal.png",
"http://a3.twimg.com/profile_images/548410609/icon_8_73.png",
"http://a1.twimg.com/profile_images/612232882/nexusoneavatar_normal.jpg",
"http://a1.twimg.com/profile_images/213722080/Bugdroid-phone_normal.png",
"http://a1.twimg.com/profile_images/645523828/OT_icon_090918_android_normal.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a3.twimg.com/profile_images/77641093/AndroidPlanet.png",
"http://a1.twimg.com/profile_images/850960042/elandroidelibre-logo_300x300_normal.jpg",
"http://a1.twimg.com/profile_images/655119538/andbook_normal.png",
"http://a3.twimg.com/profile_images/511790713/AG_normal.png",
"http://a3.twimg.com/profile_images/956404323/androinica-avatar.png",
"http://a1.twimg.com/profile_images/909231146/Android_Biz_Man_normal.png",
"http://a3.twimg.com/profile_images/72774055/AndroidHomme-LOGO_normal.jpg",
"http://a1.twimg.com/profile_images/349012784/android_logo_small_normal.jpg",
"http://a1.twimg.com/profile_images/841338368/ea-twitter-icon_normal.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a3.twimg.com/profile_images/77641093/AndroidPlanet.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a1.twimg.com/profile_images/850960042/elandroidelibre-logo_300x300.jpg",
"http://a1.twimg.com/profile_images/655119538/andbook_normal.png",
"http://a3.twimg.com/profile_images/511790713/AG_normal.png",
"http://a3.twimg.com/profile_images/956404323/androinica-avatar_normal.png",
"http://a1.twimg.com/profile_images/909231146/Android_Biz_Man_normal.png",
"http://a3.twimg.com/profile_images/121630227/Droid.jpg",
"http://a1.twimg.com/profile_images/97470808/icon_normal.png",
"http://a3.twimg.com/profile_images/511790713/AG_normal.png",
"http://a3.twimg.com/profile_images/956404323/androinica-avatar_normal.png",
"http://a1.twimg.com/profile_images/909231146/Android_Biz_Man.png",
"http://a3.twimg.com/profile_images/72774055/AndroidHomme-LOGO_normal.jpg",
"http://a1.twimg.com/profile_images/349012784/android_logo_small_normal.jpg",
"http://a1.twimg.com/profile_images/841338368/ea-twitter-icon_normal.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a3.twimg.com/profile_images/77641093/AndroidPlanet.png",
"http://a3.twimg.com/profile_images/121630227/Droid_normal.jpg",
"http://a1.twimg.com/profile_images/957149154/twitterhalf_normal.jpg",
"http://a1.twimg.com/profile_images/97470808/icon.png",
"http://a3.twimg.com/profile_images/511790713/AG_normal.png",
"http://a1.twimg.com/profile_images/909231146/Android_Biz_Man_normal.png",
"http://a3.twimg.com/profile_images/72774055/AndroidHomme-LOGO_normal.jpg",
"http://a1.twimg.com/profile_images/349012784/android_logo_small_normal.jpg",
"http://a1.twimg.com/profile_images/841338368/ea-twitter-icon.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a3.twimg.com/profile_images/77641093/AndroidPlanet_normal.png",
"http://a1.twimg.com/profile_images/850960042/elandroidelibre-logo_300x300_normal.jpg",
"http://a1.twimg.com/profile_images/655119538/andbook_normal.png",
"http://a3.twimg.com/profile_images/768060227/ap4u_normal.jpg",
"http://a1.twimg.com/profile_images/74724754/android_logo.png",
"http://a3.twimg.com/profile_images/681537837/SmallAvatarx150_normal.png",
"http://a1.twimg.com/profile_images/63737974/2008-11-06_1637_normal.png",
"http://a3.twimg.com/profile_images/548410609/icon_8_73_normal.png",
"http://a1.twimg.com/profile_images/612232882/nexusoneavatar_normal.jpg",
"http://a1.twimg.com/profile_images/213722080/Bugdroid-phone_normal.png",
"http://a1.twimg.com/profile_images/645523828/OT_icon_090918_android.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a3.twimg.com/profile_images/77641093/AndroidPlanet_normal.png",
"http://a1.twimg.com/profile_images/850960042/elandroidelibre-logo_300x300_normal.jpg",
"http://a1.twimg.com/profile_images/655119538/andbook.png",
"http://a3.twimg.com/profile_images/956404323/androinica-avatar_normal.png",
"http://a1.twimg.com/profile_images/909231146/Android_Biz_Man_normal.png",
"http://a3.twimg.com/profile_images/72774055/AndroidHomme-LOGO_normal.jpg",
"http://a1.twimg.com/profile_images/349012784/android_logo_small_normal.jpg",
"http://a1.twimg.com/profile_images/841338368/ea-twitter-icon.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a3.twimg.com/profile_images/77641093/AndroidPlanet_normal.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a1.twimg.com/profile_images/850960042/elandroidelibre-logo_300x300_normal.jpg",
"http://a1.twimg.com/profile_images/655119538/andbook_normal.png",
"http://a3.twimg.com/profile_images/511790713/AG_normal.png",
"http://a3.twimg.com/profile_images/956404323/androinica-avatar_normal.png",
"http://a1.twimg.com/profile_images/909231146/Android_Biz_Man_normal.png",
"http://a3.twimg.com/profile_images/121630227/Droid_normal.jpg",
"http://a1.twimg.com/profile_images/957149154/twitterhalf.jpg",
"http://a1.twimg.com/profile_images/97470808/icon_normal.png",
"http://a3.twimg.com/profile_images/511790713/AG_normal.png",
"http://a1.twimg.com/profile_images/909231146/Android_Biz_Man_normal.png",
"http://a3.twimg.com/profile_images/72774055/AndroidHomme-LOGO_normal.jpg",
"http://a1.twimg.com/profile_images/841338368/ea-twitter-icon_normal.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a3.twimg.com/profile_images/77641093/AndroidPlanet_normal.png"
};
@Override
public void onSuccess(int action, Object result) {
switch (action) {
case WSCfg.USER_LOGIN:
break;
case WSCfg.USER_LOGOUT:
break;
}
}
@Override
public void onFailed(int action, String errcode, Exception ex) {
switch (action) {
case WSCfg.USER_LOGIN:
break;
case WSCfg.USER_LOGOUT:
break;
}
}
}
package xiaogang.enif.widget;
import xiaogang.enif.R;
import xiaogang.enif.image.CacheView;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
public class ListsApdater extends BaseAdapter {
private String[] mUrls;
private LayoutInflater mInflater;
public ListsApdater(Context context, String[] urls) {
mUrls = urls;
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public int getCount() {
return mUrls.length;
}
@Override
public Object getItem(int position) {
return position;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (null == convertView) {
holder = new ViewHolder();
convertView = mInflater.inflate(R.layout.item, null);
holder.view = (CacheView)convertView.findViewById(R.id.image);
holder.text = (TextView)convertView.findViewById(R.id.text);
convertView.setTag(holder);
} else {
holder = (ViewHolder)convertView.getTag();
}
holder.text.setText("item "+position);
holder.view.setImageUrl(mUrls[position], R.drawable.stub);
return convertView;
}
public void recycle() {
mUrls = null;
mInflater = null;
}
private class ViewHolder {
CacheView view;
TextView text;
}
}
main.xml和item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ListView
android:id="@+id/list"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1" />
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<xiaogang.enif.image.CacheView
android:id="@+id/image"
android:layout_width="50dip"
android:layout_height="50dip"
android:scaleType="centerCrop"
android:src="@drawable/stub" />
<TextView
android:id="@+id/text"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_gravity="left|center_vertical"
android:layout_marginLeft="10dip"
android:layout_weight="1"
android:textSize="20dip" />
</LinearLayout>
例子的效果图如下:
android中图片的三级缓存cache策略(内存/文件/网络)
实现图片缓存也不难,需要有相应的cache策略。这里我采用 内存-文件-网络 三层cache机制,其中内存缓存包括强引用缓存和软引用缓存(SoftReference),其实网络不算cache,这里姑且也把它划到缓存的层次结构中
1.简介
现在android应用中不可避免的要使用图片,有些图片是可以变化的,需要每次启动时从网络拉取,这种场景在有广告位的应用以及纯图片应用(比如百度美拍)中比较多。
现在有一个问题:假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量。在当前的状况下,对于非wifi用户来说,流量还是很贵的,一个很耗流量的应用,其用户数量级肯定要受到影响。当然,我想,向百度美拍这样的应用,必然也有其内部的图片缓存策略。总之,图片缓存是很重要而且是必须的。
2.图片缓存的原理
实现图片缓存也不难,需要有相应的cache策略。这里我采用 内存-文件-网络 三层cache机制,其中内存缓存包括强引用缓存和软引用缓存(SoftReference),其实网络不算cache,这里姑且也把它划到缓存的层次结构中。当根据url向网络拉取图片的时候,先从内存中找,如果内存中没有,再从缓存文件中查找,如果缓存文件中也没有,再从网络上通过http请求拉取图片。在键值对(key-value)中,这个图片缓存的key是图片url的hash值,value就是bitmap。所以,按照这个逻辑,只要一个url被下载过,其图片就被缓存起来了。
关于Java中对象的软引用(SoftReference),如果一个对象具有软引用,内存空间足够,垃 圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高 速缓存。使用软引用能防止内存泄露,增强程序的健壮性。
从代码上来说,采用一个ImageManager来负责图片的管理和缓存,函数接口为public void loadBitmap(String url, Handler handler) ;其中url为要下载的图片地址,handler为图片下载成功后的回调,在handler中处理message,而message中包含了图片的信息以及bitmap对象。ImageManager中使用的ImageMemoryCache(内存缓存)、ImageFileCache(文件缓存)以及LruCache(最近最久未使用缓存)会在后续文章中介绍。
3.代码ImageManager.java
/*
* 图片管理
* 异步获取图片,直接调用loadImage()函数,该函数自己判断是从缓存还是网络加载
* 同步获取图片,直接调用getBitmap()函数,该函数自己判断是从缓存还是网络加载
* 仅从本地获取图片,调用getBitmapFromNative()
* 仅从网络加载图片,调用getBitmapFromHttp()
*
*/
public class ImageManager implements IManager
{
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 ImageManager()
{
imageMemoryCache = new ImageMemoryCache();
imageFileCache = new ImageFileCache();
}
/**
* 获取图片,多线程的入口
*/
public void loadBitmap(String url, Handler handler)
{
//先从内存缓存中获取,取到直接加载
Bitmap bitmap = getBitmapFromNative(url);
if (bitmap != null)
{
Logger.d(TAG, "loadBitmap:loaded from native");
Message msg = Message.obtain();
Bundle bundle = new Bundle();
bundle.putString("url", url);
msg.obj = bitmap;
msg.setData(bundle);
handler.sendMessage(msg);
}
else
{
Logger.d(TAG, "loadBitmap:will load by network");
downloadBmpOnNewThread(url, handler);
}
}
/**
* 新起线程下载图片
*/
private void downloadBmpOnNewThread(final String url, final Handler handler)
{
Logger.d(TAG, "ongoingTaskMap'size=" + ongoingTaskMap.size());
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(url);
// 不论下载是否成功,都从下载队列中移除,再由业务逻辑判断是否重新下载
// 下载图片使用了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 url)
{
// 从内存缓存中获取图片
Bitmap bitmap = imageMemoryCache.getBitmapFromMemory(url);
if (bitmap == null)
{
// 文件缓存中获取
bitmap = imageFileCache.getImageFromFile(url);
if (bitmap != null)
{
// 添加到内存缓存
imageMemoryCache.addBitmapToMemory(url, bitmap);
}
else
{
// 从网络获取
bitmap = getBitmapFromHttp(url);
}
}
return bitmap;
}
/**
* 从内存或者缓存文件中获取bitmap
*/
public Bitmap getBitmapFromNative(String url)
{
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 url)
{
Bitmap bmp = null;
try
{
byte[] tmpPicByte = getImageBytes(url);
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 url)
{
byte[] pic = null;
if (url != null && !"".equals(url))
{
Requester request = RequesterFactory.getRequester(
Requester.REQUEST_REMOTE, RequesterFactory.IMPL_HC);
// 执行请求
MyResponse myResponse = null;
MyRequest mMyRequest;
mMyRequest = new MyRequest();
mMyRequest.setUrl(url);
mMyRequest.addHeader(HttpHeader.REQ.ACCEPT_ENCODING, "identity");
InputStream is = null;
ByteArrayOutputStream baos = null;
try {
myResponse = request.execute(mMyRequest);
is = myResponse.getInputStream().getImpl();
baos = new ByteArrayOutputStream();
byte[] b = new byte[512];
int len = 0;
while ((len = is.read(b)) != -1)
{
baos.write(b, 0, len);
baos.flush();
}
pic = baos.toByteArray();
Logger.d(TAG, "icon bytes.length=" + pic.length);
}
catch (Exception e3)
{
e3.printStackTrace();
try
{
Logger.e(TAG,
"download shortcut icon faild and responsecode="
+ myResponse.getStatusCode());
}
catch (Exception e4)
{
e4.printStackTrace();
}
}
finally
{
try
{
if (is != null)
{
is.close();
is = null;
}
}
catch (Exception e2)
{
e2.printStackTrace();
}
try
{
if (baos != null)
{
baos.close();
baos = null;
}
}
catch (Exception e2)
{
e2.printStackTrace();
}
try
{
request.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
}
return pic;
}
/**
* 取出等待队列第一个任务,开始下载
*/
private void startDownloadNext()
{
synchronized(waitingTaskMap)
{
Logger.d(TAG, "begin start next");
Iterator iter = waitingTaskMap.entrySet().iterator();
while (iter.hasNext())
{
Map.Entry entry = (Map.Entry) iter.next();
Logger.d(TAG, "WaitingTaskMap isn't null,url=" + (String)entry.getKey());
if(entry != null)
{
waitingTaskMap.remove(entry.getKey());
downloadBmpOnNewThread((String)entry.getKey(), (Handler)entry.getValue());
}
break;
}
}
}
public String startDownloadNext_ForUnitTest()
{
String urlString = null;
synchronized(waitingTaskMap)
{
Logger.d(TAG, "begin start next");
Iterator iter = waitingTaskMap.entrySet().iterator();
while (iter.hasNext())
{
Map.Entry entry = (Map.Entry) iter.next();
urlString = (String)entry.getKey();
waitingTaskMap.remove(entry.getKey());
break;
}
}
return urlString;
}
/**
* 图片变为圆角
* @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;
}
public byte managerId()
{
return IMAGE_ID;
}
}
- android上的一个网络接口和图片缓存框架enif简析
- android上的一个网络接口和图片缓存框架enif
- android上的一个网络接口和图片缓存框架enif
- android 上的一个网络接口和图片框架
- Android之网络图片框架UniversalImageLoader和结合LruCache缓存图片
- Picasso 强大的网络图片缓存框架
- Android性能优化之图片的本地和网络缓存
- Android:android的框架区别(网络框架、图片异步加载与缓存框架、数据框架)特点整理
- Android网络图片缓存
- Android 网络图片缓存
- Android缓存机制:LruCache+AsyncTask缓存和显示网络图片
- Android Picasso图片缓存框架的使用
- Android 主流网络图片加载缓存库框架Universal-ImageLoader的使用
- Android网络图片的三级缓存
- Android如何优雅的缓存网络图片
- 继文章‘’ 自己动手写一个轻量级的Android网络请求框架‘’后续------增加缓存功能
- 做一个简单的Android图片缓存
- android上的缓存、缓存算法和缓存框架
- 设计模式(一):里氏代换原则
- android异步加载图片并缓存到本地实现方法
- 14-斐波那契(算法)
- WinDbg调试器
- 1亿个整数求top 10000
- android上的一个网络接口和图片缓存框架enif简析
- JAVA学习之:类与类之间的关系
- 理解not exists()——执行过程
- Zend Framework 数据库(Db)操作总结
- 一套网络配置随时随地上网
- EAP协议和EAPoL协议
- Gesture Recognizers与触摸事件分发
- 高级Bash脚本编程指南(26):文本处理命令(二)
- python3.0与2.x之间的区别