Android的UI操作不是線程安全的(出于提高性能考慮,避免實現(xiàn)多線程同步等機制所引入的延時),若多個線程同時對UI元素進行操作,可能導致線程安全問題。因此,Android中做了嚴格的規(guī)定:只有UI主線程才能對UI進行設(shè)置與操作。
網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)!專注于網(wǎng)頁設(shè)計、網(wǎng)站建設(shè)、微信開發(fā)、小程序制作、集團企業(yè)網(wǎng)站建設(shè)等服務(wù)項目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了競秀免費建站歡迎大家使用!
在實際編程中,為了避免UI界面長時間得不到響應(yīng)而導致的ANR(Application Not Responding)異常,通常將網(wǎng)絡(luò)訪問、復雜運算等一些耗時的操作被放在子線程中執(zhí)行。這就需要子線程在運行完畢后將結(jié)果返回到主線程并通過UI進行顯示。在Android中,是通過Handler+Loop+MessageQueue實現(xiàn)線程間通信的。
先看兩個實例:
實例1:模擬通過網(wǎng)絡(luò)下載數(shù)據(jù)并返回UI顯示。
操作過程為:1.UI線程獲得用戶請求。2.啟動子線程完成網(wǎng)絡(luò)數(shù)據(jù)下載(網(wǎng)絡(luò)下載過程通過強制子線程休眠若干秒來模擬)。3.子線程將下載的數(shù)據(jù)返回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; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.fragment_main); mButton = (Button) findViewById(R.id.btReqNet); mTextView = (TextView) findViewById(R.id.tvDownload); //設(shè)置按鈕的點擊事件監(jiān)聽器 mButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { showProgressDialog("","正在下載..."); //啟動子線程進行網(wǎng)絡(luò)訪問模擬 mNetAccessThread = new ChildTread(); mNetAccessThread.start(); } }); //繼承Handler類并覆蓋其handleMessage方法 mHandler = new Handler(){ //覆蓋Handler類的handleMessage方法 //接收子線程傳遞的數(shù)據(jù)并在UI顯示 @Override public void handleMessage(Message msg) { switch (msg.what) { case 1: mTextView.setText((String) msg.obj); dismissProgressDialog(); break; //可以添加其他情況,如網(wǎng)絡(luò)傳輸錯誤 //case... default: break; } } }; } class ChildTread extends Thread { @Override public void run() { //休眠6秒,模擬網(wǎng)絡(luò)訪問延遲 try { Thread.sleep(6000); } catch (InterruptedException e) { e.printStackTrace(); } //將結(jié)果通過消息返回主線程 Message msg = new Message(); msg.what = 1; mDownloadCount ++; msg.obj = new String("第"+mDownloadCount+"次從網(wǎng)上下載的數(shù)據(jù)"); 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(); } /** * 關(guān)閉progressDialog. * */ protected void dismissProgressDialog() { if(mProgressDialog != null) { mProgressDialog.dismiss(); } } }
程序運行效果:
點擊下載按鈕,UI線程通過handler.sendMessage()向子線程發(fā)送消息,子線程收到消息后啟動數(shù)據(jù)下載(通過休眠線程模擬)。
下載完畢,子線程將下載數(shù)據(jù)返回主線程并顯示。
實例2:模擬子線程向主線程發(fā)送消息。
操作過程為:1.UI線程獲得用戶輸入的消息內(nèi)容。2.通過Handler將消息發(fā)送給子線程。3.子線程獲得消息并通過Toast將內(nèi)容打印。
主要代碼如下:
public class MainActivity extends ActionBarActivity { private EditText mEditText; private Button mButton; private Handler mHandler; private Thread mChildTread; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.fragment_main); mEditText = (EditText) findViewById(R.id.etEditText); mButton = (Button) findViewById(R.id.btButton); // 設(shè)置按鈕的點擊事件監(jiān)聽器 mButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Message msg = new Message(); msg.what = 1; msg.obj = mEditText.getText(); //將消息發(fā)送到子線程 mHandler.sendMessage(msg); mEditText.setText(""); } }); //啟動子線程進行msg接收 mChildTread = new ChildTread(); mChildTread.start(); } /** * 子線程為內(nèi)部類,可以直接訪問其外部類的mHandler變量 * */ class ChildTread extends Thread { @Override public void run() { //以下三步是handler looper機制工作的固定模式 Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here switch (msg.what) { case 1: //子線程無權(quán)操作UI,只能通過Toast.makeText將收到的消息顯示 String st = msg.obj.toString(); if (st == null || st.equals("")) st = "收到的消息內(nèi)容為空"; else st = "收到來自主線程的消息:" + st; Toast.makeText(MainActivity.this, st, 6000).show(); break; //可以添加其他情況,如傳輸錯誤 //case... default: break; } } }; Looper.loop(); } }; } 程序運行效果:
小結(jié):Android通過Handler+Looper+MessageQueue機制實現(xiàn)線程間的通信,本文通過兩個簡單的實例分別基于該機制實現(xiàn)了UI線程到子線程和子線程到UI線程的消息傳遞。下一篇博文將會對Handler Looper機制的原理進行深入研究。