以下代码使用了高效显示位图中的几个概念首先,位图读取是在后台线程中完成的,当我们尝试找出大小时
,我使用标记/重置打开inputStream
(用 包裹)以不从流中读取超出必要的内容BufferedInputstream
计算比例因子时要使用的图像。下面的示例代码对图像进行二次采样以匹配 320x240 像素的大小。在非示例代码中,可以让简单的回调接口将位图从onPostExecute
实现类(回调接口实现者)发送。或者直接将视图作为成员提供,AsyncTask
并将位图设置为onPostExecute
.
使用(我的设备上下载的示例图像)调用代码:
BitmapTask task = new BitmapTask(getContentResolver());
task.execute(Uri.parse("file:///storage/emulated/0/Download/download.jpg"));
有问题的课程
private static class BitmapTask extends AsyncTask<Uri, Void, Bitmap> {
// prevent mem leaks
private WeakReference<ContentResolver> mWeakContentResolver;
public BitmapTask(ContentResolver resolver) {
mWeakContentResolver = new WeakReference<ContentResolver>(resolver);
}
@Override
protected Bitmap doInBackground(Uri... params) {
Bitmap bitmap = null;
ContentResolver resolver = mWeakContentResolver.get();
if (resolver != null) {
BufferedInputStream stream = null;
try {
stream = new BufferedInputStream(
resolver.openInputStream(params[0]));
stream.mark(1 * 1024);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
// Find out size of image
BitmapFactory.decodeStream(stream, null, options);
try {
stream.reset();
} catch (IOException e) {
Log.d(TAG, "reset failed");
}
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
Log.d(TAG, "w, h, mime " + imageWidth + " , " + imageHeight
+ " , " + imageType);
options.inJustDecodeBounds = false;
// Calculate down scale factor
options.inSampleSize = calculateInSampleSize(options, 320,
240);
return BitmapFactory.decodeStream(stream, null, options);
} catch (FileNotFoundException e) {
bitmap = null;
} finally {
IOUtils.closeStreamSilently(stream);
}
}
return bitmap;
}
@Override
protected void onPostExecute(Bitmap result) {
Log.d(TAG,
"bitmap result: "
+ ((result != null) ? "" + result.getByteCount()
: "0"));
result.recycle();
}
}
public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and
// keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
编辑:对于大型输入流,标记/重置技术可能存在问题,SkImageDecoder::Factory returned null
有时可以在日志中看到,导致位图为空,关于此事的其他 SO 问题:SkImageDecoder::Factory returned null。可以通过stream = new resolver.openInputStream(params[0]));
在返回之前再次重新初始化流变量来修复它doInBackground
编辑 2:如果您必须保留图像大小但不想限制内存使用量,您可以使用将options.inPreferredConfig = Bitmap.Config.RGB_565;
每个像素的内存减半,但请记住图像可能不再具有很好的质量(实验!)。