Android开发 ListView,Gallery,GridView等图片性能优化

Android开发 ListView,Gallery,GridView等图片性

能优化

ListView,Gallery,GridView等控件,在载入大量图片时,很容易会产生OutOfMemoryError异常,即内存溢出.因为每个应用可用内存是有限的,但是图片却很占内存,JPG,PNG本身就是压缩格式,如果分辨率高,很可能保存到磁盘中只有几百K,但是读到内存中会占用十几M内存.图片一多,自然就OutOfMemoryError了.
解决这个问题,需要考虑两个方面.
一是按需载入图片,即读图片的缩略图.比如,每个ImageView在屏幕上的面积是120dp*120dp,但是这个图片是高清的,分辨率2560*1600,如果直接载入整个图片,占用的内存是2560*1600*4/1024/1024=15.625M,如果一屏显示5个Item,占用的内存就是78.125M!而Galaxy Nexus给每个应用分配的内存只有64M,低配机型更少.所以,这里必须根据需要的分辨率载入图片.BitmapFactory解析图片时可以传入一个BitmapFactory.Options参数.

1
2
3
4
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;
options.inSampleSize = inSampleSize;
Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);

这里最重要的是inSampleSize,这是一个int值,等于1时,返回原图,大于1时,返回宽高为原图1/inSampleSize的图片,
所以,我们需要先根据需要的大小,计算inSampleSize.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
		BitmapFactory.Options options = new BitmapFactory.Options();
                //设为true,不会去解析图片,只解析边界,即宽高
		options.inJustDecodeBounds = true;
		BitmapFactory.decodeFile(jpgPath, options);
         //读出图片真实的宽高
		int[] size = new int[2];
		size[0] = options.outWidth;
		size[1] = options.outHeight;
 
		float realWidth = size[0];
		float realHeight = size[1];
 
		// 如果图片尺寸比最大值小,直接返回
		if (maxWidth > realWidth && maxHeight > realHeight) {
			return 1;
		}
		// 计算宽高比
		float target_ratio = (float) maxWidth / maxHeight;
		float real_ratio = realWidth / realHeight;
		int inSampleSize = 1;
		if (real_ratio > target_ratio) {
			// 如果width太大,height太小,以width为基准,把realWidth设为maxWidth,realHeight缩放
			inSampleSize = (int) realWidth / maxWidth;
		} else {
			inSampleSize = (int) realHeight / maxHeight;
		}

虽然经过上面的处理,图片占用的内存已经大幅减少,放个百来个Item也不会OutOfMemoryError了,但很多应用都是下拉刷新,增加Item,Item的数量不可控,如果所有的图片都放内存里,溢出也是早晚的事.这里就需要做个图片缓存,内存里只保留需要用到的,没用到的,释放.我这里做了二级缓存,第一级放内存,第二级放SD卡.
说到图片内存缓存,网上大部分文章推荐软引用(SoftReference) 和弱引用(WeakReference),但是从Android 2.3开始,所有软引用和弱引用的对象在GC时都会被回收,根本起不到缓存的作用,Google推荐使用LruCache(http://developer.android.com/reference/android/util/LruCache.html),该类可以分配指定大小的缓存空间,当达到临界值时,最先保存的对象会被挤出,释放内存.
下面是一个使用LruCache的适配器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package com.pocketdigi.googleimage;
 
import java.util.List;
 
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
 
import com.pocketdigi.googleimage.mode.ApiResult.ImageData;
import com.pocketdigi.utils.ImageUtils;
import com.pocketdigi.utils.ImageUtils.DownBitmapListener;
import com.pocketdigi.views.ImageViewer;
 
public class GoogleImageAdapter extends BaseAdapter{
	Context mContext;
	List<ImageData> mImageList;
	LayoutInflater mInflater;
	boolean mBusy = false;
	// 图片缓存,Key是url,Value就是Bitmap
	LruCache<String, Bitmap> mImageCache;
	public GoogleImageAdapter(Context context, List<ImageData> imageList) {
		mContext = context;
		mImageList = imageList;
		mInflater = LayoutInflater.from(mContext);
		//读取应用可用内存大小,这里读出来,单位是兆,Galaxy Nexus是64M
		final int memClass = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();
		//取1/8作为图片缓存
		final int maxSize = 1024 * 1024 * memClass / 8;
		mImageCache = new LruCache<String, Bitmap>(maxSize) {
			// 必须覆写sizeOf方法,因为默认返回的是1,即统计的是对象的数量,而不是占用的内存
			@Override
			protected int sizeOf(String key, Bitmap value) {
				// TODO 自动生成的方法存根
				return value.getByteCount();
			}
		};
 
	}
 
	@Override
	public int getCount() {
		// TODO 自动生成的方法存根
		return mImageList.size();
	}
 
	@Override
	public Object getItem(int position) {
		// TODO 自动生成的方法存根
		return mImageList.get(position);
	}
 
	@Override
	public long getItemId(int position) {
		// TODO 自动生成的方法存根
		return position;
	}
 
	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		// TODO 自动生成的方法存根
		final ImageData imageData = mImageList.get(position);
		ViewHolder holder = null;
		if (convertView == null) {
			convertView = mInflater.inflate(R.layout.item_listview, null);
			holder = new ViewHolder();
			holder.tv_title = (TextView) convertView.findViewById(R.id.tv_title);
			holder.iv_thumbnail = (ImageView) convertView.findViewById(R.id.iv_thumbnail);
			holder.thumbnail_url = imageData.getThumbnail_url();
			convertView.setTag(holder);
		} else {
			holder = (ViewHolder) convertView.getTag();
			if (!holder.thumbnail_url.equals(imageData.getThumbnail_url())) {
				holder.iv_thumbnail.setImageResource(R.drawable.loading);
			}
		}
 
		holder.tv_title.setText(imageData.getAbs());
		if (!isBusy()) {
			final String imgUrl = imageData.getThumbnail_url();
			Bitmap bmp = mImageCache.get(imgUrl);
			if (bmp != null) {
				holder.iv_thumbnail.setImageBitmap(bmp);
			} else {
				loadBmpFromNetWork(imgUrl);
			}
 
		}
 
		holder.iv_thumbnail.setOnClickListener(new OnClickListener() {
 
			@Override
			public void onClick(View v) {
				//点击ImageView显示大图
				ImageViewer imageViewer = new ImageViewer(mContext);
				imageViewer.setSourceView((ImageView) v);
				imageViewer.setJpgUrl(imageData.getImage_url());
				imageViewer.show();
			}
		});
 
		return convertView;
	}
	/**
	 * 从网络下载图片
	 * @param imgUrl
	 */
	private void loadBmpFromNetWork(final String imgUrl) {
		//异步从网络下载图片,这个异步下载方法,是有作磁盘缓存的
		//缓存原理,根据传入的url,计算md5,作缓存文件名,下载前先判断该文件存不存在,如果存在,从SD卡读,不存在,再去下载
		//参数120,120,是返回120*120的bitmap,避免在载入大图时浪费内存
		ImageUtils.asynchronousDownloadBitmap(imgUrl, 120, 120, new DownBitmapListener() {
			@Override
			public void onComplete(Bitmap bmp) {
				// TODO 自动生成的方法存根
				if(imgUrl!=null&&bmp!=null)
				{
					mImageCache.put(imgUrl, bmp);
					notifyDataSetChanged();
				}
			}
 
			@Override
			public void onProgressChanged(long total, long downloaded) {
				// TODO 自动生成的方法存根
 
			}
		});
	}
 
	@Override
	public int getItemViewType(int position) {
		// TODO 自动生成的方法存根
		return super.getItemViewType(position);
	}
 
	@Override
	public int getViewTypeCount() {
		// TODO 自动生成的方法存根
		return super.getViewTypeCount();
	}
	//用来保存各个控件的引用
	static class ViewHolder {
		TextView tv_title;
		ImageView iv_thumbnail;
		String thumbnail_url;
	}
 
 
 
	public boolean isBusy() {
		return mBusy;
	}
 
	public void setBusy(boolean busy) {
		this.mBusy = busy;
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值