0

byte[] の形式でソケットから画像を読み取り、画面に表示しようとするプログラムを作成しました。これを行う最善の方法は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 に答える