-1

Am new to Android and trying to call invalidate function repeatedly. So i choose thread to do that. My Thread is running properly but when tried to call invalidate () its crashing Below is the code snippet.

class MyCanvas extends View {
    private static final String TAG = "MY CANVAS EXAMPLE";

    public MyCanvas(Context context){
        super(context);
        Log.e ( TAG, " MY CANVAS CONSTRUCTOR GOT CALLED ");
        // TODO Auto-generated constructor stub
        Thread newBackground = new Thread ( new Runnable(){

                @Override
                public void run() {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    invalidate();
                    Log.e ( TAG, " MY THREAD GOT CALLED ");
                    // TODO Auto-generated method stub
                }
            });
        newBackground.start();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        Log.e ( TAG, " MY CANVAS GOT CALLED ");
        // TODO Auto-generated method stub
        super.onDraw(canvas);
    }       
}

and below is the crash log observed

09-23 17:41:09.299: E/MY CANVAS EXAMPLE(1839):  MY CANVAS CONSTRUCTOR GOT CALLED 
09-23 17:41:09.599: E/MY CANVAS EXAMPLE(1839):  MY CANVAS GOT CALLED 
09-23 17:41:10.329: W/dalvikvm(1839): threadid=9: thread exiting with uncaught exception (group=0x40018578) 
09-23 17:41:10.329: E/AndroidRuntime(1839): FATAL EXCEPTION: Thread-10 
09-23 17:41:10.329: E/AndroidRuntime(1839): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
09-23 17:41:10.329: E/AndroidRuntime(1839):     at android.view.ViewRoot.checkThread(ViewRoot.java:3020) 
09-23 17:41:10.329: E/AndroidRuntime(1839):     at android.view.ViewRoot.invalidateChild(ViewRoot.java:647) 
09-23 17:41:10.329: E/AndroidRuntime(1839):     at android.view.ViewRoot.invalidateChildInParent(ViewRoot.java:673) 
09-23 17:41:10.329: E/AndroidRuntime(1839):     at android.view.ViewGroup.invalidateChild(ViewGroup.java:2511) 
09-23 17:41:10.329: E/AndroidRuntime(1839):     at android.view.View.invalidate(View.java:5279)     
09-23 17:41:10.329: E/AndroidRuntime(1839):     at com.example.canvas.MainActivity$MyCanvas$1.run(MainActivity.java:83)
09-23 17:41:10.329: E/AndroidRuntime(1839):     at java.lang.Thread.run(Thread.java:1019)
Batuhan Coşkun
  • 2,961
  • 2
  • 31
  • 48
Aswin
  • 1

3 Answers3

1
android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

invalidate() can only be called on the main UI thread.

Don't use a thread for such delays. Instead, consider using a Handler with postDelayed() to post a Runnable to run on the main UI thread after the delay.

(There's also postInvalidate() in case you want to post an invalidate() message from a background thread if you insist on using a separate thread. Not using a separate thread is a better option.)

laalto
  • 150,114
  • 66
  • 286
  • 303
0

As far as I know, the method invalidate() isn't allowed to be run outside the main (UI) thread. If you read the details of the Exception you've got then you could see this :

09-23 17:41:10.329: E/AndroidRuntime(1839): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

To fix it, simply run invalidate() on the main thread by using runOnUiThread()method. This question should provide you more information as of how.

edit: A more elegant solution is to call postInvalidate() as described by BlackBelt. This way you wouldn't have to create a new Runnable instance. I stand corrected.

Community
  • 1
  • 1
Hadi Satrio
  • 4,272
  • 2
  • 24
  • 45
0

invalidate(); schedule a redesign of the view hierarchy which, in turn, can be only accessed by the UI Thread and not from your background thread. You should use postInvalidate(). From the documentation

Cause an invalidate of the specified area to happen on a subsequent cycle through the event loop. Use this to invalidate the View from a non-UI thread.

Blackbelt
  • 156,034
  • 29
  • 297
  • 305