Skip to content Skip to sidebar Skip to footer

Why Is There No Calledfromwrongthreadexception When A New Thread Operates Ui Immediately?

I was trying to dynamically add a view in to another view in MainActivity: activity_main.xml

Solution 1:

The root cause of the crash is the ViewRootImpl.checkThread () method.

voidcheckThread() {

 if (mThread != Thread.currentThread()) {

  throw new CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views.");

}

However, the invalidate () method is not called when the view is not measured , the invalidate() method will eventually execute to the checkThread().

ok , see :

newThread(newRunnable() {
        @Overridepublicvoidrun() {
            System.err.println("getMeasuredHeight:" + parentView.getMeasuredHeight());
            System.err.println("getMeasuredWidth:" + parentView.getMeasuredWidth());
            parentView.removeView(view);
        }
    }).start();

will get that:

W/System.err: getMeasuredWidth:0W/System.err: getMeasuredHeight:0

can see, the measurement process has not been completed , so we can change the UI without triggering the invalidate() and not trigger exception.

then, keep it sleep 50 ms:

    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.err.println("getMeasuredHeight:" + parentView.getMeasuredHeight());
            System.err.println("getMeasuredWidth:" + parentView.getMeasuredWidth());
            parentView.removeView(view);
        }
    }).start();

will get that:

W/System.err: getMeasuredHeight:360W/System.err: getMeasuredWidth:1080

can see, the measurement process has been completed, then , when we change the UI the invalidate will be triggering,and then it call the checkThread().

Solution 2:

In short, avoid manipulating Android UI elements off the UI Thread; you're going to run into unpredictable behaviour.

You can synchronize your update to the UI by using Activity.runOnUiThread(Runnable r), which should alleviate the problem. i.e.

newThread(newRunnable(){
            @Overridepublicvoidrun() {
                try{
                    Thread.sleep(3000);
                    // Synchronize on the UI Thread.
                    MainActivity.this.runOnUiThread(newRunnable() {  @Overridepublicfinalvoidrun() {
                        // Remove the View.
                        parentView.removeView(view); 
                    } });
                }
                catch (Exception e){
                    e.printStackTrace();
                }
            }
        }).start();

However, there is a much better construct you should be using; you should never underestimate the computational burden of allocating and running a new Thread!

You can achieve the exact same functionality using:

(newHandler(Looper.getMainLooper())).postDelayed(newRunnable() {  
     /* Update the UI. */ 
}, 3000)); 

This will achieve the same behaviour, without leaving you having to worry about managing synchronization along the UI Thread. Additionally, you won't be launching a whole new Thread whose only job is to sleep!

This should perform a great deal better.

Solution 3:

No need to use a thread for a delay, instead :

newThread(newRunnable(){
            @Overridepublicvoidrun() {
                try{
                    Thread.sleep(3000);
                }catch (Exception e){
                }
                parentView.removeView(view);
            }
        }).start();

Do :

newHandler().postDelayed(newRunnable() {
                @Overridepublicvoidrun() {
                    parentView.removeView(view);
                }
            }, 3000);

Post a Comment for "Why Is There No Calledfromwrongthreadexception When A New Thread Operates Ui Immediately?"