Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android - Update Bitmap from timer thread

I got an Android project composed by a single Layout with an ImageView.

public class MainActivity extends AppCompatActivity {

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

    /* the only view */
    private ImageView iv;

    ....
}

This ImageView is updated by this runnable function

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

and the runnable is ran by a temporized JNI function, running on a background thread, that call it 60 times per second.

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

After some frame is drawn, the app crashes with the following message.

A/OpenGLRenderer: Task is already in the queue!

I guess this is because the renderer didn't finish to fully render the previous frame of the ImageView and gets angry.

If i remove runOnUiThread(runnable); the problem disappear (obviously)

How can avoid this? How can i syncronize my application with the openGL renderer?

I also tried to extend ImageView and draw the bitmap on canvas into the onDraw function but i got the same result

like image 627
Davide Berra Avatar asked Aug 22 '16 11:08

Davide Berra


2 Answers

I guess you're trying create bitmapOriginal ouside the thread. Therefore, when compiler is trying to call again after 60 seconds, it's getting same objects and couldn't identify the task. I would suggest better as below.

 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);
}
like image 54
urveshpatel50 Avatar answered Oct 19 '22 13:10

urveshpatel50


The proper way to synchronize your drawing logic with the device's frame rate is to use a SurfaceView instead of an ImageView. Instead of pushing frames to the View with your own timer, you should create a rendering Thread that tries to render frames as fast as possible. When you call surfaceHolder.lockCanvas(), the Android system will automatically block until it is time to render the frame. When you unlock the canvas using unlockCanvasAndPost(), the system will draw the buffer to the screen.

See https://developer.android.com/guide/topics/graphics/2d-graphics.html#on-surfaceview for more info. Hope this helps!

like image 26
Jschools Avatar answered Oct 19 '22 12:10

Jschools