Bitmap的OOM问题解决

时间:2021-08-03 16:10:19

简介

内存溢出主要是由于图片过大导致加载图片的内存过大而出现的Exception。而解决方法就是通过BitmapFactory加载图片时使用BitmapFactory.Options对相关参数进行配置来减少加载的像素。

方法

我们获取的主要方式有以下三种:


  • 网络获取
  • 文件
  • 资源文件

  • 解决方法:
    • decodeStream()
      • decodeStream最大的秘密在于其直接调JNI>>nativeDecodeAsset()来完成decode,无需再使用java层的createBitmap,从而节省了java层的空间。
      • 因此,建议使用BitmapFactory.decodeStream来代替createBitmap方法。
  • 使用压缩读取
    • 首先,要分两种情况,如果是从网络获取到的要先把网络资源下载到本地,就是先缓存下来,之后,-.-,重新从内存读取,使用BitmapFactory.Options和BitmapFactory.decodeFile进行压缩读取。
  • 要养成良好习惯用完Bitmap要及时释放内存
    • 使用Bitmap的recycle()方法进行释放内存。-.-|||,有点不明白就是这小家伙不是及时释放的,但是我在设置了之后,会在重启该Activity的时候释放之前的Bitmap所占用的内存。

代码


1.压缩读取

public class Article {

private String id;
private String imagelink;

public void setId(String id) {
this.id = id;
}

public void setImagelink(String imagelink) {
this.imagelink = imagelink;
}

public String getId() {
return id;
}

public String getImagelink() {
return imagelink;
}

}

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.util.Log;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

/**
* Created by Notzuonotdied on 2016/8/15.
*/

public class getImage {

/**
* Gets bitmap.
*
* @param mArticle 文章对象
* @param reWidth 要求的宽度
* @param reHeight 要求的高度
* @return the bitmap 返回图片Bitmap
*/

public static Bitmap getBitmap(Article mArticle, int reWidth, int reHeight) {
return getBitmapFromEX(downloadIntoFile(mArticle),
reWidth, reHeight);
}

/**
* 获取图片
*
* @param fileName 图片缓存的地址
* @param reWidth 要求的宽度
* @param reHeight 要求的高度
*/

private static Bitmap getBitmapFromEX(String fileName, int reWidth,
int reHeight) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(fileName, options);
// 设置缩放比例,对大图片进行压缩
options.inSampleSize = getTheBestinSampleSize(options, reWidth, reHeight);
options.inJustDecodeBounds = false;
// inPreferredConfig 的默认值是ARGB_8888,改为RGB_565,可以省内存,降低消耗。
options.inPreferredConfig = Bitmap.Config.RGB_565;
return BitmapFactory.decodeFile(fileName, options);
}

/**
* 获取最佳的缩放值
*
* @param options BitmapFactory.Options
* @param reWidth 要求的宽度
* @param reHeight 要求的高度
*/

private static int getTheBestinSampleSize(BitmapFactory.Options options,
int reWidth, int reHeight) {
// Math.round四舍五入取整
return options.outWidth > reWidth || options.outHeight > reHeight ?
Math.min(Math.round((float) options.outWidth / (float) reWidth),
Math.round((float) options.outHeight / (float) reHeight))
: 1;
}

/**
* 下载图片,并且将图片保存到内存卡中
*
* @param article 导入一个对象
*/

private static String downloadIntoFile(Article article) {
String photoPath = Environment.getExternalStorageDirectory()
.getPath() + "/Test/images/";
String imageLink = getConnect.myUrl + article.getImagelink();
Environment.getExternalStoragePublicDirectory(photoPath);
File fileDirectory = new File(photoPath);
if (!fileDirectory.exists()) {
if (fileDirectory.mkdirs()) {
Log.i("Notzuonotdied", "创建文件夹成功!");
}
}
File tempFile = new File(photoPath, article.getId());
if (tempFile.exists()) {
return photoPath + article.getId();
}
try {
if (tempFile.createNewFile()) {
Log.i("Notzuonotdied", "新建成功!");
}
} catch (IOException e) {
e.printStackTrace();
}

// 流
InputStream is = null;
FileOutputStream fileOutputStream = null;

try {
URL url = new URL(imageLink);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Content-type", "application/x-java-serialized-object");
conn.setDoInput(true);
conn.connect();
is = conn.getInputStream();

fileOutputStream = new FileOutputStream(tempFile);
byte[] bytes = new byte[1024];
int len;
while ((len = is.read(bytes)) != -1) {
fileOutputStream.write(bytes, 0, len);
}
fileOutputStream.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

return photoPath + article.getId();
}
}

代码解释:

/**
* 获取图片
*
* @param fileName 图片缓存的地址
* @param reWidth 要求的宽度
* @param reHeight 要求的高度
*/

private static Bitmap getBitmapFromEX(String fileName, int reWidth,
int reHeight) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(fileName, options);
// 设置缩放比例,对大图片进行压缩
options.inSampleSize = getTheBestinSampleSize(options, reWidth, reHeight);
options.inJustDecodeBounds = false;
// inPreferredConfig 的默认值是ARGB_8888,改为RGB_565,可以省内存,降低消耗。
options.inPreferredConfig = Bitmap.Config.RGB_565;
return BitmapFactory.decodeFile(fileName, options);
}

以上代码,创建了一个options,由于我们需要获取原Bitmap文件的宽度和高度,所以需要设置options.inJustDecodeBounds = true;为true,true表示的是返回原Bitmap,而原Bitmap我们可以通过outWidth()和outHeight()来获取原Bitmap的宽度和高度。当我们获取了最佳的缩放值的时候options.inSampleSize,我们就不需要放回原Bitmap了,而是返回通过缩放的新的Bitmap了。

2.回收内存

在使用的Activity的onstop或者ondestory中

imageView.setImageDrawable(null);
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
bitmap = null;
}
System.gc();

附录

Android 多种方式正确的加载图像,有效避免oom
Android有效解决加载大图片时内存溢出的问题
Android加载图片导致内存溢出(Out of Memory异常)