Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android - notifyDataSetChanged() and threads

I have an AsyncTask that does some number crunching (more details in the next few lines) in the doInBackground() method. The data manipulated in doInBackground() is the data associated with a ListView's adapter ( a SimpleAdapter in my case). What I need to do after I process the adapter's data is call notifyDataSetChanged() and update my ListView. But calling notifyDataSetChanged() within doInBackground() displays the "Unfortunately 'app name' has stopped working" message. My error stack shows this:

06-22 08:01:49.083: E/AndroidRuntime(9955): FATAL EXCEPTION: AsyncTask #2 06-22 08:01:49.083: E/AndroidRuntime(9955): java.lang.RuntimeException: An error occured while executing doInBackground() 06-22 08:01:49.083: E/AndroidRuntime(9955): at android.os.AsyncTask$3.done(AsyncTask.java:299) 06-22 08:01:49.083: E/AndroidRuntime(9955): at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:352) 06-22 08:01:49.083: E/AndroidRuntime(9955): at java.util.concurrent.FutureTask.setException(FutureTask.java:219) 06-22 08:01:49.083: E/AndroidRuntime(9955): at java.util.concurrent.FutureTask.run(FutureTask.java:239) 06-22 08:01:49.083: E/AndroidRuntime(9955): at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230) 06-22 08:01:49.083: E/AndroidRuntime(9955): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080) 06-22 08:01:49.083: E/AndroidRuntime(9955): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573) 06-22 08:01:49.083: E/AndroidRuntime(9955): at java.lang.Thread.run(Thread.java:838) 06-22 08:01:49.083: E/AndroidRuntime(9955): Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

The last line

06-22 08:01:49.083: E/AndroidRuntime(9955): Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

gives me the information I need to debug. I solved this problem by calling notifyDataSetChanged() from onPostExecute() But what I still don't understand is how the method actually works. My questions are:

Q1. How does notifyDataSetChanged() actually update the UI ?

Q2. What is the connection between notifyDataSetChanged() and the UI thread ?

Q3. Is there any method in which notifyDataSetChanged() can be called on a worker thread ?

I am a beginner with Android and any help with these fundamental questions would be most appreciated. My internet searches have not given me the answers I wanted. Also thank you very much for your time.

like image 634
user1841702 Avatar asked Jun 22 '14 03:06

user1841702


People also ask

How does notifyDataSetChanged work on Android?

This android function notifies the attached observers that the underlying data has been changed and any View reflecting the data set should refresh itself. You can use notifyDataSetChanged ArrayAdapter, but it only works if you use the add(), insert(), remove(), and clear() on the Adapter.

What happens when we call notifyDataSetChanged?

notifyDataSetChanged. Notify any registered observers that the data set has changed. There are two different classes of data change events, item changes and structural changes. Item changes are when a single item has its data updated but no positional changes have occurred.


1 Answers

Q1. How does notifyDataSetChanged() actually update the UI ?

Actually, notifyDataSetChanged() only fires the onChanged() method on its associated observers. For AdapterView subclasses (such as ListView), this ends up calling requestLayout(), which schedules a layout pass of the view tree.

This means that the ListView does not "immediately" recreate its children views, but it will do so as soon as the UI thread is free to process messages. Eventually, getView() will be called for each item.

Q2. What is the connection between notifyDataSetChanged() and the UI thread ?

Basically, modifying an Adapter's contents from a background thread is not allowed. notifyDataSetChanged() is just the way to notify the AdapterView that its data has changed, and it should be redrawn.

Q3. Is there any method in which notifyDataSetChanged() can be called on a worker thread ?

Actually, there are several. You can use a Handler, AsyncTask.onPostExecute(), Activity.runOnUiThread() or View.post().

like image 133
matiash Avatar answered Oct 20 '22 11:10

matiash