2012-07-05 37 views
0

我寫了一個程序,它以字節[]的形式從套接字中讀取圖像,然後嘗試在屏幕上顯示它們。我跟着我發現的一些鏈接,說最好的方法是使用SurfaceView。我試圖分配一個字節數組,它將佔用我需要的最大圖像,然後不斷使用相同的內存空間來存儲我接收到的新圖像。這樣記憶就沒有機會收集垃圾了。提高圖像/位圖顯示速度Android

我想我已經將問題縮小到了「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

我認爲,這個調用創建一個新的位圖,每次並儘快 的方法返回它正在GC'ed。

這正是發生了什麼事情。它每次調用都會從緩衝區創建一個新的位圖。不管它是否是相同的位圖,舊的被扔掉。不要在視圖的「onDraw」方法中創建對象,特別是位圖。這種方法在視圖的存在中可能會被調用幾百次,創建對象的代價很​​高。

最好的選擇是等待緩衝區填充新的位圖,然後調用BitmapFactory.decodeByteArray(f, 0, fileSize);保持位圖範圍內對位圖的引用。還請記住在創建新位圖之前,請在舊位圖上調用recycle()

+0

感謝您的快速響應。我已經將decodeByteArray移出了onDraw函數,但仍然存在相同的問題。基本上這就是我正在做的。我正在等待從套接字讀取整個緩衝區,然後我會調用正在進行解碼和繪製的onDraw函數。我將解碼移到了TutorialThread內部類上方,正好位於測試的上方,同樣運氣。我認爲問題出在我稱之爲decodeByteArray函數的地方,它總是會導致垃圾收集。有關如何解決該函數調用的任何想法?謝謝。 – blouro 2012-07-05 13:41:30

+0

如果您在創建新位圖時必須創建一個新位圖,則不需要。你幾乎必須使用BitmapFactory來解碼位數組。垃圾收集發生在舊位圖被丟棄時(並且可能會在函數本身中發生一些清理)。 您可以通過將舊位圖放入軟參考中來減輕影響。它會緩存它,直到內存需要,然後將被扔掉。 – DeeV 2012-07-05 14:05:25

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

在我而言,這是完全去除所需的位圖的選區。