Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unpredictable behavior when accessing View from another thread

I was trying to build an app which have some thread usage. While working I found unpredictable behavior while I was trying to access a TextView from thread. I know android forbid us from accessing views directly from another thread.I also know how to access main thread's views from another thread using AsyncTask, Handler , Activity.runOnUiThread(Runnable) , View.post(Runnable) , View.postDelayed(Runnable, long) etc.

Here is my code snippet -

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final TextView tv = (TextView) findViewById(R.id.tv);
        Thread t = new Thread(new Runnable() {

            @Override
            public void run() {
                /*try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }*/

                /*for(int i= 0 ; i<10000000 ;i++){
                    Log.i("logger"," i ="+i);
                    tv.setText("i = "+i);
                }*/

                tv.setText("This is the new text");
            }
        });
        t.start();
    }

When I am running this code , no crash occur but as soon as I uncomment Thread.sleep() or for loop part ,crash occur. Can anyone explain this behavior , why it happening like this and why crash is not occurring with given code.Please don't post answers explaining how to do it.

Thanks in advance.

This is the crash log when I uncomment Thread.sleep() code section -

05-26 21:11:47.244: E/AndroidRuntime(14310): FATAL EXCEPTION: Thread-13346
05-26 21:11:47.244: E/AndroidRuntime(14310): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
05-26 21:11:47.244: E/AndroidRuntime(14310):    at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:5225)
05-26 21:11:47.244: E/AndroidRuntime(14310):    at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1062)
05-26 21:11:47.244: E/AndroidRuntime(14310):    at android.view.View.requestLayout(View.java:15316)
05-26 21:11:47.244: E/AndroidRuntime(14310):    at android.view.View.requestLayout(View.java:15316)
05-26 21:11:47.244: E/AndroidRuntime(14310):    at android.view.View.requestLayout(View.java:15316)
05-26 21:11:47.244: E/AndroidRuntime(14310):    at android.view.View.requestLayout(View.java:15316)
05-26 21:11:47.244: E/AndroidRuntime(14310):    at android.view.View.requestLayout(View.java:15316)
05-26 21:11:47.244: E/AndroidRuntime(14310):    at android.view.View.requestLayout(View.java:15316)
05-26 21:11:47.244: E/AndroidRuntime(14310):    at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:292)
05-26 21:11:47.244: E/AndroidRuntime(14310):    at android.view.View.requestLayout(View.java:15316)
05-26 21:11:47.244: E/AndroidRuntime(14310):    at android.widget.TextView.checkForRelayout(TextView.java:6659)
05-26 21:11:47.244: E/AndroidRuntime(14310):    at android.widget.TextView.setText(TextView.java:3670)
05-26 21:11:47.244: E/AndroidRuntime(14310):    at android.widget.TextView.setText(TextView.java:3528)
05-26 21:11:47.244: E/AndroidRuntime(14310):    at android.widget.TextView.setText(TextView.java:3503)
05-26 21:11:47.244: E/AndroidRuntime(14310):    at com.example.sampleproject.MainActivity$1.run(MainActivity.java:33)
05-26 21:11:47.244: E/AndroidRuntime(14310):    at java.lang.Thread.run(Thread.java:864)
05-26 21:11:47.284: D/memalloc(14310): ion: Mapped buffer base:0x5432a000 size:2088960 offset:0 fd:66
like image 309
BinaryGuy Avatar asked May 26 '15 12:05

BinaryGuy


1 Answers

Because of a race condition.

Essentially the crash comes when the view tries to invalidate itself in a wrong thread. setText() does not invalidate the view unless the view has been measured at least once and the TextView has a non-null mLayout.

When you create a new view hierarchy with setContentView(), the message that measures the views is posted on the UI thread's queue but the chances are your thread gets to run before the measure pass has created a layout for the text view.

For details, examine the source.

like image 80
laalto Avatar answered Sep 23 '22 00:09

laalto