android客户端加载网络大图片如何避免内存溢出的两种方法
2015-12-28 19:33
651 查看
方法一:
在Android开发中加载sdcard上的大图片到内存时容易导致OOM异常,常见的解决办法是基于BitmapFactory.Options类提供的方法定义指定的解码方式,
这段代码主要是针对用户存储在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得到图片的流对象,根据计算出的缩放比例进行缩放,主要代码如下:
方法二:
测试环境为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 内存溢出 这一块操作,在实际应用中应该考虑淡定抽取出来。这里为了方便放进来了。 溢出处理实际上就是获得设备分辨率把图片进行压缩。
在Android开发中加载sdcard上的大图片到内存时容易导致OOM异常,常见的解决办法是基于BitmapFactory.Options类提供的方法定义指定的解码方式,
设置inJustDecodeBounds属性为true,避免分配内存,返回一个null的Bitmap对象(包含andoutWidth,[code]outHeight
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 内存溢出 这一块操作,在实际应用中应该考虑淡定抽取出来。这里为了方便放进来了。 溢出处理实际上就是获得设备分辨率把图片进行压缩。
相关文章推荐
- HP刀片服务器系统Flex-10 VC配置与VMware vSphere网络设计
- 智能家居网络系统的设计(一)
- http post 请求(下)
- http post请求(上)
- 全站 HTTPS 来了
- Objective-c语言_计算机网络和URL(代码1)
- java中的TCP客户端与服务器
- 使用Let's Encrypt手动创建https证书
- 程序员常见面试之 计算机网络 知识点小结
- android中网络请求中页面关闭了会怎么样
- Android 使用 HttpURLConnection 出错
- Objective-c语言_计算机网络
- 仿微信朋友圈图片查看-glide加载网络图片,photoview 实现缩放
- 使用Fiddler捕获java的网络通信数据包
- http压力测试
- http请求流程
- 进程间通信之TCP demo1
- http://www.dexcoder.com/selfly/article/3961
- Android使用OkHttp post 键值对
- TCP服务端和客户端的框架