我编写了一个程序,它以字节[]的形式从套接字中读取图像,然后尝试在屏幕上显示它们。我已经按照我发现的一些链接说最好的方法就是使用SurfaceView。我正在尝试分配一个字节数组,它将占用我需要的最大图像,然后不断地为我收到的新图像使用相同的存储空间。这样,内存就没有机会收集垃圾。
我想我已经将问题缩小到“BitmapFactory.decodeByteArray(f,0,fileSize);”的函数调用。我认为这个调用每次创建一个新的位图,一旦方法返回它就是GC。我尝试使用inMutable和inBitmap字段但没有运气,因为图像的大小每次都在变化。我可能做错了。如果有人有使用它的经验并且它为他们工作,请告诉我。每次byte []大小改变甚至只有10个元素。
我正在尝试尽可能快地显示我正在阅读的图像,并希望重用相同的内存空间/位图,这样我就不必继续承担垃圾收集的30ms成本。有谁知道我怎么能避免垃圾收集,或至少最小化它?图像为640x480和1280x720。下面是一段代码片段,它读入图像并与LogCat输出一起显示。任何帮助,将不胜感激。感谢。
static byte[] f = new byte[250000]; // allocate enough memory space for biggest image
private TutorialThread _thread;
class Panel extends SurfaceView implements SurfaceHolder.Callback {
/* On start up connect socket */
public Panel(Context context) {
super(context);
getHolder().addCallback(this);
_thread = new TutorialThread(getHolder(), this);
if (connectSockets) {
s2 = connect(ip, s2, port);
}
}
/*
* OnDraw - Take the byte[] and use Bitmap.decodeByteArray to decode
* Image and then draw it on canvas
*
* (Slow, Causing 30ms delay due to Garbage Collection)
*/
@Override
public void onDraw(Canvas canvas) {
Bitmap i = BitmapFactory.decodeByteArray(f, 0, fileSize);
canvas.drawBitmap(i, 10, 10, null);
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
_thread.setRunning(true);
_thread.start();
}
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
// simply copied from sample application LunarLander:
// we have to tell thread to shut down & wait for it to finish, or
// else
// it might touch the Surface after we return and explode
boolean retry = true;
_thread.setRunning(false);
while (retry) {
try {
_thread.join();
retry = false;
} catch (InterruptedException e) {
// we will try it again and again...
}
}
}
}
class TutorialThread extends Thread {
private SurfaceHolder _surfaceHolder;
private Panel _panel;
private boolean _run = false;
public TutorialThread(SurfaceHolder surfaceHolder, Panel panel) {
_surfaceHolder = surfaceHolder;
_panel = panel;
}
public void setRunning(boolean run) {
_run = run;
}
@Override
public void run() {
Canvas c = null;
byte[] header = new byte[16];
while (_run) {
Log.d("Brian", "Running");
if (s2 != null) { // if socket isCreated
int bytesRead = 0;
int totalBytesRead = 0;
fileSize = 0;
/*
* read the header that contains the filesize, height, and
* width
*
* don't break until we read all header values
*/
while (totalBytesRead != (header.length)) {
try {
bytesRead = s2.getInputStream().read(header,
totalBytesRead,
(header.length - totalBytesRead));
} catch (IOException ex) {
}
if (bytesRead == -1) {
} else {
totalBytesRead += bytesRead;
}
}
// convert the filesize from bytes to int
fileSize = (fileSize << 8) + (header[0] & 0xff);
fileSize = (fileSize << 8) + (header[1] & 0xff);
fileSize = (fileSize << 8) + (header[2] & 0xff);
fileSize = (fileSize << 8) + (header[3] & 0xff);
bytesRead = 0;
// read the entire file. don't break until we read
// everything
do {
bytesRead = readChunk(s2, bytesRead, fileSize, f);
} while (bytesRead != fileSize);
} else {
Log.d("Brian", "Socket Null");
}
c = null;
try {
c = _surfaceHolder.lockCanvas(null);
synchronized (_surfaceHolder) {
_panel.onDraw(c);
}
} finally {
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
_surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
07-05 09:13:21.980: D/dalvikvm(25075): GC_FOR_ALLOC freed 0K, 6% free 7040K/7431K, paused 18ms
07-05 09:13:22.010: D/dalvikvm(25075): GC_CONCURRENT freed <1K, 6% free 7040K/7431K, paused 4ms+2ms
07-05 09:13:22.040: D/dalvikvm(25075): GC_EXPLICIT freed 600K, 14% free 6440K/7431K, paused 1ms+2ms
07-05 09:13:22.060: D/dalvikvm(25075): GC_FOR_ALLOC freed <1K, 14% free 6440K/7431K, paused 19ms
07-05 09:13:22.060: I/dalvikvm-heap(25075): Grow heap (frag case) to 6.994MB for 614416-byte allocation
07-05 09:13:22.080: D/dalvikvm(25075): GC_FOR_ALLOC freed 0K, 6% free 7040K/7431K, paused 21ms
07-05 09:13:22.120: D/dalvikvm(25075): GC_CONCURRENT freed <1K, 6% free 7040K/7431K, paused 3ms+2ms
07-05 09:13:22.150: D/dalvikvm(25075): GC_EXPLICIT freed 600K, 14% free 6440K/7431K, paused 2ms+2ms
07-05 09:13:22.170: D/dalvikvm(25075): GC_FOR_ALLOC freed <1K, 14% free 6440K/7431K, paused 19ms
07-05 09:13:22.170: I/dalvikvm-heap(25075): Grow heap (frag case) to 6.994MB for 614416-byte allocation
07-05 09:13:22.190: D/dalvikvm(25075): GC_FOR_ALLOC freed 0K, 6% free 7040K/7431K, paused 19ms
07-05 09:13:22.220: D/dalvikvm(25075): GC_CONCURRENT freed <1K, 6% free 7040K/7431K, paused 2ms+2ms
07-05 09:13:22.250: D/dalvikvm(25075): GC_EXPLICIT freed 600K, 14% free 6440K/7431K, paused 2ms+1ms
07-05 09:13:22.270: D/dalvikvm(25075): GC_FOR_ALLOC freed <1K, 14% free 6440K/7431K, paused 18ms
07-05 09:13:22.270: I/dalvikvm-heap(25075): Grow heap (frag case) to 6.994MB for 614416-byte allocation
答案 0 :(得分:2)
我遇到了同样的问题,你已经提到了解决方案:
opts.inBitmap
正如您所说,新的Bitmap必须与前一个相同。 此外,你必须设置
opts.inSampleSize = 1;
示例:
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = 1;
while (...) {
// first call will create a new bitmap
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, opts);
// subsequent calls will re-use the first bitmap
opt.inBitmap = bitmap;
// check (dump bitmaps memory address, should always output the same string)
Log.d("bitmapID", bitmap.toString());
}
就我而言,这完全删除了Bitmap所需的GC。
答案 1 :(得分:0)
我认为这次调用每次都会创建一个新的位图 当方法返回时,它正在被GC编辑。
这正是发生的事情。它每次调用都会从缓冲区创建一个新的位图。不管它是否是相同的位图,旧的都会丢弃。不要在&#34; onDraw&#34;中创建对象。视图的方法,尤其是位图。在视图存在的情况下,此方法可能会被调用数百次,并且创建对象的成本很高。
最好的选择是等待缓冲区填充新的位图,然后调用BitmapFactory.decodeByteArray(f, 0, fileSize);
保持对类范围内位图的引用。还记得在创建新位图之前在旧位图上调用recycle()
。