Android 开发之多线程处理、Handler 详解
Android開發(fā)過程中為什么要多線程
我們創(chuàng)建的Service、Activity以及Broadcast均是一個主線程處理,這里我們可以理解為UI線程。但是在操作一些耗時操作時,比如I/O讀寫的大文件讀寫,數(shù)據(jù)庫操作以及網(wǎng)絡(luò)下載需要很長時間,為了不阻塞用戶界面,出現(xiàn)ANR的響應(yīng)提示窗口,這個時候我們可以考慮使用Thread線程來解決。
? Android中使用Thread線程會遇到哪些問題
對于從事過J2ME開發(fā)的程序員來說Thread比較簡單,直接匿名創(chuàng)建重寫run方法,調(diào)用start方法執(zhí)行即可?;蛘邚腞unnable接口繼承,但對于Android平臺來說UI控件都沒有設(shè)計(jì)成為線程安全類型,所以需要引入一些同步的機(jī)制來使其刷新,這點(diǎn)Google在設(shè)計(jì)Android時倒是參考了下Win32的消息處理機(jī)制。
postInvalidate()方法
對于線程中的刷新一個View為基類的界面,可以使用postInvalidate()方法在線程中來處理,其中還提供了一些重寫方法比如postInvalidate(int left,int top,int right,int bottom)?來刷新一個矩形區(qū)域,以及延時執(zhí)行,比如postInvalidateDelayed(long delayMilliseconds)或postInvalidateDelayed(long delayMilliseconds,int left,int top,int right,int bottom)?方法,其中第一個參數(shù)為毫秒,如下:
| void | postInvalidate() |
| void | postInvalidate(int left, int top, int right, int bottom) |
| void | postInvalidateDelayed(long delayMilliseconds) |
| void | postInvalidateDelayed(long delayMilliseconds, int left, int top, int right, int bottom) |
Handler
當(dāng)然推薦的方法是通過一個Handler來處理這些,可以在一個線程的run方法中調(diào)用handler對象的postMessage或sendMessage方法來實(shí)現(xiàn),Android程序內(nèi)部維護(hù)著一個消息隊(duì)列,會輪訓(xùn)處理這些,如果你是Win32程序員可以很好理解這些消息處理,不過相對于Android來說沒有提供PreTranslateMessage這些干涉內(nèi)部的方法。
消息的處理者,handler負(fù)責(zé)將需要傳遞的信息封裝成Message,通過調(diào)用handler對象的obtainMessage()來實(shí)現(xiàn)。將消息傳遞給Looper,這是通過handler對象的sendMessage()來實(shí)現(xiàn)的。繼而由Looper將Message放入MessageQueue中。當(dāng)Looper對象看到MessageQueue中含有Message,就將其廣播出去。該handler對象收到該消息后,調(diào)用相應(yīng)的handler對象的handleMessage()方法對其進(jìn)行處理。
Handler主要接受子線程發(fā)送的數(shù)據(jù),并用此數(shù)據(jù)配合主線程更新UI.
??????當(dāng)應(yīng)用程序啟動時,Android首先會開啟一個主線程?(也就是UI線程) ,?主線程為管理界面中的UI控件,進(jìn)行事件分發(fā),比如說,你要是點(diǎn)擊一個?Button ,Android會分發(fā)事件到Button上,來響應(yīng)你的操作。??如果此時需要一個耗時的操作,例如:聯(lián)網(wǎng)讀取數(shù)據(jù),?或者讀取本地較大的一個文件的時候,你不能把這些操作放在主線程中,,如果你放在主線程中的話,界面會出現(xiàn)假死現(xiàn)象,如果5秒鐘還沒有完成的話,,會收到Android系統(tǒng)的一個錯誤提示? "強(qiáng)制關(guān)閉".??這個時候我們需要把這些耗時的操作,放在一個子線程中,因?yàn)樽泳€程涉及到UI更新,,Android主線程是線程不安全的,也就是說,更新UI只能在主線程中更新,子線程中操作是危險的.這個時候,Handler就出現(xiàn)了,來解決這個復(fù)雜的問題,?由于Handler運(yùn)行在主線程中(UI線程中),??它與子線程可以通過Message對象來傳遞數(shù)據(jù),這個時候,Handler就承擔(dān)著接受子線程傳過來的(子線程用sedMessage()方法傳弟)Message對象,(里面包含數(shù)據(jù))? ,把這些消息放入主線程隊(duì)列中,配合主線程進(jìn)行更新UI。
Handler一些特點(diǎn):handler可以分發(fā)Message對象和Runnable對象到主線程中,每個Handler實(shí)例,都會綁定到創(chuàng)建他的線程中(一般是位于主線程),
??????它有兩個作用: (1)安排消息或Runnable在某個主線程中某個地方執(zhí)行
?????????????????????????? (2)安排一個動作在不同的線程中執(zhí)行
??????? Handler中分發(fā)消息的一些方法
??????? post(Runnable)
??????? postAtTime(Runnable,long)
??????? postDelayed(Runnable long)
??????? sendEmptyMessage(int)
??????? sendMessage(Message)
??????? sendMessageAtTime(Message,long)
??????? sendMessageDelayed(Message,long)
??????以上post類方法允許你排列一個Runnable對象到主線程隊(duì)列中,sendMessage類方法,允許你安排一個帶數(shù)據(jù)的Message對象到隊(duì)列中,等待更新.
Handler實(shí)例
????//?子類需要繼承Hendler類,并重寫handleMessage(Message msg)?方法,用于接受線程數(shù)據(jù)
???? //?以下為一個實(shí)例,它實(shí)現(xiàn)的功能為?:通過線程修改界面Button的內(nèi)容
public class MyHandlerActivity extends Activity {
??? Button button;
??? MyHandler myHandler;
??? protected void onCreate(Bundle savedInstanceState) {
??????? super.onCreate(savedInstanceState);
??????? setContentview(R.layout.handlertest);
??????? button = (Button) findViewById(R.id.button);
??????? myHandler = new MyHandler();
??????? //當(dāng)創(chuàng)建一個新的Handler實(shí)例時,它會綁定到當(dāng)前線程和消息的隊(duì)列中,開始分發(fā)數(shù)據(jù)
??????? // Handler有兩個作用, (1) :定時執(zhí)行Message和Runnalbe對象
??????? // (2):讓一個動作,在不同的線程中執(zhí)行.
??????? //它安排消息,用以下方法
??????? // post(Runnable)
??????? // postAtTime(Runnable,long)
??????? // postDelayed(Runnable,long)
??????? // sendEmptyMessage(int)
??????? // sendMessage(Message);
??????? // sendMessageAtTime(Message,long)
??????? // sendMessageDelayed(Message,long)
??????? //以上方法以?post開頭的允許你處理Runnable對象
??????? //sendMessage()允許你處理Message對象(Message里可以包含數(shù)據(jù),)
??????? MyThread m = new MyThread();
??????? new Thread(m).start();
??? }
??? /**
???? *接受消息,處理消息?,此Handler會與當(dāng)前主線程一塊運(yùn)行
???? * */
??? class MyHandler extends Handler {
??????? public MyHandler() {
??????? }
??????? public MyHandler(Looper L) {
??????????? super(L);
??????? }
??????? //子類必須重寫此方法,接受數(shù)據(jù)
??????? @Override
??????? public void handleMessage(Message msg) {
??????????? // TODO Auto-generated method stub
??????????? Log.d("MyHandler", "handleMessage......");
??????????? super.handleMessage(msg);
??????????? //此處可以更新UI
??????????? Bundle b = msg.getData();
??????????? String color = b.getString("color");
??????????? MyHandlerActivity.this.button.append(color);
??????? }
??? }
??? class MyThread implements Runnable {
??????? public void run() {
??????????? try {
??????????????? Thread.sleep(10000);
??????????? } catch (InterruptedException e) {
??????????????? // TODO Auto-generated catch block
??????????????? e.printStackTrace();
??????????? }
??????????? Log.d("thread.......", "mThread........");
??????????? Message msg = new Message();
??????????? Bundle b = new Bundle();//存放數(shù)據(jù)
??????????? b.putString("color", "我的");
??????????? msg.setData(b);
??????????? MyHandlerActivity.this.myHandler.sendMessage(msg); //向Handler發(fā)送消息,更新UI
??????? }
??? }
}
? Looper
其實(shí)Android中每一個Thread都跟著一個Looper,Looper可以幫助Thread維護(hù)一個消息隊(duì)列,昨天的問題?Can't create handler inside thread?錯誤?一文中提到這一概念,但是Looper和Handler沒有什么關(guān)系,我們從開源的代碼可以看到Android還提供了一個Thread繼承類HanderThread可以幫助我們處理,在HandlerThread對象中可以通過getLooper方法獲取一個Looper對象控制句柄,我們可以將其這個Looper對象映射到一個Handler中去來實(shí)現(xiàn)一個線程同步機(jī)制,Looper對象的執(zhí)行需要初始化Looper.prepare方法就是昨天我們看到的問題,同時推出時還要釋放資源,使用Looper.release方法。
Looper是MessageQueue的管理者。每一個MessageQueue都不能脫離Looper而存在,Looper對象的創(chuàng)建是通過prepare函數(shù)來實(shí)現(xiàn)的。同時每一個Looper對象和一個線程關(guān)聯(lián)。通過調(diào)用Looper.myLooper()可以獲得當(dāng)前線程的Looper對象?
創(chuàng)建一個Looper對象時,會同時創(chuàng)建一個MessageQueue對象。除了主線程有默認(rèn)的Looper,其他線程默認(rèn)是沒有MessageQueue對象的,所以,不能接受Message。如需要接受,自己定義一個Looper對象(通過prepare函數(shù)),這樣該線程就有了自己的Looper對象和MessageQueue數(shù)據(jù)結(jié)構(gòu)了。?
Looper從MessageQueue中取出Message然后,交由Handler的handleMessage進(jìn)行處理。處理完成后,調(diào)用Message.recycle()將其放入Message Pool中。
Message
對于Android中Handler可以傳遞一些內(nèi)容,通過Bundle對象可以封裝String、Integer以及Blob二進(jìn)制對象,我們通過在線程中使用Handler對象的??? sendEmptyMessage或sendMessage方法來傳遞一個Bundle對象到Handler處理器。對于Handler類提供了重寫方法handleMessage(Message msg)?來判斷,通過msg.what來區(qū)分每條信息。將Bundle解包來實(shí)現(xiàn)Handler類更新UI線程中的內(nèi)容實(shí)現(xiàn)控件的刷新操作。相關(guān)的Handler對象有關(guān)消息發(fā)送sendXXXX相關(guān)方法如下,同時還有postXXXX相關(guān)方法,這些和Win32中的道理基本一致,一個為發(fā)送后直接返回,一個為處理后才返回。
Message:消息對象,Message Queue中的存放的對象。一個Message Queue中包含多個Message。?Message實(shí)例對象的取得,通常使用Message類里的靜態(tài)方法obtain(),該方法有多個重載版本可供選擇;它的創(chuàng)建并不一定是直接創(chuàng)建一個新的實(shí)例,而是先從Message Pool(消息池)中看有沒有可用的Message實(shí)例,存在則直接取出返回這個實(shí)例。如果Message Pool中沒有可用的Message實(shí)例,則才用給定的參數(shù)創(chuàng)建一個Message對象。調(diào)用removeMessages()時,將Message從Message Queue中刪除,同時放入到Message Pool中。除了上面這種方式,也可以通過Handler對象的obtainMessage()獲取一個Message實(shí)例。
| final boolean | sendEmptyMessage(int what) |
| final boolean | sendEmptyMessageAtTime(int what, long uptimeMillis) |
| final boolean | sendEmptyMessageDelayed(int what, long delayMillis) |
| final boolean | sendMessage(Message?msg) |
| final boolean | sendMessageAtFrontOfQueue(Message?msg) |
| boolean | sendMessageAtTime(Message?msg, long uptimeMillis) |
| final boolean | sendMessageDelayed(Message?msg, long delayMillis) |
MessageQueue
是一種數(shù)據(jù)結(jié)構(gòu),見名知義,就是一個消息隊(duì)列,存放消息的地方。每一個線程最多只可以擁有一個MessageQueue數(shù)據(jù)結(jié)構(gòu)。?
創(chuàng)建一個線程的時候,并不會自動創(chuàng)建其MessageQueue。通常使用一個Looper對象對該線程的MessageQueue進(jìn)行管理。主線程創(chuàng)建時,會創(chuàng)建一個默認(rèn)的Looper對象,而Looper對象的創(chuàng)建,將自動創(chuàng)建一個Message Queue。其他非主線程,不會自動創(chuàng)建Looper,要需要的時候,通過調(diào)用prepare函數(shù)來實(shí)現(xiàn)。?
java.util.concurrent對象分析
對于過去從事Java開發(fā)的程序員不會對Concurrent對象感到陌生吧,他是JDK 1.5以后新增的重要特性作為掌上設(shè)備,我們不提倡使用該類,考慮到Android為我們已經(jīng)設(shè)計(jì)好的Task機(jī)制,我們這里Android開發(fā)網(wǎng)對其不做過多的贅述。
Task以及AsyncTask
在Android中還提供了一種有別于線程的處理方式,就是Task以及AsyncTask,從開源代碼中可以看到是針對Concurrent的封裝,開發(fā)人員可以方便的處理這些異步任務(wù)。?當(dāng)然涉及到同步機(jī)制的方法和技巧還有很多,考慮時間和篇幅問題不再做過多的描述。
總結(jié)
以上是生活随笔為你收集整理的Android 开发之多线程处理、Handler 详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java.lang.IndexOutOf
- 下一篇: mstsc /console 远程命令