4

why this code works?

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Log.i("onCreate", Thread.currentThread().toString());
    textView = (TextView) findViewById(R.id.textView);
    imageView = (ImageView) findViewById(R.id.imageView);

    HandlerThread thread = new HandlerThread("myHandlerThread");
    thread.start();
    mUiHandler = new Handler(thread.getLooper());
    mUiHandler.post(new Runnable() {
        @Override
        public void run() {
            Log.i("Thread: ", Thread.currentThread().toString());
            Toast.makeText(getApplicationContext(), "Hello Cats!", Toast.LENGTH_SHORT).show();
            textView.setText("Hello Cats");
            imageView.setImageResource(R.mipmap.ic_launcher);
        }
    });

}

i read from somewhere that the only thread that can communicate with UI objects is UI thread, or i miss something

i have some research but not found the answer yet, please help, thank you so much guys.

this is what i got from log

10-13 18:47:42.888 23841-23841/th.co.me.sampleapp I/onCreate: Thread[main,5,main] 10-13 18:47:42.891 23841-24041/th.co.me.sampleapp I/Thread:: Thread[myHandlerThread,5,main]

UPDATE 1

i tried this code from @nshmura and the error occurs it is so confusing to me now

   textView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        HandlerThread thread = new HandlerThread("myHandlerThread");
        thread.start();
        mUiHandler = new Handler(thread.getLooper());
        mUiHandler.post(new Runnable() {
            @Override
            public void run() {
                Log.i("Thread: ", Thread.currentThread().toString());
                Toast.makeText(getApplicationContext(), "Hello Cats!", Toast.LENGTH_SHORT).show();
                textView.setText("Hello Cats");
                imageView.setImageResource(R.mipmap.ic_launcher);
            }
        });

    }
});

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

4 Answers4

0

the exception android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. will be thrown only if:

  1. called from non ui thread
  2. the call requires a layout change. For example if the size (width and height) of a TextView should be extended, because of the too long string

So if your ui operation doesn't lead to a layout change (create or recreate), the exception may not be thrown.

alex
  • 8,904
  • 6
  • 49
  • 75
  • I run the same example as OP with the TextView set to `wrap_content` for both width and height. The example changes the text, shouldn't it also request a layout change eventually? – Hadi Satrio Oct 13 '16 at 12:40
  • I think you missed my point… I made the size `wrap_content` to trigger a layout change. If you're proposed answer is correct, the app will crash due to `CalledFromWrongThreadException` – but it's not. – Hadi Satrio Oct 13 '16 at 12:51
  • Oh and by the way, I just tried another run with a fixed size `TextView` and the app still did not crash. – Hadi Satrio Oct 13 '16 at 12:51
  • 1
    perhaps you changes happens BEFORE `onCreate` is done. after `onCreate` the layout will be created. Look at sourcecode of `TextView#setText` – alex Oct 13 '16 at 12:58
  • tried this by changing text size onResume but got no error :( – kittenforcode Oct 14 '16 at 05:26
0

A Handler object registers itself with the thread in which it is created. It provides a channel to send data to this thread. For example, if you create a new Handler instance in the onCreate()method of your activity, it can be used to post data to the main thread. The data which can be posted via the Handler class can be an instance of the Message or the Runnable class.

So based on the below line, your handler should be belongs to HandlerThread.

mUiHandler = new Handler(thread.getLooper());

One thing I understand OnCreate() is main thread but OnClick Listener is separate Class. That means there is context difference.

for example:

// onCreate() we are writing like this
 Toast.makeText(this, "Hello Cats!", Toast.LENGTH_SHORT).show();
// but in Onclick method we are writing like this
 Toast.makeText(MainActivity.this, "Hello Cats!", Toast.LENGTH_SHORT).show();
Kona Suresh
  • 1,836
  • 1
  • 15
  • 25
-1

Check like this:

    textView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            HandlerThread thread = new HandlerThread("myHandlerThread");
            thread.start();
            mUiHandler = new Handler(thread.getLooper());
            mUiHandler.post(new Runnable() {
                @Override
                public void run() {
                    Log.i("Thread: ", Thread.currentThread().toString());
                    Toast.makeText(getApplicationContext(), "Hello Cats!", Toast.LENGTH_SHORT).show();
                    textView.setText("Hello Cats");
                    imageView.setImageResource(R.mipmap.ic_launcher);
                }
            });

        }
    });

Exception will happen:

  android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
      at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6556)
      at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:942)
      at android.view.ViewGroup.invalidateChild(ViewGroup.java:5081)
      at android.view.View.invalidateInternal(View.java:12719)
      at android.view.View.invalidate(View.java:12683)
      at android.view.View.invalidate(View.java:12667)
      at android.widget.TextView.checkForRelayout(TextView.java:7156)
      at android.widget.TextView.setText(TextView.java:4347)
      at android.widget.TextView.setText(TextView.java:4204)
      at android.widget.TextView.setText(TextView.java:4179)

Maybe the ViewRootImpl.checkThread() is not called at Activity.onCreate()

nshmura
  • 5,940
  • 3
  • 27
  • 46
-2

Try this code

mHandler = new Handler();

new Thread(new Runnable() {
    @Override
    public void run () {
        // Perform long-running task here
        // (like audio buffering).
        // you may want to update some progress
        // bar every second, so use handler:
        mHandler.post(new Runnable() {
            @Override
            public void run () {
                // make operation on UI - on example
                // on progress bar.
            }
        });
    }
}).start();
pacholik
  • 8,607
  • 9
  • 43
  • 55
Nitt
  • 1,813
  • 3
  • 12
  • 15