Android Handler

Handler是什么?
Handler是Android提供的一套用來更新UI的機(jī)制,也是一套消息處理機(jī)制,可以通過它發(fā)送消息绪励,也可以通過它處理消息。
所有Activity生命周期回調(diào)的方法唠粥,都是通過handler機(jī)制去發(fā)送消息的疏魏,然后根據(jù)不同的消息(message)做相應(yīng)的分支處理。例如發(fā)送一個消息給 Framework晤愧,告知需要調(diào)用onCreate()或onDestory()方法大莫。實(shí)際上在 Framework 當(dāng)中,Activity的交互大部分都是用AMS(Activity Manager Service)做處理的养涮。整個應(yīng)用程序的核心的一個類就是 Activity Thread葵硕,Activity Thread就是通過handler機(jī)制接收到 Activity Manager Service發(fā)送的有關(guān)Activity生命周期的管理的消息(比如啟動)眉抬。
為什么要使用Handler贯吓?
Android在設(shè)計的時候,就封裝了一套消息的創(chuàng)建蜀变、傳遞悄谐、處理機(jī)制,如果不遵循這樣的機(jī)制库北,就沒有辦法更新UI信息爬舰,并且會拋出異常信息们陆。這樣的機(jī)制便包含Handler機(jī)制。
我們需要把這些耗時的操作情屹,放在一個子線程中坪仇,因?yàn)樽泳€程涉及到UI更新,Android主線程是線程不安全的垃你,也就是說椅文,更新UI只能在主線程中更新,子線程中操作是危險的惜颇。這個時候皆刺,Handler就出現(xiàn)了。來解決這個復(fù)雜的問題 凌摄,由于Handler運(yùn)行在主線程中(UI線程中)羡蛾,它與子線程可以通過Message對象來傳遞數(shù)據(jù), 這個時候锨亏,Handler就承擔(dān)著接受子線程傳過來的(子線程用sedMessage()方法傳遞)Message對象(里面包含數(shù)據(jù))痴怨,把這些消息放入主線程隊列中,配合主線程進(jìn)行更新UI屯伞。
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)閉"湿右。


  1. 我們要創(chuàng)建一個handler的時候诅妹,它會和默認(rèn)的一個線程進(jìn)行綁定,而這個默認(rèn)的線程當(dāng)中就有一個MessageQueue(消息隊列)。
  2. handler的兩個用途:(1)定時發(fā)送一個Messages或者Runnables對象吭狡;(2)可以在一個線程當(dāng)中去處理并執(zhí)行一個Action的動作尖殃。
  3. 主線程運(yùn)行一個消息隊列,并管理著一些頂級的應(yīng)用對象(top-level application objects)划煮,包括Activity送丰、Broadcast Receiver等等,這些對象默認(rèn)都是創(chuàng)建在Activity Thread(也就是我們常說的UI線程或者主線程)當(dāng)中弛秋。
    下面圖是通過postDelayed來更新圖片
    Handler的示例蚪战,實(shí)現(xiàn)圖片輪播。
    代碼實(shí)現(xiàn)
  4. 在主布局中定義一個ImageView控件铐懊。
  5. 在 MainActivity 中創(chuàng)建并初始化ImageView邀桑,定義一個圖片數(shù)組 images 和數(shù)組下標(biāo)索引 index,創(chuàng)建一個Handler對象科乎。
  6. 創(chuàng)建一個內(nèi)部類 MyRunnable 實(shí)現(xiàn) Runnable 接口壁畸,重寫 run() 方法:
    public void run() {
    index++;
    index = index%3; // 圖片輪播(一般是通過ViewPager實(shí)現(xiàn)圖片輪播)
    imageView.setImageResource(images[index]);
    handler.postDelayed(myRunnable,1000); // 每隔一秒換一次圖片
    }
  7. 在onCreate()方法中調(diào)用handler,也就是在主線程中調(diào)用handler:
    handler.postDelayed(myRunnable,1000);

使用“Message”方式“發(fā)送消息”茅茂,使“Handler處理消息捏萍,并更新UI”
1.定義Handler,并且實(shí)例化空闲,使用默認(rèn)構(gòu)造函數(shù)即可令杈。
2.重寫handlerMessage方法。
private Handler msgHandler=new Handler(){
//定義handler碴倾,重寫處理message方法逗噩。當(dāng)該handler發(fā)送消息的時候,這個方法會被執(zhí)行跌榔。
public void handleMessage(android.os.Message msg) {
//msg為當(dāng)有sendMessage方法調(diào)用時异雁,傳過來的Message對象。
mTextView.setText("msg:"+msg.arg1);
};
};
3.定義Message對象僧须。其中纲刀,Message提供了三個公共變量。arg1担平,arg2示绊,obj,可以將消息放入其中暂论,作為消息信息面褐。然后發(fā)送消息即可
new Thread()
{
public void run() {
Message message=msgHandler.obtainMessage();//從Handler對象中獲取Message對象,而不是自己new空另,這樣效率高盆耽。
message.arg1=1;
message.sendToTarget(); //對于從handler對象中獲取的message蹋砚,可以直接使用該方法發(fā)送消息扼菠。
//msgHandler.sendMessage(message); //發(fā)送消息
};
}.start();

知識拓展:
1摄杂。實(shí)例化Handler時,可以使用帶一個Callback接口參數(shù)的構(gòu)造函數(shù)循榆。
其中Callback有一個未實(shí)現(xiàn)的方法析恢。 這個方法有一個返回值(boolean)
2.當(dāng)該Handler收到消息時,首先會調(diào)用Callback中的消息處理方法秧饮。
2.1 如果返回值為false映挂,消息不會被截斷。Handler依然可以處理該消息盗尸。
2.2 如果返回值為true柑船,消息將會被截斷。Handler中的處理消息方法不會被執(zhí)行泼各。


handler鞍时、looper、messagequeue扣蜻、message四者可以這樣理解:
handler:工人逆巍;
looper:傳送帶移動的動力;
messagequeue:傳送帶莽使;
message:傳送帶上面的產(chǎn)品锐极。
工人(handler)把自己的產(chǎn)品(message)放在傳送帶(messagequeue)尾部,在動力(looper)作用下芳肌,傳送帶向前移動灵再,最終產(chǎn)品到達(dá)傳送帶頭部,被取出來處理(handmessage())亿笤。


先要有個概念檬嘀。
1、handler 消息處理器责嚷,負(fù)責(zé)處理消息鸳兽。
2、Message 消息罕拂,包含消息id揍异,被處理的對象。
3爆班、MessageQueue 消息隊列衷掷,存放Handler發(fā)送過來的Message
4、looper 消息泵柿菩,不間斷的從MessageQueue消息隊列中抽取消息戚嗅。
簡單的比喻looper就是水泵,MessageQueue儲水的池塘,Message就是水懦胞,Handler就是操作的人替久。


主線程和子線程之間如何相互通信:

  1. 子線程向主線程發(fā)送消息,是通過調(diào)用主線程的Handler躏尉,發(fā)送信息給主線程的Looper,該Hnadler綁定了主線程的Looper蚯根。
  2. 主線程向子線程發(fā)送消息,是通過調(diào)用子線程的Handler胀糜,發(fā)送信息給子線程的Looper,因此必須有子線程的Looper颅拦,為了防止Looper沒有初始化,所以通過HandlerThread類教藻,來保證子線程的Looper再被主線程調(diào)用時已經(jīng)初始化距帅。
    1.創(chuàng)建主線程的handler,并向子線程發(fā)送消息:
    //主線程的handler
    Handler handler = new Handler(){
    public void handleMessage(android.os.Message msg) {
    Message message = new Message();
    threadHandler.sendMessageDelayed(message, 1000);//向子線程發(fā)送消息
    };
    };
    2.創(chuàng)建子線程的handler,向主線程發(fā)送消息括堤,要關(guān)聯(lián)一個threadHandler,threadHandler.getLooper()得到一個looper對象
    HandlerThread handlerThread = new HandlerThread("handler thread");
    handlerThread.start();//不要忘記調(diào)用start方法锥债!
    //子線程的handler
    threadHandler = new Handler(handlerThread.getLooper()){
    @Override
    public void handleMessage(Message msg) {//處理消息
    Message message = new Message();
    handler.sendMessageDelayed(message, 1000);
    }
    };

更新UI的四種方法:
tv指一個TextView;
先在onCreate()方法中新建一個線程:
new Thread() {
public void run() {
try {
Thread.sleep(2000);
方法()痊臭;
} catch (InterruptedException e) {
e.printStackTrace();
}
} }.start();
方法一:private void handle1() {
handler.post(new Runnable() {
@Override
public void run() {
tv.setText("First Way");
}
});
}
方法二:
先創(chuàng)建一個handler:
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
tv.setText("Second Way!");
};
};
然后
private void handle2() {
handler.sendEmptyMessage(1);
}
方法三:
private void updateUI() {
runOnUiThread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
tv.setText("Third Way");
}
});
}
方法四:
private void viewUI() {
tv.post(new Runnable() {

        @Override
        public void run() {
            // TODO Auto-generated method stub
            tv.setText("Fourth Way");
        }
    });
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末哮肚,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子广匙,更是在濱河造成了極大的恐慌允趟,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鸦致,死亡現(xiàn)場離奇詭異潮剪,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)分唾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門抗碰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人绽乔,你說我怎么就攤上這事弧蝇。” “怎么了折砸?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵看疗,是天一觀的道長。 經(jīng)常有香客問我睦授,道長两芳,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任去枷,我火速辦了婚禮怖辆,結(jié)果婚禮上是复,老公的妹妹穿的比我還像新娘。我一直安慰自己竖螃,他們只是感情好淑廊,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著斑鼻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪猎荠。 梳的紋絲不亂的頭發(fā)上坚弱,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機(jī)與錄音关摇,去河邊找鬼荒叶。 笑死,一個胖子當(dāng)著我的面吹牛输虱,可吹牛的內(nèi)容都是我干的些楣。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼宪睹,長吁一口氣:“原來是場噩夢啊……” “哼愁茁!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起亭病,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤鹅很,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后罪帖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體促煮,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年整袁,在試婚紗的時候發(fā)現(xiàn)自己被綠了菠齿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡坐昙,死狀恐怖绳匀,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情炸客,我是刑警寧澤襟士,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站嚷量,受9級特大地震影響陋桂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蝶溶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一嗜历、第九天 我趴在偏房一處隱蔽的房頂上張望宣渗。 院中可真熱鬧,春花似錦梨州、人聲如沸痕囱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鞍恢。三九已至,卻和暖如春每窖,著一層夾襖步出監(jiān)牢的瞬間帮掉,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工窒典, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蟆炊,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓瀑志,卻偏偏與公主長得像涩搓,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子劈猪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容