下载位图并写入现有位图



我正在使用以下代码从URL下载位图。如果我这样循环(就像从相机流式传输图像一样),位图将被一次又一次地重新分配。所以我想知道是否有一种方法可以将新下载的字节数组写入内存中已经分配的现有位图中。

public static Bitmap downloadBitmap(String url) {
try {
URL newUrl = new URL(url);
return BitmapFactory.decodeStream(newUrl.openConnection()
.getInputStream());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}

在题为"在Android 3.0及更高版本上管理内存"的位图内存管理部分的这一部分中,他们开始谈论如何操作位图,以便您可以重用位图空间,从而无需重新分配位图本身的位置。如果你确实在考虑使用相机中的流,那么这将覆盖蜂窝,因为它们的大小相同。否则,它可能只会帮助超过4.4基特卡特。

但是,您可以在downloadBitmap类中存储一个本地WeakReference(如果您希望在内存问题时收集它),然后重新分配给该空间并返回到那里,而不是每次在一行中创建一个位图。

由于应用程序在每个周期中分配和取消分配内存,因此速度会减慢。有三种方法可以避免这种情况。

第一个版本在没有OpenCV的情况下工作,但在每个周期中仍会分配一些内存。但数量要小得多,因此速度至少快两倍。怎样通过重新使用现有的已分配好的缓冲区(byte[])。我将它与预先分配的长度为1.000.000的SteamInfo缓冲区一起使用(大约是我预期的两倍)。

顺便说一句,分块读取输入流并使用BitmapFactory.decodeByteArray比将URL的输入流直接放入BitmapFactory.decodeStream要快得多。

public static class StreamInfo {
public byte[] buffer;
public int length;
public StreamInfo(int length) {
buffer = new byte[length];
}
}
public static StreamInfo imageByte(StreamInfo buffer, String url) {
try {
URL newUrl = new URL(url);
InputStream is = (InputStream) newUrl.getContent();
byte[] tempBuffer = new byte[8192];
int bytesRead;
int position = 0;
if (buffer != null) {
// re-using existing buffer
while ((bytesRead = is.read(tempBuffer)) != -1) {
System.arraycopy(tempBuffer, 0, buffer.buffer, position,
bytesRead);
position += bytesRead;
}
buffer.length = position;
return buffer;
} else {
// allocating new buffer
ByteArrayOutputStream output = new ByteArrayOutputStream();
while ((bytesRead = is.read(tempBuffer)) != -1) {
output.write(tempBuffer, 0, bytesRead);
position += bytesRead;
}
byte[] result = output.toByteArray();
buffer = new StreamInfo(result.length * 2, false);
buffer.length = position;
System.arraycopy(result, 0, buffer.buffer, 0, result.length);
return buffer;
}
} catch (MalformedURLException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}

第二个版本使用OpenCV Mat和预先分配的位图。接收流的操作与版本一相同。因此,它不再需要进一步的内存分配(有关详细信息,请查看此链接)。此版本运行良好,但速度稍慢,因为它包含OpenCV Mat和位图之间的转换。

private NetworkCameraFrame frame;
private HttpUtils.StreamInfo buffer = new HttpUtils.StreamInfo(1000000);
private MatOfByte matForConversion;
private NetworkCameraFrame receive() {
buffer = HttpUtils.imageByte(buffer, uri);
if (buffer == null || buffer.length == 0)
return null;
Log.d(TAG, "Received image with byte-array of length: "
+ buffer.length / 1024 + "kb");
if (frame == null) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap bmp = BitmapFactory.decodeByteArray(buffer.buffer, 0,
buffer.length);
frame = new NetworkCameraFrame(bmp.getWidth(), bmp.getHeight());
Log.d(TAG, "NetworkCameraFrame created");
bmp.recycle();
}
if (matForConversion == null)
matForConversion = new MatOfByte(buffer.buffer);
else
matForConversion.fromArray(buffer.buffer);
Mat newImage = Highgui.imdecode(matForConversion,
Highgui.IMREAD_UNCHANGED);
frame.put(newImage);
return frame;
}
private class NetworkCameraFrame implements CameraFrame {
Mat mat;
private int mWidth;
private int mHeight;
private Bitmap mCachedBitmap;
private boolean mBitmapConverted;
public NetworkCameraFrame(int width, int height) {
this.mWidth = width;
this.mHeight = height;
this.mat = new Mat(new Size(width, height), CvType.CV_8U);
this.mCachedBitmap = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
}
@Override
public Mat gray() {
return mat.submat(0, mHeight, 0, mWidth);
}
@Override
public Mat rgba() {
return mat;
}
// @Override
// public Mat yuv() {
// return mYuvFrameData;
// }
@Override
public synchronized Bitmap toBitmap() {
if (mBitmapConverted)
return mCachedBitmap;
Mat rgba = this.rgba();
Utils.matToBitmap(rgba, mCachedBitmap);
mBitmapConverted = true;
return mCachedBitmap;
}
public synchronized void put(Mat frame) {
mat = frame;
invalidate();
}
public void release() {
mat.release();
mCachedBitmap.recycle();
}
public void invalidate() {
mBitmapConverted = false;
}
};

第三个版本在BitmapFactory上使用"使用BitmapFactory"的说明。选项和可变位图,然后在解码时重复使用。它甚至在安卓系统上对我有用。在创建第一个位图时,请确保使用正确的BitmapFactory.Options。

BitmapFactory.Options options = new BitmapFactory.Options();
options.inBitmap = bmp;  // the old Bitmap that should be reused
options.inMutable = true;
options.inSampleSize = 1;
Bitmap bmp = BitmapFactory.decodeByteArray(buffer, 0, buffer.length, options);
options.inBitmap = bmp;

这实际上是当时最快的流媒体。

最新更新