標籤:android looper handler 線程間通訊
Android的UI操作不是安全執行緒的(出於提高效能考慮,避免實現多線程同步等機制所引入的延時),若多個線程同時對UI元素進行操作,可能導致安全執行緒問題。因此,Android中做了嚴格的規定:只有UI主線程才能對UI進行設定與操作。
在實際編程中,為了避免UI介面長時間得不到響應而導致的ANR(Application Not Responding)異常,通常將網路訪問、複雜運算等一些耗時的操作被放在子線程中執行。這就需要子線程在運行完畢後將結果返回到主線程並通過UI進行顯示。在Android中,是通過Handler+Loop+MessageQueue實現線程間通訊的。
先看兩個執行個體:
執行個體1:類比通過網路下載資料並返回UI顯示。
操作過程為:1.UI線程獲得使用者請求。2.啟動子線程完成網路資料下載(網路下載過程通過強制子線程休眠若干秒來類比)。3.子線程將下載的資料返回UI線程並顯示。
主要代碼如下:
public class MainActivity extends ActionBarActivity {private Button mButton;private TextView mTextView;private Handler mHandler;private Thread mNetAccessThread;private ProgressDialog mProgressDialog;private int mDownloadCount = 0;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.fragment_main);mButton = (Button) findViewById(R.id.btReqNet);mTextView = (TextView) findViewById(R.id.tvDownload);//設定按鈕的點擊事件監聽器mButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {showProgressDialog("","正在下載...");//啟動子線程進行網路訪問類比mNetAccessThread = new ChildTread();mNetAccessThread.start();}});//繼承Handler類並覆蓋其handleMessage方法mHandler = new Handler(){//覆蓋Handler類的handleMessage方法//接收子線程傳遞的資料並在UI顯示@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case 1:mTextView.setText((String) msg.obj);dismissProgressDialog();break;//可以添加其他情況,如網路傳輸錯誤//case...default:break;}}};}class ChildTread extends Thread {@Overridepublic void run() {//休眠6秒,類比網路訪問延遲try {Thread.sleep(6000);} catch (InterruptedException e) {e.printStackTrace();}//將結果通過訊息返回主線程Message msg = new Message();msg.what = 1;mDownloadCount ++;msg.obj = new String("第"+mDownloadCount+"次從網上下載的資料");mHandler.sendMessage(msg);}};/** * 開啟progressDialog. * * @param title 對話方塊標題. * @param content 對話方塊本文. */protected void showProgressDialog(String title,String content) {mProgressDialog = new ProgressDialog(this);if(title != null)mProgressDialog.setTitle(title);if(content != null)mProgressDialog.setMessage(content);mProgressDialog.show();}/** * 關閉progressDialog. * */protected void dismissProgressDialog() {if(mProgressDialog != null){mProgressDialog.dismiss();}}}
程式運行效果:
點擊下載按鈕,UI線程通過handler.sendMessage()向子線程發送訊息,子線程收到訊息後啟動資料下載(通過休眠線程類比)。
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M02/4C/36/wKioL1Q45jnRyNfNAAB1lcKvu-o305.jpg" title="360手機小幫手1011_15_54_01.png" style="float:none;" alt="wKioL1Q45jnRyNfNAAB1lcKvu-o305.jpg" />
下載完畢,子線程將下載資料返回主線程並顯示。
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M00/4C/37/wKioL1Q4563zuwMZAACM4fmNrKU067.jpg" title="360手機小幫手1011_15_56_02.png" alt="wKioL1Q4563zuwMZAACM4fmNrKU067.jpg" />
執行個體2:類比子線程向主線程發送訊息。
操作過程為:1.UI線程獲得使用者輸入的訊息內容。2.通過Handler將訊息發送給子線程。3.子線程獲得訊息並通過Toast將內容列印。
主要代碼如下:
public class MainActivity extends ActionBarActivity {private EditText mEditText;private Button mButton;private Handler mHandler;private Thread mChildTread;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.fragment_main);mEditText = (EditText) findViewById(R.id.etEditText);mButton = (Button) findViewById(R.id.btButton);// 設定按鈕的點擊事件監聽器mButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Message msg = new Message();msg.what = 1;msg.obj = mEditText.getText();//將訊息發送到子線程mHandler.sendMessage(msg);mEditText.setText("");}});//啟動子線程進行msg接收mChildTread = new ChildTread();mChildTread.start();}/*** 子線程為內部類,可以直接存取其外部類的mHandler變數**/class ChildTread extends Thread { @Override public void run() { //以下三步是handler looper機制工作的固定模式Looper.prepare();mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages hereswitch (msg.what) {case 1: //子線程無權操作UI,只能通過Toast.makeText將收到的訊息顯示 String st = msg.obj.toString(); if (st == null || st.equals("")) st = "收到的訊息內容為空白"; else st = "收到來自主線程的訊息:" + st; Toast.makeText(MainActivity.this, st, 6000).show(); break;//可以添加其他情況,如傳輸錯誤//case...default:break;} }};Looper.loop(); }};}程式運行效果:
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M02/4C/37/wKioL1Q46pnjjJMDAAB3ZtMgIgY620.jpg" title="360手機小幫手1011_15_54_01.png" alt="wKioL1Q46pnjjJMDAAB3ZtMgIgY620.jpg" />650) this.width=650;" src="http://s3.51cto.com/wyfs02/M01/4C/37/wKioL1Q46neDgzh5AACNfaspK8Y747.jpg" title="360手機小幫手1011_15_56_02.png" alt="wKioL1Q46neDgzh5AACNfaspK8Y747.jpg" />
小結:Android通過Handler+Looper+MessageQueue機制實現線程間的通訊,本文通過兩個簡單的執行個體分別基於該機制實現了UI線程到子線程和子線程到UI線程的訊息傳遞。下一篇博文將會對Handler Looper機制的原理進行深入研究。
本文出自 “sprocessor” 部落格,請務必保留此出處http://sprocessor.blog.51cto.com/160089/1562589
Android的Handler Looper Message機制應用執行個體與詳解(一)