Pytanie Android - zaktualizuj bitmapę z wątku z timerem


Mam projekt Androida złożony z jednego układu z ImageView.

public class MainActivity extends AppCompatActivity {

    /* original and stretched sized bitmaps */
    private Bitmap bitmapOriginal;
    private Bitmap bitmapStretched;

    /* the only view */
    private ImageView iv;

    ....
}

Ten ImageView jest aktualizowany przez tę działającą funkcję

    runnable = new Runnable() {
        @Override
        public void run() {
            iv.setImageBitmap(bitmapStretched);
        }
    };

a operacja jest uruchamiana przez tymczasową funkcję JNI, działającą na wątku tła, który wywołuje ją 60 razy na sekundę.

public void jniTemporizedCallback(int buf[]) {

    /* set data to original sized bitmap */
    bitmapOriginal.setPixels(buf, 0, origWidth, 0, 0, origWidth, origHeight);

    /* calculate the stretched one */
    bitmapStretched = Bitmap.createScaledBitmap(bitmapOriginal, width, height, false);

    /* tell the main thread to update the image view */
    runOnUiThread(runnable);
}

Po narysowaniu jakiejś ramki aplikacja ulega awarii z następującym komunikatem.

A/OpenGLRenderer: Task is already in the queue!

Domyślam się, że to dlatego, że renderer nie skończył w pełni renderować poprzedniej klatki ImageView i wpada w złość.

Jeśli usunę runOnUiThread(runnable); problem znika (oczywiście)

Jak można tego uniknąć? Jak mogę zsynchronizować moją aplikację z rendererem OpenGL?

Próbowałem także rozszerzyć ImageView i narysować bitmapę na płótnie do funkcji onDraw, ale otrzymałem ten sam wynik


14
2017-08-22 11:30


pochodzenie


Niech to Ci pomoże. stackoverflow.com/questions/5869114/... - Husnain Khan
spróbuj tego, aby sprawdzić, czy poprzedni wątek jest zrobiony czy nie. Sprawdź to stackoverflow.com/questions/8799373/... - Abhishek


Odpowiedzi:


Chyba próbujesz stworzyć bitmapOriginal przez wątek. Dlatego, gdy kompilator próbuje wywołać ponownie po 60 sekundach, otrzymuje te same obiekty i nie może zidentyfikować zadania. Sugerowałbym lepiej, jak poniżej.

 public void jniTemporizedCallback(int buf[]) {
      // Initialize
      bitmapOriginal = Bitmap.createBitmap(///)
     /* set data to original sized bitmap */
     bitmapOriginal.setPixels(buf, 0, origWidth, 0, 0, origWidth, origHeight);

    /* calculate the stretched one */
    bitmapStretched = Bitmap.createScaledBitmap(bitmapOriginal, width, height,false);

    /* tell the main thread to update the image view */
    runOnUiThread(runnable);
}

6
2017-09-22 07:31





Właściwym sposobem synchronizacji logiki rysowania z szybkością wyświetlania urządzenia jest użycie SurfaceView zamiast ImageView. Zamiast przesuwania ramek do widoku przy użyciu własnego zegara, powinieneś utworzyć renderujący wątek, który spróbuje renderować ramki tak szybko jak to możliwe. Kiedy zadzwonisz surfaceHolder.lockCanvas()system Android automatycznie zablokuje się, dopóki nie nadejdzie czas renderowania ramki. Po odblokowaniu płótna za pomocą unlockCanvasAndPost(), system narysuje bufor na ekranie.

Widzieć https://developer.android.com/guide/topics/graphics/2d-graphics.html#on-surfaceview po więcej informacji. Mam nadzieję że to pomoże!


5
2017-09-23 17:44





Problem był całkowicie niezwiązany z samą bitmapą ....

To był sygnał zegara czasu rzeczywistego, który zepsuł się z Androidem RenderThread.

Dalsze wyjaśnienie tutaj:

Zegar czasu rzeczywistego Android i JNI


4
2017-09-27 12:23





Podaj tutaj cel użycia takiej metody renderowania? Co chcesz zrobić ?, istnieje wspaniała funkcja animacji w silniku Android, może być to zadanie można zrobić za pomocą tej animacji.

Jeszcze jeden, jeśli użyjesz kodów takich jak Twoja, bateria telefonu zostanie uruchomiona do zera bardzo szybko coz, to załaduje procesor / gpu na maksimum.

w każdym razie - spróbuj umieścić bloki z uruchomionego zadania, ustaw bool taskRun = true na starcie i czeku if (!taskRun){ taskRun = true; //here start your task..} i na wątku ui po aktualizacji ui możesz się przełączyć taskRun = false; Dzięki temu możesz pominąć niektóre klatki, ale nie powinno się zawieszać.


4
2017-09-27 12:49





Problem polega na tym, że Handler głównego wątku zawiera odniesienie do twojego Runnable. Kiedy chcesz uruchomić swój Runnable po raz drugi stary Runnable jest już w Message Queue, stąd Task is already in the queue wiadomość. Jeśli utworzysz Runnable za każdym razem, gdy chcesz wykonać Runnable jak w poniższym kodzie, myślę, że problem zostanie rozwiązany.

public void jniTemporizedCallback(int buf[]) {
    /* set data to original sized bitmap */
    bitmapOriginal.setPixels(buf, 0, origWidth, 0, 0, origWidth,         origHeight);

    /* calculate the stretched one */
    bitmapStretched = Bitmap.createScaledBitmap(bitmapOriginal, width, height, false);

     /* tell the main thread to update the image view */
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            iv.setImageBitmap(bitmapStretched);
        }
    });    
}

3
2017-09-28 04:51





Myślę, że masz rację, ponieważ nie masz pewności, że Android renderuje obrazy w 60 klatkach na sekundę. I tak, myślę, że potrzebujesz właśnie zsynchronizować natywny oddzwonień bitmapowy z Androidem Render. A więc zacznijmy.

Wolę używać blokady z Java stosu współbieżności. Ponieważ widzisz, kiedy blokujesz obiekt i kiedy go odblokujesz. W przypadku korzystania z lotnych (na przykład pewnych ograniczeń referencyjnych) obiektu Bitmap, należy sprawdzić blokowanie tego obiektu w miejscach, w których korzystasz z Bitmapy.

Myślę też, że powinieneś użyć opcji Zablokuj z TEN PRZYKŁAD (aby odblokować obiekt Zablokuj z dowolnego innego wątku). Oto przykład. Poniższy przykład działa poprawnie. Po prostu nie zapomnij o usuwaniu kontekstu i zatrzymywaniu zadania:

public class MainActivity extends AppCompatActivity {

    /* Initialize lock (avoid lazy init, with your methods) */
    private ReentrantLock lock = new ReentrantLock();

    ............

    private runnableDrawImage = new Runnable() {
        @Override
        public void run() {
            iv.setImageBitmap(bitmapStretched);
            lock.unlock();
        }
    };

    .......... 


   public void jniTemporizedCallback(int buf[]) {
       /* synchronize by locking state*/
       lock.lock();

       bitmapOriginal = Bitmap.createBitmap(///)
       bitmapOriginal.setPixels(buf, 0, origWidth, 0, 0, origWidth, origHeight);

       bitmapStretched = Bitmap.createScaledBitmap(bitmapOriginal, width, height,false);
       MainActivity.this.runOnUiThread(runnableDrawImage);
   }

}

1
2017-09-22 11:59