Cameron Fife Cameron Fife - 1 month ago 7
Java Question

calling Thread.sleep() after modifying the UI appears to result in a runtime error

I just want my code to display the values 1 to 10 in a listview. However, I want to "simulate" pausetimes by sleeping for 250 milliseconds each time an item is added into the listview. The problem is that calling Thread.sleep() results in a run time crash. The first item (the number 1) displays successfully, but it crashes after that. Any suggestions on why my program crashes?

if I comment out Thread.sleep(), this works

class Read extends AsyncTask<Void, Integer, Void> {
ArrayAdapter<String> adpt;
File file;
Scanner scan;

Read(ArrayAdapter<String> a, File f) {
adpt = a;
file = f;
try {
scan = new Scanner(file);
}
catch (IOException e) {
Toast.makeText(MainActivity.this, "Error reading file!", Toast.LENGTH_SHORT).show();
// I should do something... just not sure what.
}
}

@Override
protected void onPreExecute() {

}

@Override
protected Void doInBackground(Void... params) {
Integer i = 0;
// Toast.makeText(MainActivity.this, "Error reading file!", Toast.LENGTH_SHORT).show();
adpt.setNotifyOnChange(false);
while (scan.hasNext()) {
String j = scan.next();
adpt.add("dead");
i++;
try {
Thread.sleep(250);
}
catch (InterruptedException e) {
}
publishProgress(i);
}
return null;
}

@Override
protected void onProgressUpdate(Integer... values) {
adpt.notifyDataSetChanged();
}

@Override
protected void onPostExecute(Void result) {
scan.close();
adpt.notifyDataSetChanged();
}
}


Error Logs

E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #1
Process: com.example.multithread, PID: 4314
java.lang.RuntimeException: An error occurred while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:318)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:354)
at java.util.concurrent.FutureTask.setException(FutureTask.java:223)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6855)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1040)
at android.view.View.requestLayout(View.java:19655)
at android.view.View.requestLayout(View.java:19655)
at android.view.View.requestLayout(View.java:19655)
at android.view.View.requestLayout(View.java:19655)
at android.view.View.requestLayout(View.java:19655)
at android.view.View.requestLayout(View.java:19655)
at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:360)
at android.view.View.requestLayout(View.java:19655)
at android.widget.AbsListView.requestLayout(AbsListView.java:1988)
at android.widget.AdapterView$AdapterDataSetObserver.onChanged(AdapterView.java:840)
at android.widget.AbsListView$AdapterDataSetObserver.onChanged(AbsListView.java:6357)
at android.database.DataSetObservable.notifyChanged(DataSetObservable.java:37)
at android.widget.BaseAdapter.notifyDataSetChanged(BaseAdapter.java:50)
at android.widget.ArrayAdapter.notifyDataSetChanged(ArrayAdapter.java:310)
at android.widget.ArrayAdapter.add(ArrayAdapter.java:200)
at com.example.multithread.MainActivity$Read.doInBackground(MainActivity.java:70)
at com.example.multithread.MainActivity$Read.doInBackground(MainActivity.java:39)
at android.os.AsyncTask$2.call(AsyncTask.java:304)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243) 
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133) 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607) 
at java.lang.Thread.run(Thread.java:761) 
Application terminated.

Answer

It's not the Thread.sleep() that causes the crash. As seen in the logcat, you're trying to modify an UI component adpt in a non-UI thread.

You can move the adpt.add() call to e.g. onProgressUpdate() to make it execute on the UI thread.

Comments