问题
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?
回答1:
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 the onPostExecute(Result)
method.
Furthermore, you don't need to call notifyDataSetChanged()
explicitly; calls to adapter.add(Object)
will notify the ListView that the adapter has changed.
回答2:
Instead of
for (int i=0; i<fileList.length; i++) {
adapter.add(fileList[i]);
}
Use the following instead (assuming fontsName
is an ArrayList<String>
):
for (int i=0; i<fileList.length; i++) {
fontsName.add(fileList[i]);
}
The idea is to update the underlying data in the adapter (which is fine to be done on a different thread) and then notify the adapter about the change from the UI thread (using notifyDataSetChanged()
as you have done).
来源:https://stackoverflow.com/questions/12003351/why-i-cant-use-runonuithread-to-update-an-adapter