I am trying to load some data into a layout. I am loading the data via an AsyncTask
, which will load data from an SQLite datasource then insert that data into the layout on completion. I'd like to show a progress bar during this time, by adding it to the layout when the task starts, and removing it from the layout when the task completes.
Here's a rough outline:
private class ItemLoader extends AsyncTask<Void, Void, List<Item>> {
private Context context;
private LayoutInflater inflater;
private View loading;
public DataLoader(Context context) {
this.context = context;
this.inflater = LayoutInflater.from(context);
this.loading = createLoading();
}
private View createLoading() {
return inflater.inflate(R.layout.loading, null);
}
@Override
public void onPreExecute() {
// Insert 'loading' view into the 'items' layout.
LinearLayout layout = (LinearLayout) findViewById(R.id.items);
layout.addView(loading);
}
@Override
protected List<Item> doInBackground(Void... params) {
return loadItemsFromDataSource();
}
private List<Item> loadItemsFromDataSource() {
ItemsSource d = new ItemsDataSource(context);
d.open();
try {
return d.lookupItems();
} finally {
d.close();
}
}
@Override
public void onPostExecute(List<Item> items) {
LinearLayout layout = (LinearLayout) findViewById(R.id.items);
// Remove 'loading' view from the 'items' layout.
layout.removeView(loading);
// Add items to the 'items' layout.
for (Item item: items) {
addItemToLayout(item, layout);
}
}
My loading layout is copied from the Android API docs for ProgressBar:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@android:style/Widget.ProgressBar.Small"
android:layout_marginRight="5dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/loading" />
</LinearLayout>
The AsyncTask
is called during the activity's onCreate()
method:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.itemsLayout);
...
new ItemLoader(this).execute();
}
I can see that the 'loading' spinner and text are added to my layout while the data is loaded in the background, and that this spinner/text are removed when the data is added to the layout.
But the spinner is not spinning when the doInBackground()
method is running. I'm adding and removing the progress bar in the UI thread (in the onPreExecute()
and onPostExecute()
methods).
Interestingly, if a add a sleep()
call in doInBackground()
before the data is loaded from the data source, then I can see the spinner is spinning. But once the data source call is made, the spinner stops spinning:
@Override
protected void doInBackground(Void... params) {
sleepForFiveSeconds(); // spinner is spinning
List<Item> items = loadItemsFromDataSource(); // spinner stops spinning
sleepForFiveSeconds(); // spinner still not spinning
return items;
}
And it's not just a spinner manipulated within an AsyncTask
. I notice the same behavior when a ProgressBar is added to the activity, either by defining it in the activity's layout or adding it dynamically in the activity's onCreate()
method. In these cases, the spinner is spinning until the data source lookup is performed. It will start spinning again once the data source lookup returns.
So it seems that the spinners are accessed within the UI thread, but data source operations in a background thread are causing these spinners to stop.
Any suggestions?
Edited to add: this is with my Nexus 7 (Android 4.2) and HTC phone (Android 2.3.4), with the android manifest setting minimum SDK version to 10.