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

来源:互联网 发布:寻找牛股共性 编程 编辑:程序博客网 时间:2024/05/14 05:30

        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参数.
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.
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的适配器:


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;
}
 
}

0 0
原创粉丝点击