您的位置:首页 > 理论基础 > 计算机网络

android客户端加载网络大图片如何避免内存溢出的两种方法

2015-12-28 19:33 651 查看
方法一:

在Android开发中加载sdcard上的大图片到内存时容易导致OOM异常,常见的解决办法是基于BitmapFactory.Options类提供的方法定义指定的解码方式,
设置inJustDecodeBounds属性为true,避免分配内存,返回一个null的Bitmap对象(包含
outWidth
,[code]outHeight
and
outMimeType
),然后读取图片的尺寸和类型。再根据屏幕的高和宽对图片进行缩放,最后将缩放的图片加载到内存,主要代码如下:

1         Options opts = new Options();
2         opts.inJustDecodeBounds = true; //设置为true, 加载器不会返回图片, 而是设置Options对象中以out开头的字段.即仅仅解码边缘区域
3         BitmapFactory.decodeFile(filePath, opts);
4
5         // 得到图片的宽和高
6         int imageWidth = opts.outWidth;
7         int imageHeight = opts.outHeight;
8
9         // 获取屏幕的宽和高
10         Display display = this.getWindowManager().getDefaultDisplay(); // 获取默认窗体显示的对象
11         int screenWidth = display.getWidth();
12         int screenHeight = display.getHeight();
13
14         // 计算缩放比例
15         int widthScale = imageWidth / screenWidth;
16         int heightScale = imageHeight / screenHeight;
17
18         int scale = widthScale > heightScale ? widthScale:heightScale;20
21          // 指定加载可以加载出图片.
22         opts.inJustDecodeBounds = false;
23         // 使用计算出来的比例进行缩放
24         opts.inSampleSize = scale;
25         Bitmap bm = BitmapFactory.decodeFile(path, opts);


这段代码主要是针对用户存储在sdcard上的大图片,如果现在要从网络上获取一张大图片并在Android机上显示出来,应该如何避免OOM异常呢?

首先加载网络大图片如果是通过BitmapFactory.decodeStream(inputStream)方法的话会出现内存溢出,所以我们同样可以考虑使用Options 类对图片进行缩放处理,然后用BitmapFactory.decodeStream(is, outPadding, opts)方法加载网络图片流对象,但是options的缩放比例该如何确定,如果直接写options.inSampleSize = 固定值;会存在两个问题:一是如果固定值太小任然有内存溢出的可能,二是如果固定值太大会导致加载出来的图片模糊不清,所以现在同样考虑根据图片的高和宽与屏幕的高和宽进行缩放?但是图片都还没加载出来怎样得到图片高和宽呢?你可能会想到通过把options.inJustDecodeBounds
设置为true; 然后调用BitmapFactory.decodeStream(is, outPadding, options),再通过options.outWidth;和options.outHeight;得到图片的高和宽,想法很好,但是当你计算出缩放比再通过options.inJustDecodeBounds = false;options.inSampleSize = scale;Bitmap bm = BitmapFactory.decodeStream(is, outPadding,
options)方法获取缩放后的图片时,你会看到手机界面上啥到没有,再看日志发现输出一行SkImageDecoder::Factory returned null信息,返回的竟然是空对象,郁闷吧,这是为什么呢?原来你在代码中两次使用了BitmapFactory.decodeStream(is, outPadding, options)方法,进一步说是因为你两次使用了同一个InputStream流对象,在第二次使用时流的起始位置已经移动到末尾了,所以返回的是空,那该怎么办呢?我目前使用的方法是分别打开两个流对象,一个用于设置options的缩放比例,一个用于BitmapFactory.decodeStream(is,
outPadding, options)方法调用,经过测试我发现如果先设置options后再传递给BitmapFactory.decodeStream时会非常卡,图片老半天加载不出来,所以我换了一种方式:先通过HttpClient得到图片的流对象,获取它的宽高并根据屏幕的宽高计算出缩放比例;然后再通过HttpClient得到图片的流对象,根据计算出的缩放比例进行缩放,主要代码如下:

/**
* 计算图片的缩放比例
* @return
*/
public int getScare() {
try {
HttpClient client = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(imageUrl);
HttpResponse response = client.execute(httpGet);
int code = response.getStatusLine().getStatusCode();

if (200 == code) {
InputStream is = response.getEntity().getContent();
Options opts = new Options();
opts.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, null, opts);

int imageWidth = opts.outWidth;
int imageHeight = opts.outHeight;

Display display = ImageActivity.this.getWindowManager()
.getDefaultDisplay();
int screenWidth = display.getWidth();
int screenHeight = display.getHeight();

int widthscale = imageWidth / screenWidth;
int heightscale = imageHeight / screenHeight;
int scale = widthscale > heightscale ? widthscale : heightscale;

return scale;
}
} catch (Exception e) {
e.printStackTrace();
}
return 1;//网络连接失败时默认返回1
}


/**
* 获取网络图片
*/
protected void getNetImage() {
try {
HttpClient client = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(imageUrl);
HttpResponse response = client.execute(httpGet);
int code = response.getStatusLine().getStatusCode();

if (200 == code) {
InputStream is = response.getEntity().getContent();

Options opts = new Options();

//根据计算出的比例进行缩放
int scale = getScare();
opts.inSampleSize = scale;

Bitmap bm = BitmapFactory.decodeStream(is, null, opts);

//将bm发生给主线程用于显示图片,更新UI
Message msg = Message.obtain();
msg.obj = bm;
handler.sendMessage(msg);

}
} catch (Exception e) {
e.printStackTrace();
}

}


//显示图片
Handler handler = new Handler() {
public void handleMessage(Message msg) {
Bitmap bm = (Bitmap) msg.obj;
iv.setImageBitmap(bm);
};
};


方法二:

测试环境为Adnroid 2.1以上。

1.AndroidManifest.xml 权限配置:

添加互联网访问权限:

复制代码代码如下:

<uses-permission android:name="android.permission.INTERNET" />

2.异步图片类 ImageDownloadTask

复制代码代码如下:

import java.io.ByteArrayOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.net.MalformedURLException;

import java.net.URL;

import android.graphics.Bitmap;

import android.graphics.BitmapFactory;

import android.os.AsyncTask;

import android.widget.ImageView;

public class ImageDownloadTask extends AsyncTask<Object, Object, Bitmap> {

private ImageView imageView = null;

/***

* 这里获取到手机的分辨率大小

* */

public void setDisplayWidth(int width) {

_displaywidth = width;

}

public int getDisplayWidth() {

return _displaywidth;

}

public void setDisplayHeight(int height) {

_displayheight = height;

}

public int getDisplayHeight() {

return _displayheight;

}

public int getDisplayPixels() {

return _displaypixels;

}

private int _displaywidth = 480;

private int _displayheight = 800;

private int _displaypixels = _displaywidth * _displayheight;

@Override

protected Bitmap doInBackground(Object... params) {

// TODO Auto-generated method stub

Bitmap bmp = null;

imageView = (ImageView) params[1];

try {

String url = (String) params[0];

bmp = getBitmap(url, _displaypixels,true);

} catch (Exception e) {

return null;

}

return bmp;

}

protected void onPostExecute(Bitmap result) {

if (imageView != null&&result!=null) {

imageView.setImageBitmap(result);

if (null != result && result.isRecycled() == false)

System.gc();

}

}

/**

* 通过URL获得网上图片。如:http://www.xxxxxx.com/xx.jpg

* */

public Bitmap getBitmap(String url, int displaypixels, Boolean isBig) throws MalformedURLException, IOException {

Bitmap bmp = null;

BitmapFactory.Options opts = new BitmapFactory.Options();

InputStream stream = new URL(url).openStream();

byte[] bytes = getBytes(stream);

//这3句是处理图片溢出的begin( 如果不需要处理溢出直接 opts.inSampleSize=1;)

opts.inJustDecodeBounds = true;

BitmapFactory.decodeByteArray(bytes, 0, bytes.length, opts);

opts.inSampleSize = computeSampleSize(opts, -1, displaypixels);

//end

opts.inJustDecodeBounds = false;

bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, opts);

return bmp;

}

/**

* 数据流转成btyle[]数组

* */

private byte[] getBytes(InputStream is) {

ByteArrayOutputStream baos = new ByteArrayOutputStream();

byte[] b = new byte[2048];

int len = 0;

try {

while ((len = is.read(b, 0, 2048)) != -1) {

baos.write(b, 0, len);

baos.flush();

}

} catch (IOException e) {

e.printStackTrace();

}

byte[] bytes = baos.toByteArray();

return bytes;

}

/****

* 处理图片bitmap size exceeds VM budget (Out Of Memory 内存溢出)

*/

private int computeSampleSize(BitmapFactory.Options options,

int minSideLength, int maxNumOfPixels) {

int initialSize = computeInitialSampleSize(options, minSideLength,

maxNumOfPixels);

int roundedSize;

if (initialSize <= 8) {

roundedSize = 1;

while (roundedSize < initialSize) {

roundedSize <<= 1;

}

} else {

roundedSize = (initialSize + 7) / 8 * 8;

}

return roundedSize;

}

private int computeInitialSampleSize(BitmapFactory.Options options,

int minSideLength, int maxNumOfPixels) {

double w = options.outWidth;

double h = options.outHeight;

int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math

.sqrt(w * h / maxNumOfPixels));

int upperBound = (minSideLength == -1) ? 128 : (int) Math.min(

Math.floor(w / minSideLength), Math.floor(h / minSideLength));

if (upperBound < lowerBound) {

return lowerBound;

}

if ((maxNumOfPixels == -1) && (minSideLength == -1)) {

return 1;

} else if (minSideLength == -1) {

return lowerBound;

} else {

return upperBound;

}

}

}

3.测试调用代码:

复制代码代码如下:

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

ImageDownloadTask imgtask =new ImageDownloadTask();

/**这里是获取手机屏幕的分辨率用来处理 图片 溢出问题的。begin*/

DisplayMetrics dm = new DisplayMetrics();

getWindowManager().getDefaultDisplay().getMetrics(dm);

imgtask.setDisplayWidth(dm.widthPixels);

imgtask.setDisplayHeight(dm.heightPixels);

//end

ImageView imageView_test= (ImageView)findViewById(R.id.imageView_test);

imgtask.execute("http://pic.qukantu.com/big/7515/201201031116491.jpg",imageView_test);

}

4.小结:

主要是通过 extends AsyncTask<Object, Object, Bitmap> 来实现异步的。

图片Out Of Memory 内存溢出 这一块操作,在实际应用中应该考虑淡定抽取出来。这里为了方便放进来了。 溢出处理实际上就是获得设备分辨率把图片进行压缩。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: