0

我编写了一个程序,它以字节 [] 的形式从套接字读取图像,然后尝试在屏幕上显示它们。我关注了一些我发现的链接,说最好的方法是使用 SurfaceView。我正在尝试分配一个字节数组,该数组将占我需要的最大图像,然后不断为我收到的新图像使用相同的内存空间。这样内存就没有机会收集垃圾了。

我想我已经将问题缩小到“BitmapFactory.decodeByteArray(f, 0, fileSize);”的函数调用。我认为这个调用每次都会创建一个新的位图,一旦方法返回,它就会被 GC 处理。我尝试使用 inMutable 和 inBitmap 字段,但没有运气,因为图像的大小每次都在变化。不过,我可能做错了什么。如果有人有任何使用它的经验并且对他们有用,请告诉我。每次 byte[] 大小甚至仅更改 10 个元素。

我正在尝试尽可能快地显示我正在阅读的图像,并希望重用相同的内存空间/位图,这样我就不必继续承担 30 毫秒的垃圾收集成本。有谁知道我如何避免垃圾收集,或者至少将其最小化?图像为 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
4

2 回答 2

2

我刚刚遇到了同样的问题,您已经提到了解决方案:

opts.inBitmap

如您所说,新的位图必须与前一个具有相同的大小。此外,您必须设置

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());
}

就我而言,这完全消除了位图所需的 GC。

于 2013-03-14T17:16:56.380 回答
0

我认为这个调用每次都会创建一个新的位图,一旦方法返回,它就会被 GC 处理。

这正是正在发生的事情。每次调用它都会从您的缓冲区创建一个新的位图。旧的被扔掉,不管它是否是同一个位图。永远不要在视图的“onDraw”方法中创建对象,尤其是位图。此方法可能在视图存在时被调用数百次,并且创建对象的成本很高。

最好的选择是等到缓冲区被新的位图填充,然后调用BitmapFactory.decodeByteArray(f, 0, fileSize); 保持对位图的引用在类的范围内。recycle()还记得在创建新位图之前调用旧位图。

于 2012-07-05T13:24:17.337 回答