3

Getting an error: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
I really dont have a clue why.

  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Button generate = (Button) findViewById(R.id.gen);
    final TextView dice1 = (TextView) findViewById(R.id.dice1);
    final TextView dice2 = (TextView) findViewById(R.id.dice2);
    final TextView dice3 = (TextView) findViewById(R.id.dice3);


    generate.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {           
            dice1.setText(" ");
            dice2.setText(" ");
            dice3.setText(" ");         
            Thread thread = new Thread()
            {
                @Override
                public void run() {
                    try {
                        while(true) {
                            sleep(2000);
                            setText("lol", "lol", "lol");
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            thread.start();             
        }

    });
}
public void setText(String d1, String d2, String d3){
    dice1.setText(d1);
    dice2.setText(d2);
    dice3.setText(d3);  
}

Thanks for helping.

Tom Doe
  • 331
  • 6
  • 23

3 Answers3

5

You have this error because you manipulate views in another thread than the UI thread. Your setText method modify the text of 3 TextViews (dice1, dice2, dice3).

You should call this method with runOnUiThread like this:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Button generate = (Button) findViewById(R.id.gen);
    final TextView dice1 = (TextView) findViewById(R.id.dice1);
    final TextView dice2 = (TextView) findViewById(R.id.dice2);
    final TextView dice3 = (TextView) findViewById(R.id.dice3);

    generate.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {           
            dice1.setText(" ");
            dice2.setText(" ");
            dice3.setText(" ");         
            Thread thread = new Thread()
            {
                @Override
                public void run() {
                    try {
                        while(true) {
                            sleep(2000);
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    setText("lol", "lol", "lol");
                                }
                            });
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            thread.start();             
        }

    });
}
public void setText(String d1, String d2, String d3){
    dice1.setText(d1);
    dice2.setText(d2);
    dice3.setText(d3);  
}
Donkey
  • 1,176
  • 11
  • 19
  • sleep inside a thread is a bad design. http://developer.android.com/training/articles/perf-anr.html – Raghunandan Jun 18 '13 at 20:05
  • @Raghunandan If you call _sleep()_ on the main thread yes, but not in this case. However it's sure that using an [AsynTask](http://developer.android.com/reference/android/os/AsyncTask.html) for example is probably better than create a Thread each time the user clicks on 'generate' button... – Donkey Jun 18 '13 at 20:27
  • If you implement `Thread or HandlerThread`, be sure that your UI thread does not block while waiting for the worker thread to complete— `do not call Thread.wait() or Thread.sleep()`. Quoting from doc. – Raghunandan Jun 18 '13 at 20:28
  • Yes, and it means "don't call _Thread.wait()_ on the UI thread waiting for another thread to call _Thread.notify()_ for instance. Calling _Thread.sleep()_ (or _Thread.wait()_) on another thread than the UI thread won't cause an ANR. – Donkey Jun 18 '13 at 20:52
  • it blocks the worker thread. so it is not good. i am not saying it will cause anr. – Raghunandan Jun 18 '13 at 20:53
2

You are, literally, trying to set fields on your view from the wrong thread. Your OnClickListener creates a new thread, but only the thread that created a view and its hierarchy may alter those views.

You might refactor to use http://developer.android.com/reference/java/util/concurrent/ScheduledThreadPoolExecutor.html

Alternatively, you could just wrap your setter code to be inside of a runOnUiThread block. There's an example on this site, along with a caution of why this might not be a good idea: Android: RunOnUiThread vs AsyncTask.

Community
  • 1
  • 1
Eric
  • 18,512
  • 4
  • 29
  • 34
1

You are setting setText on a thread apart. Put the method calling manipulating UI components inside a runOnUiThread block.

Fustigador
  • 6,339
  • 12
  • 59
  • 115