編輯:關於Android編程
本文實例講述了Android編程中的消息機制。分享給大家供大家參考,具體如下:
在分析Android消息機制之前,我們先來看一段代碼:
public class MainActivity extends Activity implements View.OnClickListener { private TextView stateText; private Button btn; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); stateText = (TextView) findViewById(R.id.tv); btn = (Button) findViewById(R.id.btn); btn.setOnClickListener(this); } @Override public void onClick(View v) { new WorkThread().start(); } //工作線程 private class WorkThread extends Thread { @Override public void run() { //......處理比較耗時的操作 //處理完成後改變狀態 stateText.setText("completed"); } } }
這段代碼似乎看上去很正常,但是當你運行時就會發現,它會報一個致命性的異常:
ERROR/AndroidRuntime(421): FATAL EXCEPTION: Thread-8
ERROR/AndroidRuntime(421): android.view.ViewRoot$CalledFromWrongThreadException:
Only the original thread that created a view hierarchy can touch its views.
到底是怎麼回事呢?原因在於,Android系統中的視圖組件並不是線程安全的,如果要更新視圖,必須在主線程中更新,不可以在子線程中執行更新的操作。
既然這樣,我們就在子線程中通知主線程,讓主線程做更新操作吧。那麼,我們如何通知主線程呢?我們需要使用到Handler對象。
我們稍微修改一下上面的代碼:
public class MainActivity extends Activity implements View.OnClickListener { private static final int COMPLETED = 0; private TextView stateText; private Button btn; private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == COMPLETED) { stateText.setText("completed"); } } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); stateText = (TextView) findViewById(R.id.tv); btn = (Button) findViewById(R.id.btn); btn.setOnClickListener(this); } @Override public void onClick(View v) { new WorkThread().start(); } //工作線程 private class WorkThread extends Thread { @Override public void run() { //......處理比較耗時的操作 //處理完成後給handler發送消息 Message msg = new Message(); msg.what = COMPLETED; handler.sendMessage(msg); } } }
通過上面這種方式,我們就可以解決線程安全的問題,把復雜的任務處理工作交給子線程去完成,然後子線程通過handler對象告知主線程,由主線程更新視圖,這個過程中消息機制起著重要的作用。
下面,我們就來分析一下Android中的消息機制。
熟悉Windows編程的朋友知道Windows程序是消息驅動的,並且有全局的消息循環系統。Google參考了Windows的消息循環機制,也在Android系統中實現了消息循環機制。Android通過Looper、Handler來實現消息循環機制。Android的消息循環是針對線程的,每個線程都可以有自己的消息隊列和消息循環。
Android系統中的Looper負責管理線程的消息隊列和消息循環。通過Looper.myLooper()得到當前線程的Looper對象,通過Looper.getMainLooper()得到當前進程的主線程的Looper對象。
前面提到,Android的消息隊列和消息循環都是針對具體線程的,一個線程可以存在一個消息隊列和消息循環,特定線程的消息只能分發給本線程,不能跨線程和跨進程通訊。但是創建的工作線程默認是沒有消息隊列和消息循環的,如果想讓工作線程具有消息隊列和消息循環,就需要在線程中先調用Looper.prepare()來創建消息隊列,然後調用Looper.loop()進入消息循環。下面是我們創建的工作線程:
class WorkThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // 處理收到的消息 } }; Looper.loop(); } }
這樣一來,我們創建的工作線程就具有了消息處理機制了。
那麼,為什麼前邊的示例中,我們怎麼沒有看到Looper.prepare()和Looper.loop()的調用呢?原因在於,我們的Activity是一個UI線程,運行在主線程中,Android系統會在Activity啟動時為其創建一個消息隊列和消息循環。
前面提到最多的是消息隊列(MessageQueue)和消息循環(Looper),但是我們看到每個消息處理的地方都有Handler的存在,它是做什麼的呢?Handler的作用是把消息加入特定的Looper所管理的消息隊列中,並分發和處理該消息隊列中的消息。構造Handler的時候可以指定一個Looper對象,如果不指定則利用當前線程的Looper對象創建。下面是Handler的兩個構造方法:
/** * Default constructor associates this handler with the queue for the * current thread. * * If there isn't one, this handler won't be able to receive messages. */ public Handler() { if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = null; } /** * Use the provided queue instead of the default one. */ public Handler(Looper looper) { mLooper = looper; mQueue = looper.mQueue; mCallback = null; }
下面是消息機制中幾個重要成員的關系圖:
一個Activity中可以創建出多個工作線程,如果這些線程把他們消息放入Activity主線程的消息隊列中,那麼消息就會在主線程中處理了。因為主線程一般負責視圖組件的更新操作,對於不是線程安全的視圖組件來說,這種方式能夠很好的實現視圖的更新。
那麼,子線程如何把消息放入主線程的消息隊列中呢?只要Handler對象以主線程的Looper創建,那麼當調用Handler的sendMessage方法,系統就會把消息主線程的消息隊列,並且將會在調用handleMessage方法時處理主線程消息隊列中的消息。
對於子線程訪問主線程的Handler對象,你可能會問,多個子線程都訪問主線程的Handler對象,發送消息和處理消息的過程中會不會出現數據的不一致呢?答案是Handler對象不會出現問題,因為Handler對象管理的Looper對象是線程安全的,不管是添加消息到消息隊列還是從消息隊列中讀取消息都是同步保護的,所以不會出現數據不一致現象。
深入理解Android消息處理機制對於應用程序開發非常重要,也可以讓我們對線程同步有更加深刻的認識,希望這篇文章可以對朋友們有所幫助。
希望本文所述對大家Android程序設計有所幫助。
手機怎麼掃描二維碼?手機掃描二維碼方法介紹一、通過微信掃描二維碼打開微信,點擊微信右上角的+號,從彈出的菜單中選擇“掃一掃”;在彈出
首先看不正常的圖,點擊tracing_dialog按鈕彈出對話框然後看理論上的效果圖觀察兩張圖發現,不正常的圖最上方被狀態欄遮擋住了,而該問題存在於android4.4版
今年剛開始學習android,不過是做修改源碼,自己在寢室捎帶學習了下做APP,由於是剛開始學習,這個游戲只注重了游戲的實現,實現了大概功能後,也沒有實現游戲的細節,像分
本文本著開源的精神介紹如何向一個Android系統中添加一個產品的整個過程,按照以下過程筆者有理由相信每個將要從事本行業的人都可以完成,其實添加一個產品並不難,難的是對其