Looper是每条线程里的Message Queue的管家。Android没有Global的Message Queue,而Android会自动替主线程(UI线程)建立Message Queue,但在子线程里并没有建立Message Queue。所以调用Looper.getMainLooper()得到的主线程的Looper不为NULL,但调用Looper.myLooper()得到当前线程的Looper就有可能为NULL。
对于子线程使用Looper,API Doc提供了正确的使用方法:
class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); //创建本线程的Looper并创建一个MessageQueue mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop(); //开始运行Looper,监听Message Queue } }
现在来看一个例子,模拟从网络获取数据,加载到ListView的过程:
public class ListProgressDemo extends ListActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.listprogress); ((Button) findViewById(R.id.load_Handler)).setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View view) { data = null; data = new ArrayList<String>(); adapter = null; showDialog(PROGRESS_DIALOG); new ProgressThread(handler, data).start(); } }); } @Override protected Dialog onCreateDialog(int id) { switch(id) { case PROGRESS_DIALOG: return ProgressDialog.show(this, "", "Loading. Please wait...", true); default: return null; } } private class ProgressThread extends Thread { private Handler handler; private ArrayList<String> data; public ProgressThread(Handler handler, ArrayList<String> data) { this.handler = handler; this.data = data; } @Override public void run() { for (int i=0; i<8; i++) { data.add("ListItem"); //后台数据处理 try { Thread.sleep(100); }catch(InterruptedException e) { Message msg = handler.obtainMessage(); Bundle b = new Bundle(); b.putInt("state", STATE_ERROR); msg.setData(b); handler.sendMessage(msg); } } Message msg = handler.obtainMessage(); Bundle b = new Bundle(); b.putInt("state", STATE_FINISH); msg.setData(b); handler.sendMessage(msg); } } // 此处甚至可以不需要设置Looper,因为Handler默认就使用当前线程的Looper private final Handler handler = new Handler(Looper.getMainLooper()) { public void handleMessage(Message msg) { // 处理Message,更新ListView int state = msg.getData().getInt("state"); switch(state){ case STATE_FINISH: dismissDialog(PROGRESS_DIALOG); Toast.makeText(getApplicationContext(), "加载完成!", Toast.LENGTH_LONG) .show(); adapter = new ArrayAdapter<String>(getApplicationContext(), android.R.layout.simple_list_item_1, data ); setListAdapter(adapter); break; case STATE_ERROR: dismissDialog(PROGRESS_DIALOG); Toast.makeText(getApplicationContext(), "处理过程发生错误!", Toast.LENGTH_LONG) .show(); adapter = new ArrayAdapter<String>(getApplicationContext(), android.R.layout.simple_list_item_1, data ); setListAdapter(adapter); break; default: } } }; private ArrayAdapter<String> adapter; private ArrayList<String> data; private static final int PROGRESS_DIALOG = 1; private static final int STATE_FINISH = 1; private static final int STATE_ERROR = -1; }
这个例子,我自己写完后觉得还是有点乱,要稍微整理才能看明白线程间交互的过程以及数据的前后变化。随后了解到AsyncTask类,相应修改后就很容易明白了!
2.3 AsyncTask AsyncTask版: ((Button) findViewById(R.id.load_AsyncTask)).setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View view) { data = null; data = new ArrayList<String>(); adapter = null; //显示ProgressDialog放到AsyncTask.onPreExecute()里 //showDialog(PROGRESS_DIALOG); new ProgressTask().execute(data); } }); private class ProgressTask extends AsyncTask<ArrayList<String>, Void, Integer> { /* 该方法将在执行实际的后台操作前被UI thread调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条。*/ @Override protected void onPreExecute() { // 先显示ProgressDialog showDialog(PROGRESS_DIALOG); } /* 执行那些很耗时的后台计算工作。可以调用publishProgress方法来更新实时的任务进度。 */ @Override protected Integer doInBackground(ArrayList<String>... datas) { ArrayList<String> data = datas[0]; for (int i=0; i<8; i++) { data.add("ListItem"); } return STATE_FINISH; } /* 在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用, * 后台的计算结果将通过该方法传递到UI thread. */ @Override protected void onPostExecute(Integer result) { int state = result.intValue(); switch(state){ case STATE_FINISH: dismissDialog(PROGRESS_DIALOG); Toast.makeText(getApplicationContext(), "加载完成!", Toast.LENGTH_LONG) .show(); adapter = new ArrayAdapter<String>(getApplicationContext(), android.R.layout.simple_list_item_1, data ); setListAdapter(adapter); break; case STATE_ERROR: dismissDialog(PROGRESS_DIALOG); Toast.makeText(getApplicationContext(), "处理过程发生错误!", Toast.LENGTH_LONG) .show(); adapter = new ArrayAdapter<String>(getApplicationContext(), android.R.layout.simple_list_item_1, data ); setListAdapter(adapter); break; default: } }