14

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

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
Davide Berra
  • 6,387
  • 2
  • 29
  • 50
  • May this Help you. http://stackoverflow.com/questions/5869114/loading-new-image-each-second – Husnain Khan Aug 22 '16 at 11:32
  • try this to check if previous thread is done or not. check this http://stackoverflow.com/questions/8799373/waiting-for-a-runnable-to-complete-before-running-another-runnable – Abhishek Sep 29 '16 at 03:12

6 Answers6

6

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);
}
urveshpatel50
  • 1,675
  • 16
  • 35
5

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!

Jschools
  • 2,698
  • 1
  • 17
  • 18
4

Problem was totally unrelated to the Bitmap itself....

It was the real time clock signal that messed with Android RenderThread.

Further explanation here:

Android and JNI real time clock

Community
  • 1
  • 1
Davide Berra
  • 6,387
  • 2
  • 29
  • 50
4

Provide here purposes of use such method for rendering? What you want to do?, there are great animation functionality in android engine, may be this task can be done with this animation.

One more if you will use codes like yours battery of phone will run to zero very fast coz this will load cpu/gpu to max.

in anyway - try to place blocks from running task, set bool taskRun = true on start and check if (!taskRun){ taskRun = true; //here start your task..} and on ui thread after updating ui you can switch to taskRun = false; Using this you can skip some frames, but should not crash.

Stepan Maksymov
  • 2,618
  • 19
  • 31
3

The problem is that the Handler of the main thread is keeping a reference to your Runnable. When you want to run your Runnable for the second time, the old Runnable is already in the Message Queue, hence Task is already in the queue message. If you create a Runnable every time u want to execute the Runnable like in the code below, I think the problem will be solved.

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);
        }
    });    
}
Tin Tran
  • 2,265
  • 1
  • 14
  • 17
1

I think you are right with reason, because you cannot be sure, that Android render images in 60 FPS. And yeah, I think you need just synchronize Bitmap Native Callback with Android Render. So, lets start.

I prefer using Lock from concurrency stack Java. Because you see, when you lock object, and when you unlock. In case of using volatile (for example, sure there also reference restrictions) on Bitmap object, you need to check locking this object in very places, where you using Bitmap.

Also I think you should use Lock from THIS EXAMPLE (to unlock Lock object from any other Thread). So, here is example. Example below will work properly. Just don't forget about Context deleting and stopping task:

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

}
Community
  • 1
  • 1
GensaGames
  • 5,538
  • 4
  • 24
  • 53