2016-08-22 17 views
14

Mam projekt Androida złożony z jednego układu z ImageView.Android - Zaktualizuj bitmapę z wątku timera

public class MainActivity extends AppCompatActivity { 

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

    /* the only view */ 
    private ImageView iv; 

    .... 
} 

ImageView ta jest aktualizowana przez ten runnable funkcji

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

i runnable jest prowadzony przez zwłokę funkcji JNI, działa na wątek tła, które nazywają go 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 wraz z następującym komunikatem.

A/OpenGLRenderer: Task is already in the queue! 

Domyślam się, że jest to spowodowane tym, że renderer nie zakończył w pełni renderowania poprzedniej klatki ImageView i wpada w złość.

Jeśli usunąć runOnUiThread(runnable); problem zniknie (oczywiście)

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

Próbowałem również rozszerzyć ImageView i narysować mapę bitową na płótnie do funkcji OnDraw ale mam ten sam rezultat

+0

Niech to ci pomoże. http://stackoverflow.com/questions/5869114/loading-new-image-each-second –

+0

wypróbuj to, aby sprawdzić, czy poprzedni wątek jest zrobiony czy nie. sprawdź to http://stackoverflow.com/questions/8799373/waiting-for-a-runnable-to-complete-before-running-another-runnable – Abhishek

Odpowiedz

4

Problem był całkowicie niezwiązane z samą bitmapę ....

To był sygnał zegara czasu rzeczywistego, które pomieszane z Androidem RenderThread.

Dalsze wyjaśnienie tutaj:

Android and JNI real time clock

6

Chyba próbujesz stworzyć bitmapOriginal ouside wątku. 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); 
} 
1

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 ze stosu współbieżności Java. 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.

Również myślę, że powinieneś użyć Lock z THIS EXAMPLE (aby odblokować obiekt Lock z dowolnego innego wątku). Oto przykład. Poniższy przykład działa poprawnie. Tylko nie zapomnij o Context usuwania i zatrzymywania 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); 
    } 

} 
5

Właściwym sposobem, aby zsynchronizować logikę rysowania z liczbą klatek 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. Gdy zadzwonisz pod numer surfaceHolder.lockCanvas(), system Android automatycznie zablokuje się, dopóki nie nadejdzie czas renderowania ramki. Po odblokowaniu płótna przy użyciu unlockCanvasAndPost() system narysuje bufor na ekranie.

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

4

podać tutaj użytek takiego sposobu 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 jedno, jeśli użyjesz kodów takich jak Twoja bateria telefonu będzie działać do zera bardzo szybko coz to załaduje procesor/gpu do max.

w każdym razie - starają się umieszczać bloki z systemem zadanie, ustaw bool taskRun = true na początku i sprawdź if (!taskRun){ taskRun = true; //here start your task..} i na wątku UI po aktualizacji UI można przełączyć się taskRun = false; Stosując ten można pominąć niektórych klatek, ale nie powinien upaść.

3

Problem polega na tym, że Handler głównego wątku zawiera odniesienie do Twojego Runnable. Jeśli chcesz uruchomić swój Runnable po raz drugi, stary Runnable jest już w komunikacie Message Queue, a więc Task is already in the queue. 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); 
     } 
    });  
} 
Powiązane problemy