解决加载多图oom,内存和硬盘缓冲,过滤重复链接,同一视图没被缓冲前,某些view不显示的问题。

来源:互联网 发布:学淘宝要学多久时间 编辑:程序博客网 时间:2024/06/08 08:09

文章,参考自:http://blog.csdn.net/guolin_blog/article/details/34093441

不过本身自己的代码也也修改了很多。


在网络上找了很多例子,但是很多都是,不十分满意,

1.要不就是图片错乱,

2.第一次运行,如果滚动加载过快,要不就是无限多的线程,

3.要不就是在同一视图里面,如果没被缓冲,而且又有相同链接的话,某些视图即不显示出来。


网络下载,我用的是开源框格volley。


异步下载方式:
 1.在页面滚动的时候,中断线程,停止下载,
  2.页面停止的时候,开启线程下载图片。
  
  
  缓冲技术:
  1.图片显示,如果内存有,优先从内存取,
  2.如果内存没有,从硬盘取,然后保存进内存中
  3.如果所有缓冲都没有,即网络下载,然后再缓冲到硬盘和内存中(硬盘和内存里面的并非全部一样,内存有可能会溢出,会删除排在前面的一些缓冲,但是硬盘一直有,除非内存卡爆满)
  
  
  过滤相同链接的重复下载,
  1.在同一视图里,加载所有要下载的图片(key),链接(value)到map
  2.map.containvalue(url),过滤重复的链接,防止重复下载
  
  解决重复链接,没被缓冲前,在同一视图里,后面的图片不显示的问题:
  1.在下载某个链接的图片后,
  2.在map寻找一样的下载链接,
  3.把相同的链接的图片显示出来
  4.每下载完一个链接图片,在map里面删除key:作用:1.减少内存占用 2.判断是否该停止线程运行(主要)

/*** *  *  * 异步下载方式: * 1.在页面滚动的时候,中断线程,停止下载, * 2.页面停止的时候,开启线程下载图片。 *  *  * 缓冲技术: * 1.图片显示,如果内存有,从内存取, * 2.如果硬盘有,从硬盘取,然后保存进内存中 * 3.网络下载,缓冲到硬盘和内存中 *  *  * 过滤相同链接的重复下载, * 1.Hashmap<View, String> ----> 在同一视图里,加载所有要下载的图片(key),链接(value) * 2.过滤重复的链接,防止重复下载 *  * 解决重复图片不显示,在同一个视图里面,如果有重复的链接图片,并且都还没有被缓冲,以前的版本不显示的 * 1.在下载某个链接的图片后, * 2.在Hashmap<View, String>寻找一样的下载链接, * 3.就把相同的显示出来 * 4.每下载完一个链接图片,在map里面删除key,减少内存占用 * 5.在下载完map里面的所有链接,关闭线程 * 6.下次视图在开始启动线程下载图片的时候,先清空map,才加载要下载的图片(key),链接(value) */import java.util.ArrayList;import java.util.HashMap;import java.util.Iterator;import java.util.Map.Entry;import android.annotation.SuppressLint;import android.content.Context;import android.graphics.Bitmap;import android.graphics.Bitmap.Config;import android.util.Log;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.AbsListView;import android.widget.AbsListView.OnScrollListener;import android.widget.ArrayAdapter;import android.widget.GridView;import android.widget.ImageView;import android.widget.TextView;import com.android.volley.RequestQueue;import com.android.volley.Response;import com.android.volley.VolleyError;import com.android.volley.toolbox.ImageRequest;import com.android.volley.toolbox.Volley;/** * GridView的适配器,负责异步从网络上下载图片展示在照片墙上。 *  * @author guolin */public class PhotoWallAdapter extends ArrayAdapter<String> implementsOnScrollListener {/** * GridView的实例 */private GridView mPhotoWall;/** * 第一张可见图片的下标 */private int mFirstVisibleItem;/** * 一屏有多少张图片可见 */private int mVisibleItemCount;/** * 记录是否刚打开程序,用于解决进入程序不滚动屏幕,不会下载图片的问题。 */private boolean isFirstEnter = true;/** * volley的request */private RequestQueue mQueue = null;/** * 内存缓冲 */private BitmapCache mMemoryCache = null;/** * 硬盘缓冲 */private FilesUtil mHardDriveCache = null;/** * 解决同一屏幕,相同链接图片不显示的问题 */private HashMap<View, String> mSameViewUrls = null;@SuppressLint("NewApi")public PhotoWallAdapter(Context context, int textViewResourceId,String[] objects, GridView photoWall) {super(context, textViewResourceId, objects);this.mPhotoWall = photoWall;this.mPhotoWall.setOnScrollListener(this);// 初始化volley.内存缓冲,硬盘缓冲等相关参数init(context);this.mSameViewUrls = new HashMap<View, String>();}/** * 初始化volley osmdroid *  * @param context */private void init(Context context) {this.mQueue = Volley.newRequestQueue(context);this.mMemoryCache = new BitmapCache();this.mHardDriveCache = new FilesUtil(context);// this.mHardDriveCache.removeAll();}@SuppressLint("NewApi")@Overridepublic View getView(int position, View convertView, ViewGroup parent) {final String url = getItem(position);View view;if (convertView == null) {view = LayoutInflater.from(getContext()).inflate(R.layout.gv_img_wall_item, null);} else {view = convertView;}final ImageView photo = (ImageView) view.findViewById(R.id.photo);final TextView tv = (TextView) view.findViewById(R.id.num);tv.setText((position + 6) + "");// 给ImageView设置一个Tag,保证异步加载图片时不会乱序photo.setTag(url);setImageView(url, photo);return view;}/** * 给ImageView设置图片。首先从本地硬盘或者内存中取图片,如果没有, 就给ImageView设置一张默认图片。 *  * @param imageUrl *            图片的URL地址,用于作为LruCache的键。 * @param imageView *            用于显示图片的控件。 */private void setImageView(String imageUrl, ImageView imageView) {Bitmap bitmap = getBitmapFromCache(imageUrl);if (bitmap != null) {imageView.setImageBitmap(bitmap);Log.i("-*--------", "本地硬盘或者内存添加");} else {imageView.setImageResource(R.drawable.empty_photo);Log.i("-*--------", "设置默认图片");}}@Overridepublic void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {mFirstVisibleItem = firstVisibleItem;mVisibleItemCount = visibleItemCount;// 下载的任务应该由onScrollStateChanged里调用,但首次进入程序时onScrollStateChanged并不会调用,// 因此在这里为首次进入程序开启下载任务。if (isFirstEnter && visibleItemCount > 0) {loadBitmaps(view, firstVisibleItem, visibleItemCount);isFirstEnter = false;}}@Override/** *  仅当GridView静止时才去下载图片,GridView滑动时取消所有正在下载的任务 */public void onScrollStateChanged(AbsListView view, int scrollState) {if (scrollState == SCROLL_STATE_IDLE) {boolean isDownload = loadBitmaps(view, mFirstVisibleItem,mVisibleItemCount);if (isDownload) {mQueue.start();Log.i("----------", "开启下载");} else {mQueue.stop();Log.i("----------", "中断停止下载");}} else {mQueue.stop();}}/** * 从LruCache中获取一张图片,如果不存在就返回null。 *  * @param imageUrl *            LruCache的键,这里传入图片的URL地址。 * @return 对应传入键的Bitmap对象,或者null。 */@SuppressLint("NewApi")public Bitmap getBitmapFromCache(String imageUrl) {// 先从内存取,看看是否有值Bitmap bitmap = mMemoryCache.getBitmap(imageUrl);// 没有的话,再从硬盘里面取if (bitmap == null) {bitmap = mHardDriveCache.getBitmap(imageUrl.replaceAll("[^\\w]", ""));if (bitmap != null) {mMemoryCache.putBitmap(imageUrl, bitmap);}Log.i("----------", "从硬盘取的");} else {Log.i("----------", "从内存取的");}return bitmap;}/** * 加载Bitmap对象。此方法会在LruCache中检查所有屏幕中可见的ImageView的Bitmap对象, * 如果发现任何一个ImageView的Bitmap对象不在缓存中,就会开启异步线程去下载图片。 *  * @param view * @param firstVisibleItem *            第一个可见的ImageView的下标 * @param visibleItemCount *            屏幕中总共可见的元素数 * @return 如果有需要下载的图片,返回true;否则返回false */private boolean loadBitmaps(AbsListView view, int firstVisibleItem,int visibleItemCount) {boolean isDownload = false;mSameViewUrls.clear();try {for (int i = firstVisibleItem; i < firstVisibleItem+ visibleItemCount; i++) {final String imageUrl = Images.imageThumbUrls[i];ImageView imageView = (ImageView) mPhotoWall.findViewWithTag(imageUrl);// 先从内存和硬盘查找看看,没有的话,再去下载Bitmap bitmap = getBitmapFromCache(imageUrl);if (bitmap != null) {imageView.setImageBitmap(bitmap);} else {View viewItem = view.getChildAt(i - firstVisibleItem).findViewById(R.id.photo);// 过滤重复链接if (!mSameViewUrls.containsValue(imageUrl)) {addDownloadTask(imageUrl);}mSameViewUrls.put(viewItem, imageUrl);isDownload = true;}}} catch (Exception e) {e.printStackTrace();}return isDownload;}/** * 下载图片,再把图片放到内存还有硬盘缓冲里面 *  * @param imageUrl */private void addDownloadTask(final String imageUrl) {final ImageRequest imageRequest = new ImageRequest(imageUrl,new Response.Listener<Bitmap>() {@Overridepublic void onResponse(Bitmap response) {try {if (response != null) {// 保存到内存里mMemoryCache.putBitmap(imageUrl, response);// 保存到硬盘里mHardDriveCache.savaBitmap(imageUrl.replaceAll("[^\\w]", ""),response);// 显示图片showBitmap(response);}} catch (Exception e) {// TODO: handle exceptionLog.e("------------", "error", e);}}private void showBitmap(Bitmap response) throws Exception {Iterator<Entry<View, String>> iterator = mSameViewUrls.entrySet().iterator();//搞个列表,删去已经下载的链接,减少下次过滤相同链接的循环。ArrayList<View> delList = new ArrayList<View>();while (iterator.hasNext()) {Entry<View, String> entry = iterator.next();//设置有相同链接的图片,if (entry.getValue().equals(imageUrl)) {ImageView key = (ImageView) entry.getKey();key.setImageBitmap(response);//添加已经下载的连接,准备删除delList.add(key);}}//删去已经下载的链接for (View view : delList) {mSameViewUrls.remove(view);}//如果全部链接下载完结,中断线程if (mSameViewUrls.isEmpty()) {mQueue.stop();}}}, 0, 0, Config.RGB_565, new Response.ErrorListener() {@Overridepublic void onErrorResponse(VolleyError error) {try {ImageView imageView = (ImageView) mPhotoWall.findViewWithTag(imageUrl);imageView.setImageResource(R.drawable.ic_launcher);} catch (Exception e) {// TODO: handle exceptionLog.e("------------", "error", e);}}});mQueue.add(imageRequest);}public void stopNetTasks() {// TODO Auto-generated method stubmQueue.stop();}}

源码下载

2 0
原创粉丝点击