I have an adapter and a spinner view which is set to use the adapter for it’s entries. I’m adding items to adapter from a list of all files in /assets/ folder, I found that this task takes very long time (even about 2 seconds for a list of 2 files on a 1.5Ghz phone!). Then I came up to use a worker thread to gather my list and not block UI thread. here is my code:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.settings);
adapter = new ArrayAdapter<String>(SettingsActivity.this, android.R.layout.simple_spinner_item, fontsName);
Spinner fontsSpinner = (Spinner) findViewById(R.id.settings_font_spinner);
fontsSpinner.setAdapter(adapter);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
// The thread to gather font names from /assets/fonts/
thread = new Thread(){
@Override
public void run(){
try {
String[] fileList = getAssets().list("fonts");
if (fileList != null)
for (int i=0; i<fileList.length; i++) {
adapter.add(fileList[i]);
}
} catch (IOException e) {
}
runOnUiThread(new Runnable(){
@Override
public void run() {
adapter.notifyDataSetChanged();
}
});
}
};
thread.start();
}
but it causes an error and crash:
08-17 13:54:01.017: E/AndroidRuntime(17929): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
Am I using runOnUiThread() correctly?! It’s interesting that this code works perfectly when not using any thread, but it blocks UI (which is a pain).
any help on this please?
As zapl mentioned,
adapter.add(Object)has to be called from the UI thread.You should use Android’s AsyncTask class rather than a Thread object. You could, for instance, load the data from file in AsyncTask’s
doInBackground(Params...)method and update the list view in theonPostExecute(Result)method.Furthermore, you don’t need to call
notifyDataSetChanged()explicitly; calls toadapter.add(Object)will notify the ListView that the adapter has changed.