在分析Android消息機制以前,咱們先來看一段代碼: android
01 |
public class MainActivity extends Activity implements View.OnClickListener { |
03 |
private TextView stateText; |
07 |
public void onCreate(Bundle savedInstanceState) { |
08 |
super.onCreate(savedInstanceState); |
09 |
setContentView(R.layout.main); |
10 |
stateText = (TextView) findViewById(R.id.tv); |
11 |
btn = (Button) findViewById(R.id.btn); |
13 |
btn.setOnClickListener(this); |
17 |
public void onClick(View v) { |
18 |
new WorkThread().start(); |
22 |
private class WorkThread extends Thread { |
28 |
stateText.setText("completed"); |
這段代碼彷佛看上去很正常,可是當你運行時就會發現,它會報一個致命性的異常: 編程
1 |
ERROR/AndroidRuntime(421): FATAL EXCEPTION: Thread-8 |
2 |
ERROR/AndroidRuntime(421): android.view.ViewRoot$CalledFromWrongThreadException: |
3 |
Only the original thread that created a view hierarchy can touch its views. |
究竟是怎麼回事呢?緣由在於,Android系統中的視圖組件並非線程安全的,若是要更新視圖,必須在主線程中更新,不能夠在子線程中執行更新的操做。 安全
既然這樣,咱們就在子線程中通知主線程,讓主線程作更新操做吧。那麼,咱們如何通知主線程呢?咱們須要使用到Handler對象。 ide
咱們稍微修改一下上面的代碼: oop
01 |
public class MainActivity extends Activity implements View.OnClickListener { |
03 |
private static final int COMPLETED = 0; |
05 |
private TextView stateText; |
08 |
private Handler handler = new Handler() { |
10 |
public void handleMessage(Message msg) { |
11 |
if (msg.what == COMPLETED) { |
12 |
stateText.setText("completed"); |
18 |
public void onCreate(Bundle savedInstanceState) { |
19 |
super.onCreate(savedInstanceState); |
20 |
setContentView(R.layout.main); |
21 |
stateText = (TextView) findViewById(R.id.tv); |
22 |
btn = (Button) findViewById(R.id.btn); |
24 |
btn.setOnClickListener(this); |
28 |
public void onClick(View v) { |
29 |
new WorkThread().start(); |
33 |
private class WorkThread extends Thread { |
39 |
Message msg = new Message(); |
41 |
handler.sendMessage(msg); |
經過上面這種方式,咱們就能夠解決線程安全的問題,把複雜的任務處理工做交給子線程去完成,而後子線程經過handler對象告知主線程,由主線程更新視圖,這個過程當中消息機制起着重要的做用。 this
下面,咱們就來分析一下Android中的消息機制。 spa
熟悉Windows編程的朋友知道Windows程序是消息驅動的,而且有全局的消息循環系統。Google參考了Windows的消息循環機制, 也在Android系統中實現了消息循環機制。Android經過Looper、Handler來實現消息循環機制。Android的消息循環是針對線程 的,每一個線程均可以有本身的消息隊列和消息循環。 .net
Android系統中的Looper負責管理線程的消息隊列和消息循環。經過Looper.myLooper()獲得當前線程的Looper對象,經過Looper.getMainLooper()獲得當前進程的主線程的Looper對象。 線程
前面提到,Android的消息隊列和消息循環都是針對具體線程的,一個線程能夠存在一個消息隊列和消息循環,特定線程的消息只能分發給本線程,不 能跨線程和跨進程通信。可是建立的工做線程默認是沒有消息隊列和消息循環的,若是想讓工做線程具備消息隊列和消息循環,就須要在線程中先調用 Looper.prepare()來建立消息隊列,而後調用Looper.loop()進入消息循環。下面是咱們建立的工做線程: 對象
01 |
class WorkThread extends Thread { |
02 |
public Handler mHandler; |
07 |
mHandler = new Handler() { |
08 |
public void handleMessage(Message msg) { |
這樣一來,咱們建立的工做線程就具備了消息處理機制了。
那麼,爲何前邊的示例中,咱們怎麼沒有看到Looper.prepare()和Looper.loop()的調用呢?緣由在於,咱們的Activity是一個UI線程,運行在主線程中,Android系統會在Activity啓動時爲其建立一個消息隊列和消息循環。
前面提到最多的是消息隊列(MessageQueue)和消息循環(Looper),可是咱們看到每一個消息處理的地方都有Handler的存在,它 是作什麼的呢?Handler的做用是把消息加入特定的Looper所管理的消息隊列中,並分發和處理該消息隊列中的消息。構造Handler的時候能夠 指定一個Looper對象,若是不指定則利用當前線程的Looper對象建立。下面是Handler的兩個構造方法:
02 |
* Default constructor associates this handler with the queue for the |
05 |
* If there isn't one, this handler won't be able to receive messages. |
08 |
if (FIND_POTENTIAL_LEAKS) { |
09 |
final Class<? extends Handler> klass = getClass(); |
10 |
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && |
11 |
(klass.getModifiers() & Modifier.STATIC) == 0) { |
12 |
Log.w(TAG, "The following Handler class should be static or leaks might occur: " + |
13 |
klass.getCanonicalName()); |
17 |
mLooper = Looper.myLooper(); |
18 |
if (mLooper == null) { |
19 |
throw new RuntimeException( |
20 |
"Can't create handler inside thread that has not called Looper.prepare()"); |
22 |
mQueue = mLooper.mQueue; |
27 |
* Use the provided queue instead of the default one. |
29 |
public Handler(Looper looper) { |
31 |
mQueue = looper.mQueue; |
下面是消息機制中幾個重要成員的關係圖:
一個Activity中能夠建立出多個工做線程,若是這些線程把他們消息放入Activity主線程的消息隊列中,那麼消息就會在主線程中處理了。由於主線程通常負責視圖組件的更新操做,對於不是線程安全的視圖組件來講,這種方式可以很好的實現視圖的更新。
那麼,子線程如何把消息放入主線程的消息隊列中呢?只要Handler對象以主線程的Looper建立,那麼當調用Handler的 sendMessage方法,系統就會把消息主線程的消息隊列,而且將會在調用handleMessage方法時處理主線程消息隊列中的消息。
對於子線程訪問主線程的Handler對象,你可能會問,多個子線程都訪問主線程的Handler對象,發送消息和處理消息的過程當中會不會出現數據 的不一致呢?答案是Handler對象不會出現問題,由於Handler對象管理的Looper對象是線程安全的,不論是添加消息到消息隊列仍是從消息隊 列中讀取消息都是同步保護的,因此不會出現數據不一致現象。
深刻理解Android消息處理機制對於應用程序開發很是重要,也可讓咱們對線程同步有更加深入的認識,但願這篇文章能夠對朋友們有所幫助。