一、重要參考資料
【參考資料】
目前來看,下面的幾個網址中的内容品質比較不錯,基本不需要再讀别的網址了。
1、android消息機制一
android消息機制(一)
一、 角色描述
1.looper: 一個線程可以産生一個looper對象,由它來管理此線程裡的message queue(消息隊列)。
2.handler: 你可以構造handler對象來與looper溝通,以便push新消息到message queue裡;或者接收looper(從message queue取出)所送來的消息。
3. message queue(消息隊列):用來存放線程放入的消息。
4.線程:ui thread 通常就是main thread,而android啟動程式時會替它建立一個message queue。
每一個線程裡可含有一個looper對象以及一個messagequeue資料結構。在你的應用程式裡,可以定義handler的子類别來接收looper所送出的消息。
在你的android程式裡,新誕生一個線程,或執行 (thread)時,并不會自動建立其message loop。
android裡并沒有global的message queue資料結構,例如,不同apk裡的對象不能透過massage queue來交換訊息(message)。 例如:線程a的handler對象可以傳遞消息給别的線程,讓别的線程b或c等能送消息來給線程a(存于a的message queue裡(//如何了解?))。
線程a的message queue裡的訊息,隻有線程a所屬的對象可以處理。
使用looper.mylooper可以取得目前線程的looper對象。
使用mhandler = new eevnthandler(looper.mylooper()); 可用來構造目前線程的handler對象;其中,eevnthandler是自已實作的handler的子類别。
使用mhandler = new eevnthandler(looper.getmainlooper()); 可誕生用來處理main線程的handler對象;其中,eevnthandler是自已實作的handler的子類别。
這樣描述可能太抽像,下面舉幾個實際的例子來說明:
二、 舉例
1. 同線程内不同元件間的消息傳遞
looper類用來管理特定線程内對象之間的消息交換(message exchange)。你的應用程式可以産生許多個線程。而一個線程可以有許多個元件,這些元件之間常常需要互相交換訊息。如果有這種需要,您可以替線程構造一個looper對象,來擔任訊息交換的管理工作。looper對象會建立一個messagequeue資料結構來存放各對象傳來的消息(包括ui事件或system事件等)。如下圖:
每一個線程裡可含有一個looper對象以及一個messagequeue資料結構。在你的應用程式裡,可以定義handler的子類别來接收looper所送出的消息。
同線程不同元件之間的消息傳遞:
public class activity1 extends activity implements onclicklistener{
button button = null;
textview text = null;
@override
protected void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
setcontentview(r.layout.activity1);
button = (button)findviewbyid(r.id.btn);
button.setonclicklistener(this);
text = (textview)findviewbyid(r.id.content);
}
public void onclick(view v) {
switch (v.getid()) {
case r.id.btn: //元件1
looper looper = looper.mylooper();//取得目前線程裡的looper
myhandler mhandler = new myhandler(looper);//構造一個handler使之可與looper通信
//buton等元件可以由mhandler将消息傳給looper後,再放入messagequeue中,同時mhandler也可以接受來自looper消息
mhandler.removemessages(0);
string msgstr = "主線程不同元件通信:消息來自button";
message m = mhandler.obtainmessage(1, 1, 1, msgstr);//構造要傳遞的消息
mhandler.sendmessage(m);//發送消息:系統會自動調用handlemessage方法來處理消息
break;
}
}
private class myhandler extends handler{
public myhandler(looper looper){
super(looper);
}
@override
public void handlemessage(message msg) {//處理消息
text.settext(msg.obj.tostring()); //元件2
}
說明:
looper = looper.mylooper ();
調用looper類别的靜态mylooper()函數,以取得目前線程裡的looper對象.
mhandler = new myhandler (looper);
構造一個myhandler對象來與looper溝通。activity等對象可以藉由myhandler對象來将消息傳給looper,然後放入messagequeue裡;myhandler對象也扮演listener的角色,可接收looper對象所送來的消息。
message m = mhandler.obtainmessage(1, 1, 1, obj);
先構造一個message對象,并将資料存入對象裡。
mhandler.sendmessage(m);
就透過mhandler對象而将消息m傳給looper,然後放入messagequeue裡。
此時,looper對象看到messagequeue裡有消息m,就将它廣播出去,mhandler對象接到此訊息時,會呼叫其handlemessage()函數來處理,于是輸出"this my message!"于畫面上,
android消息處理機制(二)
角色綜述(回顧):
(1)ui thread 通常就是main thread,而android啟動程式時會替它建立一個messagequeue。
(2)當然需要一個looper對象,來管理該messagequeue。
(3)我們可以構造handler對象來push新消息到message queue裡;或者接收looper(從message queue取出)所送來的消息。
(4)線程a的handler對象可以傳遞給别的線程,讓别的線程b或c等能送訊息來給線程a(存于a的message queue裡)。
(5)線程a的message queue裡的消息,隻有線程a所屬的對象可以處理。
子線程傳遞消息給主線程
publicclass activity2extends activityimplements onclicklistener{
button button =null;
textview text =null;
myhandler mhandler = null;
thread thread ;
@override
protectedvoid oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
setcontentview(r.layout.activity1);
button = (button)findviewbyid(r.id.btn);
button.setonclicklistener(this);
text = (textview)findviewbyid(r.id.content);
}
publicvoid onclick(view v) {
switch (v.getid()) {
case r.id.btn:
thread =new mythread();
thread.start();
break;
}
}
privateclass myhandlerextends handler{
public myhandler(looper looper){
super(looper);
}
@override
publicvoid handlemessage(message msg) {//處理消息
text.settext(msg.obj.tostring());
privateclass mythreadextends thread{
publicvoid run() {
looper curlooper = looper.mylooper();
looper mainlooper = looper.getmainlooper();
string msg ;
if(curlooper==null){
mhandler =new myhandler(mainlooper);
msg = "curlooper is null";
}else{
mhandler =new myhandler(curlooper);
msg = "this is curlooper";
}
mhandler.removemessages(0);
message m = mhandler.obtainmessage(1, 1, 1, msg);
mhandler.sendmessage(m);
}
說明:
android會自動替主線程建立message queue。在這個子線程裡并沒有建立message queue。是以,mylooper值為null,而mainlooper則指向主線程裡的looper。于是,執行到:
mhandler = new myhandler (mainlooper);
此mhandler屬于主線程。
mhandler.sendmessage(m);
就将m消息存入到主線程的message queue裡。mainlooper看到message queue裡有訊息,就會作出處理,于是由主線程執行到mhandler的handlemessage()來處理消息。
3、android線程間通信的message機制
在android下面也有多線程的概念,在c/c++中,子線程可以是一個函數,一般都是一個帶有循環的函數,來處理某些資料,優先線程隻是一個複雜的運算過程,是以可能不需要while循環,運算完成,函數結束,線程就銷毀。對于那些需要控制的線程,一般我們都是和互斥鎖互相關聯,進而來控制線程的進度,一般我們建立子線程,一種線程是很常見的,那就是帶有消息循環的線程。
消息循環是一個很有用的線程方式,曾經自己用c在linux下面實作一個消息循環的機制,往消息隊列裡添加資料,然後異步的等待消息的傳回。當消息隊列為空的時候就會挂起線程,等待新的消息的加入。這是一個很通用的機制。
在android,這裡的線程分為有消息循環的線程和沒有消息循環的線程,有消息循環的線程一般都會有一個looper,這個事android的新概念。我們的主線程(ui線程)就是一個消息循環的線程。針對這種消息循環的機制,我們引入一個新的機制handle,我們有消息循環,就要往消息循環裡面發送相應的消息,自定義消息一般都會有自己對應的處理,消息的發送和清除,消息的的處理,把這些都封裝在handle裡面,注意handle隻是針對那些有looper的線程,不管是ui線程還是子線程,隻要你有looper,我就可以往你的消息隊列裡面添加東西,并做相應的處理。
但是這裡還有一點,就是隻要是關于ui相關的東西,就不能放在子線程中,因為子線程是不能操作ui的,隻能進行資料、系統等其他非ui的操作。
那麼什麼情況下面我們的子線程才能看做是一個有looper的線程呢?我們如何得到它looper的句柄呢?
looper.mylooper();獲得目前的looper
looper.getmainlooper () 獲得ui線程的lopper
我們看看handle的初始化函數,如果沒有參數,那麼他就預設使用的是目前的looper,如果有looper參數,就是用對應的線程的looper。
如果一個線程中調用looper.prepare(),那麼系統就會自動的為該線程建立一個消息隊列,然後調用 looper.loop();之後就進入了消息循環,這個之後就可以發消息、取消息、和處理消息。這個如何發送消息和如何處理消息可以再其他的線程中通過handle來做,但前提是我們的hanle知道這個子線程的looper,但是你如果不是在子線程運作 looper.mylooper(),一般是得不到子線程的looper的。
public void run() {
synchronized (mlock) {
looper.prepare();
//do something
}
looper.loop();
}
是以很多人都是這樣做的:我直接在子線程中建立handle,然後在子線程中發送消息,這樣的話就失去了我們多線程的意義了。
class mythread extends thread{
private ehandler mhandler ;
public void run() {
looper mylooper, mainlooper;
mylooper = looper.mylooper ();
mainlooper = looper.getmainlooper ();
string obj;
if (mylooper == null ){
mhandler = new ehandler(mainlooper);
obj = "current thread has no looper!" ;
}
else {
mhandler = new ehandler(mylooper);
obj = "this is from current thread." ;
mhandler .removemessages(0);
message m = mhandler .obtainmessage(1, 1, 1, obj);
mhandler .sendmessage(m);
}
}
可以讓其他的線程來控制我們的handle,可以把 private ehandler mhandler ;放在外面,這樣我們的發消息和處理消息都可以在外面來定義,這樣增加程式代碼的美觀,結構更加清晰。
對如任何的handle,裡面必須要重載一個函數
public void handlemessage(message msg)
這個函數就是我們的消息處理,如何處理,這裡完全取決于你,然後通過 obtainmessage和 sendmessage等來生成和發送消息, removemessages(0)來清除消息隊列。google真是太智慧了,這種架構的産生,我們寫代碼更加輕松了。
有的時候,我們的子線程想去改變ui了,這個時候千萬不要再子線程中去修改,獲得ui線程的looper,然後發送消息即可。
我們看看goole music app的源代碼。
在mediaplaybackactivity.java中,我們可以看一下再oncreate中的有這樣的兩句:
malbumartworker = new worker("album art worker");
malbumarthandler = new albumarthandler(malbumartworker.getlooper());
很明顯這兩句,是建構了一個子線程。并且這個子線程還是looper的子線程,這裡很牛逼的使用了 malbumartworker.getlooper()這個函數,因為我們知道,我們能夠得到子線程的looper的途徑隻有一個:就是在子線程中調用 looper.mylooper (),并且這個函數還要在我們perpare之後調用才能得到正确的looper,但是他這裡用了一個這樣的什麼東東 getlooper,不知道它是如何實作的?
這裡有一個大概的思路,我們在子線程的的prepare之後調用 mylooper ()這個方法,然後儲存在一個成員變量中,這個getlooper就傳回這個東西,但是這裡會碰到多線程的一個很突出的問題,同步。我們在父線程中調用 malbumartworker.getlooper(),但是想要這個傳回正确的looper就必須要求我們的子線程運作了prepare,但是這個東西實在子線程運作的,我們如何保證呢?
我們看google是如何實作的?
private class worker implements runnable {
private final object mlock = new object();
private looper mlooper;
/**
* creates a worker thread with the given name. the thread
* then runs a [email=%7b@link]{@link[/email] android.os.looper}.
* @param name a name for the new thread
*/
worker(string name) {
thread t = new thread(null, this, name);
t.setpriority(thread.min_priority);
t.start();
while (mlooper == null) {
try {
mlock.wait();
} catch (interruptedexception ex) {
}
public looper getlooper() {
return mlooper;
public void run() {
mlooper = looper.mylooper();
mlock.notifyall();
public void quit() {
mlooper.quit();
}
我們知道,一個線程類的構造函數是在主線程中完成的,是以在我們的 worker的構造函數中我們創佳一個線程,然後讓這個線程運作,這一這個線程的建立是指定一個 runnabl,這裡就是我們的worker本身,在主線程調用 t.start();,這後,我們子線程已經建立,并且開始執行work的run方法。然後下面的代碼很藝術:
synchronized (mlock) {
我們開始等待我們的子線程給mlooper指派,如果不指派我們就繼續等,然後我們的子線程在運作run方法之後,在給 mlooper指派之後,通知worker夠着函數中的wait,然後我們的構造函數才能完成,是以我們說:
malbumartworker = new worker("album art worker");
這句本身就是阻塞的,它建立了一個子線程,開啟了子線程,并且等待子線程給mlooper指派,指派完成之後,這個函數才傳回,這樣才能保證我們的子線程的looper的擷取絕對是正确的,這個構思很有創意。值得借鑒<!--++ plugin_code qcomic begin--> <!--++ plugin_code qcomic end-->
4、android中handler的使用方法-在子線程中更新界面
http://blog.csdn.net/chaoyu168/article/details/50914021
二、知識要點
一、知識點
1、handler應該由處理消息的線程建立。
2、handler與建立它的線程相關聯,而且也隻與建立它的線程相關聯。handler運作在建立它的線程中,是以,如果在handler中進行耗時的操作,會阻塞建立它的線程。
【來源】以上來自:
二、一些知識點
1、android的線程分為有消息循環的線程和沒有消息循環的線程,有消息循環的線程一般都會有一個looper。主線程(ui線程)就是一個消息循環的線程。
2、
looper.mylooper(); //獲得目前的looper
looper.getmainlooper () //獲得ui線程的lopper
3、handle的初始化函數(構造函數),如果沒有參數,那麼他就預設使用的是目前的looper,如果有looper參數,就是用對應的線程的looper。
三、應用執行個體
3.1 handler傳遞message【應用示例一】
package com.android.tutor;
import java.util.timer;
import java.util.timertask;
import android.app.activity;
import android.os.bundle;
import android.os.handler;
import android.os.message;
public class handlerdemo extends activity {
//title為settitle方法提供變量,這裡為了友善我設定成了int型
private int title = 0;
private handler mhandler = new handler(){
public void handlemessage(message msg) {
switch (msg.what) {
case 1:
updatetitle();
break;
}
};
};
public void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
setcontentview(r.layout.main);
timer timer = new timer();
timer.scheduleatfixedrate(new mytask(), 1, 5000);
}
private class mytask extends timertask{
@override
public void run() {
message message = new message();
message.what = 1;
mhandler.sendmessage(message);
}
public void updatetitle(){
settitle("welcome to mr wei's blog " + title);
title ++;
}
上面的代碼,直接在主線程中定義handler成員。在子線程中通過主線程的handler向主線程發送消息。其使用步驟如下:
1、在主線程中定義handler,并為這個handler實作handlemessage方法。
2、在子線程中調用主線程的handler,通過其sendmessage方法發送消息。
3.2 handler傳遞runnable對象還有另外一種用handler來進行線程間通信的方式,那就是用handler來傳遞一個runnable對象,而不是一個message。
【應用執行個體三】
使用步驟
1、在主線程中定義handler對象
2、構造一個runnable對象,為該對象實作runnable方法,在該方法中進行一些你想做的耗時操作。
3、在子線程中使用handler對象post(runnable)對象.
轉載:http://blog.csdn.net/chaoyu168/article/details/50913941