ListView加载网络图片优化(加二级缓存使滑动流畅)

如果要从网络加载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


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值