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

文章,参考自: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> implements
		OnScrollListener {

	/**
	 * 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")
	@Override
	public 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("-*--------", "设置默认图片");
		}
	}

	@Override
	public 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>() {
					@Override
					public 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 exception
							Log.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() {
					@Override
					public void onErrorResponse(VolleyError error) {
						try {
							ImageView imageView = (ImageView) mPhotoWall
									.findViewWithTag(imageUrl);
							imageView.setImageResource(R.drawable.ic_launcher);
						} catch (Exception e) {
							// TODO: handle exception
							Log.e("------------", "error", e);
						}
					}
				});
		mQueue.add(imageRequest);
	}

	public void stopNetTasks() {
		// TODO Auto-generated method stub
		mQueue.stop();
	}
}

源码下载

时间: 2024-10-14 08:05:49

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

图片--Android有效解决加载大图片时内存溢出的问题

Android有效解决加载大图片时内存溢出的问题 博客分类: Android Android游戏虚拟机算法JNI 尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图,因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存. 因此,改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView

Android有效解决加载大图片时内存溢出的问题

尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图, 因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存. 因此,改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView的 source, decodeStream最大的秘密在于其直接调用JNI>>nativeDecodeAsse

有效解决Android加载大图片时内存溢出的问题

首先解析一下基本的知识: 位图模式,bitmap颜色位数是1位 灰度模式,bitmap颜色位数是8位,和256色一样 RGB模式,bitmap颜色位数是24位 在RGB模式下,一个像素对应的是红.绿.蓝三个字节 CMYK模式,bitmap颜色位数是32位  在CMYK模式下,一个像素对应的是青.品.黄.黑四个字节 图像文件的字节数(Byte) = 图像分辨率*颜色深度/8(bit/8) 例如:一幅640*480图像分辨率.RGB色一般为24位真彩色,图像未经压缩的数据容量为:640X480X24

使用SDWebImage加载大量图片后造成内存泄露的解决办法 转载

使用SDWebImage加载大量图片后造成内存泄露的解决办法 时间:2015-07-21 14:26:47      阅读:5885      评论:0      收藏:0      [点我收藏+] SDWebImage的知名度就不用说了,github上近10k的star,国内外太多的App使用其进行图片加载. 但是最近在使用过程中发现,在UITableView中不断加载更多的内容,使用SDWebImage会造成内存占用越来越大,导致memory warning最终terminate,稍微找了下

解决MWPhotoBrowser中的SDWebImage加载大图导致的内存警告问题

解决MWPhotoBrowser中的SDWebImage加载大图导致的内存警告问题 iOS开发 · 2015-01-22 11:31 MWPhotoBrowser是一个非常不错的照片浏览器,在github的star接近3000个,地址:https://github.com/mwaterfall/MWPhotoBrowser.git MWPhotoBrowser来加载小图1M以下的都应该不会有内存警告的问题.如果遇到大图,3M.4M.5M的大图,很有可能导致内存警告.最近我就遇到这个问题,很是头疼

Android加载大图片OOM异常解决

尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图, 因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存. 因此,改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView的 source, decodeStream最大的秘密在于其直接调用JNI>>nativeDecodeAsse

Android 高清加载巨图方案, 拒绝压缩图片

源地址:http://blog.csdn.net/lmj623565791/article/details/49300989 一.概述 距离上一篇博客有段时间没更新了,主要是最近有些私事导致的,那么就先来一篇简单一点的博客脉动回来. 对于加载图片,大家都不陌生,一般为了尽可能避免OOM都会按照如下做法: 对于图片显示:根据需要显示图片控件的大小对图片进行压缩显示. 如果图片数量非常多:则会使用LruCache等缓存机制,将所有图片占据的内容维持在一个范围内. 其实对于图片加载还有种情况,就是单个

Android 高清加载长图或大图方案

一.概述 对于加载图片,大家都不陌生,一般为了尽可能避免OOM都会按照如下做法: 对于图片显示:根据需要显示图片控件的大小对图片进行压缩显示. 如果图片数量非常多:则会使用LruCache等缓存机制,将所有图片占据的内容维持在一个范围内. 其实对于图片加载还有种情况,就是单个图片非常巨大,并且还不允许压缩.比如显示:世界地图.清明上河图.微博长图等. 那么对于这种需求,该如何做呢? 首先不压缩,按照原图尺寸加载,那么屏幕肯定是不够大的,并且考虑到内存的情况,不可能一次性整图加载到内存中,所以肯定

Android(java)学习笔记236:多媒体之加载大图片到内存(Bitmap API)

1.Bitmap (API使用) android里面的bitmap中,一个像素点需要4个byte去表示,这是因为android表示颜色是" argb ":其中 a 表示是透明度,然后是" rgb" 颜色表示范围 00000000 ~~~ffffffff 2.加载图片到内存: 上面说到了图形表示使用4byte,和int一样,所以Android里面每个像素点都是使用一个int来表示的. Bitmap bitmap = BitmapFactory.decodeResour