如果要从网络加载N多张图,就要考虑缓存问题。图片本来就属于比较占内存的资源,下载又耗时,还要开线程。假设我们滑动ListView的速度很快,ListView的个数又多,很容易造成OOM。所以要为其加缓存。当缓存中存在某张图片的时候,直接从缓存中拿,没有的话再去网络加载。在网络上看到一个双缓存的例子,觉得不错。将强引用和弱引用结合起来,取长补短。参考别的例子,将此例略作修改,贴个完整的例子供大家参考。
先来看界面,很简单,一个自定义的ListView,为ListView设置滑动事件。
MainActivity.java
..//省略包
public class MainActivity extends Activity {
ListView lv;
LoaderAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv = (ListView) findViewById(R.id.list);
adapter = new LoaderAdapter(500, this);// 设置listview行数为500行
lv.setAdapter(adapter);
lv.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
switch (scrollState) {
case OnScrollListener.SCROLL_STATE_FLING:
adapter.setFlagBusy(true);
break;
case OnScrollListener.SCROLL_STATE_IDLE:
adapter.setFlagBusy(false);
break;
case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
adapter.setFlagBusy(false);
break;
default:
break;
}
adapter.notifyDataSetChanged();
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// TODO Auto-generated method stub
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
数据适配器:
LoadAdapter.java
..//省略包
public class LoaderAdapter extends BaseAdapter {
private static final String TAG = "LoaderAdapter";
private ImageLoader imageLoader;
private boolean mBusy = false;//设置忙碌状态,即ListView滑动或不滑动,滑动为true
public void setFlagBusy(boolean busy) {
this.mBusy = busy;
}
private static final String[] URLS = {};//图片链接,此处数据量太多因此省略
private int mCount;//item总数
private Context mContext;
public LoaderAdapter(int count, Context context) {
this.mCount = count;
this.mContext = context;
imageLoader = new ImageLoader();
}
@Override
public int getCount() {
return mCount;
}
@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 mHolder = null;
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(
R.layout.item_list, null);
mHolder = new ViewHolder();
mHolder.tv_text = (TextView) convertView.findViewById(R.id.tv_tips);
mHolder.iv_image = (ImageView) convertView
.findViewById(R.id.iv_image);
convertView.setTag(mHolder);
} else {
mHolder = (ViewHolder) convertView.getTag();
}
String url = "";
url = URLS[position % URLS.length];//因为图片数量不足500,因此让其平铺
if (mBusy) {//忙碌,从缓存中拿出图片显示,若没有,显示默认图片
Bitmap bitmap = imageLoader.getBitmapFromCache(url);
if (bitmap != null) {
mHolder.iv_image.setImageBitmap(bitmap);
} else {
mHolder.iv_image.setImageResource(R.drawable.ic_launcher);
mHolder.tv_text.setText("--" + position + "--FLING");
}
} else {// 不忙就异步下载不存在的图片
imageLoader.loadImage(url, this, mHolder);
mHolder.tv_text.setText("--" + position + "--IDLE ||TOUCH_SCROLL");
}
return convertView;
}
static class ViewHolder {
TextView tv_text;
ImageView iv_image;
}
}
最后看一下最关键的ImageLoader类:
ImageLoader.java
//省略包
public class ImageLoader {
private static final String TAG = "ImageLoader";
private int MAX_CAPACITY = 50;// 一级缓存最大空间
private long DELAY_BEFORE_PURGE = 10 * 1000;// 定时清理缓存
// 一级缓存
//此处参考网上,限制最大缓存空间,防止溢出
// 0.75是加载因子,为经验值,true则表示按照最近访问量的高低排序,false则表示按照插入顺序排序
private HashMap<String, Bitmap> mFirstLevelCache = new LinkedHashMap<String, Bitmap>(
MAX_CAPACITY / 2, 0.75f, true) {
//LinkedHashMap是序列化的,要声明serialVersionUID 用来表明类的不同版本间的兼容性。
private static final long serialVersionUID = 1L;
protected boolean removeEldestEntry(Entry<String, Bitmap> eldest) {
if (size() > MAX_CAPACITY) {// 当超过一级缓存阈值的时候,将老的值从一级缓存搬到二级缓存
mSecondLevelCache.put(eldest.getKey(),
new SoftReference<Bitmap>(eldest.getValue()));
return true;
}
return false;
};
};
// 二级缓存,采用的是软应用,只有在内存吃紧的时候软应用才会被回收,有效的避免了oom
// 此处采用ConcurrentHashMap代替LinkedHashMap,保证线程安全
private ConcurrentHashMap<String, SoftReference<Bitmap>> mSecondLevelCache = new ConcurrentHashMap<String, SoftReference<Bitmap>>(
MAX_CAPACITY / 2);
// 定时清理缓存
private Runnable mClearCache = new Runnable() {
@Override
public void run() {
clear();
}
};
private Handler mPurgeHandler = new Handler();
// 重置缓存清理的timer
private void resetPurgeTimer() {
mPurgeHandler.removeCallbacks(mClearCache);
mPurgeHandler.postDelayed(mClearCache, DELAY_BEFORE_PURGE);
}
// 清理缓存
public void clear() {
mFirstLevelCache.clear();
mSecondLevelCache.clear();
}
//添加进缓存
public void addImage2Cache(String url, Bitmap value) {
if (value == null || url == null) {
return;
}
synchronized (mFirstLevelCache) {
mFirstLevelCache.put(url, value);
}
}
/**
* 返回缓存,没有则返回null
*
* @param url
* @return
*/
public Bitmap getBitmapFromCache(String url) {
Bitmap bitmap = null;
bitmap = getFromFirstLevelCache(url);// 从一级缓存中拿
if (bitmap != null) {
return bitmap;
}
bitmap = getFromSecondLevelCache(url);// 从二级缓存中拿
return bitmap;
}
// 从二级缓存中拿
private Bitmap getFromSecondLevelCache(String url) {
Bitmap bitmap = null;
SoftReference<Bitmap> softReference = mSecondLevelCache.get(url);
if (softReference != null) {
bitmap = softReference.get();
if (bitmap == null) {// 由于内存吃紧,软引用已经被gc回收了
mSecondLevelCache.remove(url);
}
}
return bitmap;
}
// 从一级缓存中拿
private Bitmap getFromFirstLevelCache(String url) {
Bitmap bitmap = null;
synchronized (mFirstLevelCache) {
bitmap = mFirstLevelCache.get(url);
if (bitmap != null) {// 将最近访问的元素放到链的头部,提高下一次访问该元素的检索速度(LRU算法)
mFirstLevelCache.remove(url);
mFirstLevelCache.put(url, bitmap);
}
}
return bitmap;
}
/**
* 加载图片,缓存中有就从缓存中拿,没有就下载
*
* @param url
* @param adapter
* @param holder
*/
public void loadImage(String url, BaseAdapter adapter, ViewHolder holder) {
resetPurgeTimer();
Bitmap bitmap = getBitmapFromCache(url);
if (bitmap == null) {
// 缓存中没有将图片设置为默认图片
holder.iv_image.setImageResource(R.drawable.ic_launcher);
ImageLoaderTask task = new ImageLoaderTask();//开启线程下载
task.execute(url, adapter, holder);
}
}
class ImageLoaderTask extends AsyncTask<Object, Void, Bitmap> {
String url;
BaseAdapter adapter;
@Override
protected Bitmap doInBackground(Object... params) {
url = (String) params[0];
adapter = (BaseAdapter) params[1];
Bitmap bmp = loadImageFromInternet(url);// 获取网络图片
return bmp;
}
@Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
if (result == null) {
return;
}
addImage2Cache(url, result);// 放入缓存
adapter.notifyDataSetChanged();// 触发getView方法执行,这个时候getView实际上会拿到刚刚缓存好的图片
}
}
//从网络上下载
public Bitmap loadImageFromInternet(String url) {
Bitmap bitmap = null;
HttpClient client = AndroidHttpClient.newInstance("android");
HttpParams params = client.getParams();
HttpConnectionParams.setConnectionTimeout(params, 3000);
HttpConnectionParams.setSocketBufferSize(params, 3000);
HttpResponse response = null;
InputStream inputStream = null;
HttpGet httpGet = null;
try {
httpGet = new HttpGet(url);
response = client.execute(httpGet);
int stateCode = response.getStatusLine().getStatusCode();
if (stateCode != HttpStatus.SC_OK) {
Log.d(TAG, "func [loadImage] stateCode=" + stateCode);
return bitmap;
}
HttpEntity entity = response.getEntity();
if (entity != null) {
try {
inputStream = entity.getContent();
return bitmap = BitmapFactory.decodeStream(inputStream);
} finally {
if (inputStream != null) {
inputStream.close();
}
entity.consumeContent();
}
}
} catch (ClientProtocolException e) {
httpGet.abort();
e.printStackTrace();
} catch (IOException e) {
httpGet.abort();
e.printStackTrace();
} finally {
((AndroidHttpClient) client).close();
}
return bitmap;
}
}
注:以上代码均来自互联网
ok,水平有限,注释粗糙,希望大家批评指教。
附上源码:http://download.csdn.net/detail/u011665766/9420052