【问题标题】:Increase Speed of Image/Bitmap Display Android提高图像/位图显示 Android 的速度
【发布时间】:2012-07-05 21:43:57
【问题描述】:

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

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

我正在尝试尽可能快地显示我正在阅读的图像,并希望重用相同的内存空间/位图,这样我就不必继续承担 30 毫秒的垃圾收集成本。有谁知道我如何避免垃圾收集,或者至少将其最小化?图像为 640x480 和 1280x720。下面是读取图像并将其与 LogCat 输出一起显示的代码的 sn-p。任何帮助,将不胜感激。谢谢。

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

【问题讨论】:

    标签: android image bitmap garbage-collection bytearray


    【解决方案1】:

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

    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。

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

      • 感谢您的快速回复。我已将 decodeByteArray 从 onDraw 函数中移出,但仍然遇到同样的问题。基本上这就是我正在做的事情。我正在等待从套接字读取整个缓冲区,然后我会调用正在进行解码和绘图的 onDraw 函数。我将解码移到了TutorialThread 内部类的正上方,尝试使用surfaceholder 和同样的运气。我认为问题出在我调用 decodeByteArray 函数的任何地方,它总是会导致垃圾收集。关于如何解决该函数调用的任何想法?谢谢。
      • 如果您在获得新位图时必须创建新位图,那么不需要。您几乎必须使用 BitmapFactory 来解码位数组。当旧位图被丢弃时会发生垃圾收集(并且函数本身可能会进行一些清理)。您可以通过将旧位图放在软参考中来减轻这种影响。它会缓存它,直到需要内存然后才会被丢弃。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-10-15
      • 2017-07-25
      • 2017-11-03
      • 1970-01-01
      • 2016-02-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多