2013-06-20 3 views
3

Мне нужно отображать изображения с разными кадрами в секунду, максимальная FPS составляет 30. Изображения взяты из SDcard и все имеют одинаковый размер: 480 x 640. Я создал 3 возможных решения, но каждый из них имеет проблемы:Самый быстрый способ загрузки/отображения растрового изображения; лучший способ повторного использования Bitmap

Ниже представлены результаты при 30 FPS.

I. Не повторное использование растрового изображения

  • много GC звонков: приблиз. 30 ГХ в секунду
  • загрузки процессора: достигать до 92%

    private Bitmap bitmap; 
    
    private void startAnimation1() { 
        TimerTask updateImage = new UpdateImage1(); 
        timer.scheduleAtFixedRate(updateImage, 0, 1000/FPS); 
    } 
    
    class UpdateImage1 extends TimerTask { 
        @Override 
        public void run() { 
         try { 
          if (i == IMAGES_NR) { 
           i = 0; 
          } 
          bitmap = BitmapFactory.decodeStream(new FileInputStream(framesFiles[i]), null, null); 
          i++; 
         } catch (FileNotFoundException e) { 
          System.out.println("Exception 1: " + e.getMessage()); 
         } 
    
         runOnUiThread(new Runnable() { 
          @Override 
          public void run() { 
           imgView.setImageBitmap(bitmap); 
          } 
         }); 
        } 
    } 
    

II. повторное использование растровых изображений с помощью BitmapFactory.Options.inBitmap

  • GC вызовов ниже - 1 или 2 на второй нагрузке
  • CPU: достигать до 84%

После WILE бега анимация, с которой работает приложение:

06-20 15:08:58.158: WARN/System.err(7880): java.lang.ArrayIndexOutOfBoundsException: length=-5131855; regionStart=0; regionLength=1024 
06-20 15:08:58.158: WARN/System.err(7880): at java.util.Arrays.checkOffsetAndCount(Arrays.java:1731) 
06-20 15:08:58.158: WARN/System.err(7880): at java.io.BufferedInputStream.read(BufferedInputStream.java:273) 
06-20 15:08:58.158: WARN/System.err(7880): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method) 
06-20 15:08:58.158: WARN/System.err(7880): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:587) 
06-20 15:08:58.158: WARN/System.err(7880): at com.example.SendPreviewOptimization.MyActivity$UpdateImage2.run(MyActivity.java:148) 
06-20 15:08:58.158: WARN/System.err(7880): at java.util.Timer$TimerImpl.run(Timer.java:284) 
06-20 15:08:58.168: DEBUG/skia(7880): ---- read threw an exception 
06-20 15:08:58.168: DEBUG/skia(7880): --- decoder->decode returned false 
06-20 15:08:58.168: WARN/System.err(7880): java.lang.IllegalArgumentException: Problem decoding into existing bitmap 
06-20 15:08:58.168: WARN/System.err(7880): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:590) 
06-20 15:08:58.168: WARN/System.err(7880): at com.example.SendPreviewOptimization.MyActivity$UpdateImage2.run(MyActivity.java:148) 
06-20 15:08:58.168: WARN/System.err(7880): at java.util.Timer$TimerImpl.run(Timer.java:284) 
06-20 15:08:58.178: ERROR/msm8960.hwcomposer(330): prepareBypass: Unable to setup bypass due to non-pmem memory 
06-20 15:08:58.198: ASSERT/libc(7880): Fatal signal 11 (SIGSEGV) at 0xffd1d447 (code=1) 
06-20 15:08:58.238: ERROR/msm8960.hwcomposer(330): prepareBypass: Unable to setup bypass due to non-pmem memory 
06-20 15:08:58.498: ERROR/MP-Decision(1448): DOWN Ld:25 Ns:1.100000 Ts:190 rq:0.000000 seq:194.000000 
06-20 15:08:58.708: INFO/DEBUG(27660): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 



    private static BitmapFactory.Options bitmapOptions; 
    private FileInputStream in; 

    private void startAnimation2() { 
     bitmapOptions = new BitmapFactory.Options(); 
     // setup bitmap reuse options: 
     bitmapOptions.inPurgeable = true; 
     bitmapOptions.inInputShareable = true; 
     bitmapOptions.inBitmap = reusableBitmap; 
     bitmapOptions.inMutable = true; 
     bitmapOptions.inSampleSize = 1; 

     TimerTask updateImage = new UpdateImage2(); 
     timer.scheduleAtFixedRate(updateImage, 0, 1000/FPS); 
    } 

    class UpdateImage2 extends TimerTask { 
     @Override 
     public void run() { 
      try { 
       if (i == IMAGES_NR) { 
        i = 0; 
       } 

       //** version 1: 
       in = new FileInputStream(framesFiles[i]); 
       //decode into existing bitmap 
       BitmapFactory.decodeStream(in, null, bitmapOptions); 
       in.close(); 

       //** version 2: 
       //BitmapFactory.decodeFile(framesFiles[i].getAbsolutePath(), bitmapOptions); 

       i++; 
      } catch (Exception e) { 
       System.out.println("Exception 2: " + e.getMessage()); 
      } 
      runOnUiThread(new Runnable() { 
       @Override 
       public void run() { 
        imgView.setImageBitmap(reusableBitmap); 
       } 
      }); 
     } 
    } 

III. Вариант III: (. Одна вещь, которая делает Bytebuffer более эффективным является использование прямой памяти) с использованием Буферы

  • этот вариант я не мог заставить его работать :(

    private ByteBuffer buffer; 
    private byte[] b; 
    private IntBuffer mPixels; 
    
    private void startAnimation3() { 
        buffer = ByteBuffer.allocate(480 * 640 * 6); 
        b = new byte[480 * 640 * 6]; 
        TimerTask updateImage = new UpdateImage3(); 
        timer.scheduleAtFixedRate(updateImage, 0, 1000/FPS); 
    } 
    
    class UpdateImage3 extends TimerTask { 
        public void run() { 
         try { 
          if (i == IMAGES_NR) { 
           i = 0; 
          } 
          FileInputStream frameInputStream = new FileInputStream(framesFiles[i]); 
          frameInputStream.read(b); 
          buffer.wrap(b); 
          buffer.position(0); 
          reusableBitmap.copyPixelsFromBuffer(buffer); 
          frameInputStream.close(); 
          i++; 
         } catch (Exception e) { 
          System.out.println("Exception 3: " + e.getMessage()); 
         } 
         runOnUiThread(new Runnable() { 
          @Override 
          public void run() { 
           imgView.setImageBitmap(reusableBitmap); 
          } 
         }); 
        } 
    } 
    
    private ByteBuffer copyToBuffer(Bitmap bitmap) { 
        int size = bitmap.getHeight() * bitmap.getRowBytes(); 
        ByteBuffer buffer = ByteBuffer.allocateDirect(size); 
        bitmap.copyPixelsToBuffer(buffer); 
        return buffer; 
    } 
    

В каждом из вышеуказанных решений я принимаю в логарифмических лотах

ERROR/msm8960.hwcomposer(330): prepareBypass: Unable to setup bypass due to non-pmem memory 

, который я не знаю, что именно означает.

Я не работал с повторным использованием Bitmap и не знаю, какое из них является лучшим решением.

Я добавил проект, который я здесь создал: https://www.dropbox.com/sh/3xov369u1bmjpd1/qBQax4t48D, а также 2 кадра/изображения.

Ответ на Нерон T

Я пробовал эту библиотеку:

//Option IV: 
    private AQuery aquery; 

    private void startAnimation4() { 
     aquery = new AQuery(this); 
     aquery.id(R.id.imgView); 

     TimerTask updateImage = new UpdateImage4(); 
     timer.scheduleAtFixedRate(updateImage, 0, 1000/FPS); 
    } 

    class UpdateImage4 extends TimerTask { 
     public void run() { 
      try { 
       if (i == 29) { 
        i = 0; 
       } 
       runOnUiThread(new Runnable() { 
        @Override 
        public void run() { 
         //load image from file, down sample to target width of 300 pixels 
         aquery.image(framesFiles[i],300); 
        } 
       }); 
       i++; 
      } catch (Exception e) { 
       System.out.println("Exception 4: " + e.getMessage()); 
      } 
     } 
    } 

Это не работает, как я ожидал, - у меня есть мерцающий эфект перед каждой картиной.Я думаю, что сначала он очищает фотографии и после этого добавляет новый :(

ответ

0

Обратите внимание: вы не можете изменять виды изнутри потоков, не являясь UIThread (основной), попробуйте использовать AsyncTasks. Простой способ использования изображений без имея слишком много думать использует рамки как Android Query, посмотрите here.

Если вы хотите сделать это с помощью проверки руки this и this.

+0

спасибо first.I'm используя 'метод runOnUiThread'. Контекст в моем приложении более сложный, чем тот из приведенного выше примера. Я попробую файл jar файла ImageLoading из предоставленной вами ссылки (точнее, вариант загрузки изображения из файла асинхронно), но ответ на версии, которые я раскрыл в моем вопросе, более чем приветствуется. – Paul

+0

Простите, не заметили, что вы запустили его onUIThread, и я спрашивал себя, почему он не дал такую ​​ошибку. Надеюсь, библиотека работает для вас. –

+0

Я обновил ответ с результатами использования этой библиотеки. – Paul

Смежные вопросы