BinaryGuy
BinaryGuy

Reputation: 407

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

Upvotes: 6

Views: 122

Answers (4)

laalto
laalto

Reputation: 152867

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.

Upvotes: 4

balu b
balu b

Reputation: 272

below code working fine for me:

public class MainActivity extends Activity {

        Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                Bundle data = msg.getData();
                String result=data.getString("key");
                textview.setText(result);
            }
        };

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            final TextView tv = (TextView) findViewById(R.id.tv);
            Thread t = new Thread(new Runnable() {

                @Override
                public void run() {

                    Bundle bundle=new Bundle();
                    bundle.putString("key","you data");
                    Message message=new Message();
                    message.setData(bundle);
                    handler.sendMessage(message);

                }
            });
            t.start();

        }
    }

Upvotes: 0

Chackle
Chackle

Reputation: 2269

Try executing any code that interacts with the UI like this

runOnUiThread(new Runnable() {
  @Override
  public void run() {
      // Put your UI interactions here
  }
});

Upvotes: 0

Stefano Vuerich
Stefano Vuerich

Reputation: 996

You cannot update UI from another thread, use this instead

@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() {

            runOnUiThread(new Runnable() {

                    @Override
                    public void run() {
                        tv.setText("This is the new text");
                    }
                });  
        }
    });
    t.start();
}

If you want to know more here a good article:

http://android-developers.blogspot.co.uk/2009/05/painless-threading.html

Upvotes: 0

Related Questions