Android編程中的消息機(jī)制實(shí)例詳解
本文實(shí)例講述了Android編程中的消息機(jī)制。分享給大家供大家參考,具體如下:
在分析Android消息機(jī)制之前,我們先來(lái)看一段代碼:
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(); } //工作線(xiàn)程 private class WorkThread extends Thread { @Override public void run() { //......處理比較耗時(shí)的操作 //處理完成后改變狀態(tài) stateText.setText("completed"); } } }
這段代碼似乎看上去很正常,但是當(dāng)你運(yùn)行時(shí)就會(huì)發(fā)現(xiàn),它會(huì)報(bào)一個(gè)致命性的異常:
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系統(tǒng)中的視圖組件并不是線(xiàn)程安全的,如果要更新視圖,必須在主線(xiàn)程中更新,不可以在子線(xiàn)程中執(zhí)行更新的操作。
既然這樣,我們就在子線(xiàn)程中通知主線(xiàn)程,讓主線(xiàn)程做更新操作吧。那么,我們?nèi)绾瓮ㄖ骶€(xiàn)程呢?我們需要使用到Handler對(duì)象。
我們稍微修改一下上面的代碼:
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(); } //工作線(xiàn)程 private class WorkThread extends Thread { @Override public void run() { //......處理比較耗時(shí)的操作 //處理完成后給handler發(fā)送消息 Message msg = new Message(); msg.what = COMPLETED; handler.sendMessage(msg); } } }
通過(guò)上面這種方式,我們就可以解決線(xiàn)程安全的問(wèn)題,把復(fù)雜的任務(wù)處理工作交給子線(xiàn)程去完成,然后子線(xiàn)程通過(guò)handler對(duì)象告知主線(xiàn)程,由主線(xiàn)程更新視圖,這個(gè)過(guò)程中消息機(jī)制起著重要的作用。
下面,我們就來(lái)分析一下Android中的消息機(jī)制。
熟悉Windows編程的朋友知道Windows程序是消息驅(qū)動(dòng)的,并且有全局的消息循環(huán)系統(tǒng)。Google參考了Windows的消息循環(huán)機(jī)制,也在Android系統(tǒng)中實(shí)現(xiàn)了消息循環(huán)機(jī)制。Android通過(guò)Looper、Handler來(lái)實(shí)現(xiàn)消息循環(huán)機(jī)制。Android的消息循環(huán)是針對(duì)線(xiàn)程的,每個(gè)線(xiàn)程都可以有自己的消息隊(duì)列和消息循環(huán)。
Android系統(tǒng)中的Looper負(fù)責(zé)管理線(xiàn)程的消息隊(duì)列和消息循環(huán)。通過(guò)Looper.myLooper()得到當(dāng)前線(xiàn)程的Looper對(duì)象,通過(guò)Looper.getMainLooper()得到當(dāng)前進(jìn)程的主線(xiàn)程的Looper對(duì)象。
前面提到,Android的消息隊(duì)列和消息循環(huán)都是針對(duì)具體線(xiàn)程的,一個(gè)線(xiàn)程可以存在一個(gè)消息隊(duì)列和消息循環(huán),特定線(xiàn)程的消息只能分發(fā)給本線(xiàn)程,不能跨線(xiàn)程和跨進(jìn)程通訊。但是創(chuàng)建的工作線(xiàn)程默認(rèn)是沒(méi)有消息隊(duì)列和消息循環(huán)的,如果想讓工作線(xiàn)程具有消息隊(duì)列和消息循環(huán),就需要在線(xiàn)程中先調(diào)用Looper.prepare()來(lái)創(chuàng)建消息隊(duì)列,然后調(diào)用Looper.loop()進(jìn)入消息循環(huán)。下面是我們創(chuàng)建的工作線(xiàn)程:
class WorkThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // 處理收到的消息 } }; Looper.loop(); } }
這樣一來(lái),我們創(chuàng)建的工作線(xiàn)程就具有了消息處理機(jī)制了。
那么,為什么前邊的示例中,我們?cè)趺礇](méi)有看到Looper.prepare()和Looper.loop()的調(diào)用呢?原因在于,我們的Activity是一個(gè)UI線(xiàn)程,運(yùn)行在主線(xiàn)程中,Android系統(tǒng)會(huì)在Activity啟動(dòng)時(shí)為其創(chuàng)建一個(gè)消息隊(duì)列和消息循環(huán)。
前面提到最多的是消息隊(duì)列(MessageQueue)和消息循環(huán)(Looper),但是我們看到每個(gè)消息處理的地方都有Handler的存在,它是做什么的呢?Handler的作用是把消息加入特定的Looper所管理的消息隊(duì)列中,并分發(fā)和處理該消息隊(duì)列中的消息。構(gòu)造Handler的時(shí)候可以指定一個(gè)Looper對(duì)象,如果不指定則利用當(dāng)前線(xiàn)程的Looper對(duì)象創(chuàng)建。下面是Handler的兩個(gè)構(gòu)造方法:
/** * 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; }
下面是消息機(jī)制中幾個(gè)重要成員的關(guān)系圖:
一個(gè)Activity中可以創(chuàng)建出多個(gè)工作線(xiàn)程,如果這些線(xiàn)程把他們消息放入Activity主線(xiàn)程的消息隊(duì)列中,那么消息就會(huì)在主線(xiàn)程中處理了。因?yàn)橹骶€(xiàn)程一般負(fù)責(zé)視圖組件的更新操作,對(duì)于不是線(xiàn)程安全的視圖組件來(lái)說(shuō),這種方式能夠很好的實(shí)現(xiàn)視圖的更新。
那么,子線(xiàn)程如何把消息放入主線(xiàn)程的消息隊(duì)列中呢?只要Handler對(duì)象以主線(xiàn)程的Looper創(chuàng)建,那么當(dāng)調(diào)用Handler的sendMessage方法,系統(tǒng)就會(huì)把消息主線(xiàn)程的消息隊(duì)列,并且將會(huì)在調(diào)用handleMessage方法時(shí)處理主線(xiàn)程消息隊(duì)列中的消息。
對(duì)于子線(xiàn)程訪(fǎng)問(wèn)主線(xiàn)程的Handler對(duì)象,你可能會(huì)問(wèn),多個(gè)子線(xiàn)程都訪(fǎng)問(wèn)主線(xiàn)程的Handler對(duì)象,發(fā)送消息和處理消息的過(guò)程中會(huì)不會(huì)出現(xiàn)數(shù)據(jù)的不一致呢?答案是Handler對(duì)象不會(huì)出現(xiàn)問(wèn)題,因?yàn)镠andler對(duì)象管理的Looper對(duì)象是線(xiàn)程安全的,不管是添加消息到消息隊(duì)列還是從消息隊(duì)列中讀取消息都是同步保護(hù)的,所以不會(huì)出現(xiàn)數(shù)據(jù)不一致現(xiàn)象。
深入理解Android消息處理機(jī)制對(duì)于應(yīng)用程序開(kāi)發(fā)非常重要,也可以讓我們對(duì)線(xiàn)程同步有更加深刻的認(rèn)識(shí),希望這篇文章可以對(duì)朋友們有所幫助。
希望本文所述對(duì)大家Android程序設(shè)計(jì)有所幫助。
- android異步消息機(jī)制 源碼層面徹底解析(1)
- 代碼分析Android消息機(jī)制
- Android異步消息機(jī)制詳解
- android線(xiàn)程消息機(jī)制之Handler詳解
- android利用消息機(jī)制獲取網(wǎng)絡(luò)圖片
- Android 消息機(jī)制詳解及實(shí)例代碼
- Android的消息機(jī)制
- Android消息機(jī)制Handler的工作過(guò)程詳解
- 深入剖析Android消息機(jī)制原理
- Android 消息機(jī)制以及handler的內(nèi)存泄露
- Android6.0 消息機(jī)制原理解析
- Android 消息機(jī)制問(wèn)題總結(jié)
- 深入淺析Android消息機(jī)制
- Android編程之消息機(jī)制實(shí)例分析
- android異步消息機(jī)制 從源碼層面解析(2)
相關(guān)文章
android 添加按(power鍵)電源鍵結(jié)束通話(huà)(掛斷電話(huà))
首先我們發(fā)現(xiàn)現(xiàn)在我們所用的android智能手機(jī)大部分都有當(dāng)你在打電話(huà)時(shí)按power鍵來(lái)掛斷電話(huà),一般都是在設(shè)置中2013-01-01Android仿淘寶商品拖動(dòng)查看詳情及標(biāo)題欄漸變功能
這篇文章主要介紹了Android仿淘寶商品拖動(dòng)查看詳情及標(biāo)題欄漸變功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09Android觸摸事件和mousedown、mouseup、click事件之間的關(guān)系
今天小編就為大家分享一篇關(guān)于Android觸摸事件和mousedown、mouseup、click事件之間的關(guān)系,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-01-01Android自定義ToolBar并實(shí)現(xiàn)沉浸式的方法
這篇文章主要給大家介紹了關(guān)于Android自定義ToolBar并實(shí)現(xiàn)沉浸式的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)各位Android開(kāi)發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05Android6.0 動(dòng)態(tài)權(quán)限機(jī)制深入講解
這篇文章主要給大家介紹了關(guān)于Android6.0 動(dòng)態(tài)權(quán)限機(jī)制的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08Android從0到完整項(xiàng)目(1)使用Android studio 創(chuàng)建項(xiàng)目詳解
本篇文章主要介紹了Android從0到完整項(xiàng)目(1)使用Android studio 創(chuàng)建項(xiàng)目詳解,具有一定的參考價(jià)值,有興趣的可以了解一下2017-07-07Android studio button 按鈕 四種綁定事件的方法【實(shí)例代碼】
這篇文章主要介紹了Android studio button 按鈕 四種綁定事件的方法,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-08-08Android技巧一之啟動(dòng)屏+新功能左右導(dǎo)航邏輯
這篇文章主要介紹了Android技巧一之啟動(dòng)屏+新功能左右導(dǎo)航邏輯的相關(guān)資料,需要的朋友可以參考下2016-01-01