一,相關概念
在Android中如果通過用戶界面(如button)來來啓動線程,然後再線程中的執行代碼將狀態信息輸出到用戶界面(如文本框),這時候就會拋出以下的異常信息:
5-12 13:33:04.393: ERROR/JavaBinder(1029):android.view.ViewRoot$CalledFromWrongThreadException:Onlythe original thread that created
a view hierarchy can touch its views.
該異常的意思是,只有最初創建視圖層次結構的線程才能接觸該結構中的視圖,也就是說,不是最初創建界面的線程是不能接觸界面元素的。那麼,在不是創建界面的線程中,如何把內容輸出到界面元素中呢?
對了,我們可以通過線程消息分發機制來實現。就應用程序而言,Android系統中Java的應用程序和其他系統上一樣,都是依賴於消息驅動完成那些比較有難度的工作,它們的工作原理大致是分別有一個消息隊列負責接收各種消息,一個消息循環可以不斷從中取出消息然後做處理。先了解一下幾個消息機制的常見名詞概念:
消息的封裝者和處理者,handler負責將需要傳遞的信息封裝成Message,通過調用handler對象的obtainMessage()來實現;將消息傳遞給Looper,這是通過handler對象的sendMessage()來實現的。繼而由Looper將Message放入MessageQueue中。當Looper對象看到MessageQueue中含有Message,就將其廣播出去。該handler對象收到該消息後,調用相應的handler對象的handleMessage()方法對其進行處理。
消息對象,Message Queue中的存放的對象。一個Message Queue中包含多個Message。Message實例對象的取得,通常使用Message類裏的靜態方法obtain(),該方法有多個重載版本可供選擇;它的創建並不一定是直接創建一個新的實例,而是先從Message
Pool(消息池)中看有沒有可用的Message實例,存在則直接取出返回這個實例。如果Message Pool中沒有可用的Message實例,則才用給定的參數創建一個Message對象。調用removeMessages()時,將Message從Message Queue中刪除,同時放入到Message Pool中。除了上面這種方式,也可以通過Handler對象的obtainMessage()獲取一個Message實例。
是一種數據結構,見名知義,就是一個消息隊列,存放消息的地方。每一個線程最多只可以擁有一個MessageQueue數據結構。創建一個線程的時候,並不會自動創建其MessageQueue。通常使用一個Looper對象對該線程的MessageQueue進行管理。主線程創建時,會創建一個默認的Looper對象,而Looper對象的創建,將自動創建一個Message
Queue。其他非主線程,不會自動創建Looper,要需要的時候,通過調用prepare函數來實現。
是MessageQueue的管理者。每一個MessageQueue都不能脫離Looper而存在,Looper對象的創建是通過prepare函數來實現的。同時每一個Looper對象和一個線程關聯。通過調用Looper.myLooper()可以獲得當前線程的Looper對象創建一個Looper對象時,會同時創建一個MessageQueue對象。除了主線程有默認的Looper,其他線程默認是沒有MessageQueue對象的,所以,不能接受Message。如需要接受,自己定義一個Looper對象(通過prepare函數),這樣該線程就有了自己的Looper對象和MessageQueue數據結構了。Looper從MessageQueue中取出Message然後,交由Handler的handleMessage進行處理。處理完成後,調用Message.recycle()將其放入Message
Pool中。
二,Looper類分析
當需要向其他線程發消息時我們需要實例化Looper對象會有以下兩步:
1,Looper.prepare()
我們可以看下源碼:
-
-
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
-
public static void prepare() {
-
if (sThreadLocal.get() != null) {
-
throw new RuntimeException("Only one Looper may be created per thread");
-
}
-
-
sThreadLocal.set(new Looper());
-
}
可以看出來,Looper.prepare()會在調用線程的局部變量中設置一個Looper對象。這個調用線程就是LooperThread的run線程。F3看一下Looper的構造方法:
-
private Looper() {
-
-
mQueue = new MessageQueue();
-
mRun = true;
-
-
mThread = Thread.currentThread();
-
}
所以Looper.prepare()的作用是設置一個Looper對象保存在sThreadLocal裏,而Looper自己內部封裝了一個消息隊列。就這樣把Looper和調用線程關聯在一起了。到底要幹嘛,我們看Looper的第二步棋
2,Looper.loop()
-
public static void loop() {
-
-
Looper me = myLooper();
-
if (me == null) {
-
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
-
}
-
-
MessageQueue queue = me.mQueue;
-
while (true) {
-
-
Message msg = queue.next();
-
if (msg != null) {
-
if (msg.target == null) {
-
-
return;
-
}
-
msg.target.dispatchMessage(msg);
-
msg.recycle();
-
}
-
}
-
}
-
-
-
-
-
-
public static Looper myLooper() {
-
return sThreadLocal.get();
-
}
綜上可以知道Looper類的作用是:
封裝了一個消息隊列;
Looper的prepare方法把這個Looper和調用prepare的線程綁定在一起;
處理線程調用loop方法,處理來自該消息隊列的消息。
三,Handler類分析
1,好多構造函數
一樣看去,我們可以看到幾個眼熟的傢伙:MessageQueue,Looper,Callback,一個不少,他們是幹嘛的?往下看發現有4個形態不一的構造函數:
-
-
-
-
public Handler() {
-
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;
-
}
-
-
-
-
-
public Handler(Callback callback) {
-
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 = callback;
-
}
-
-
-
-
-
public Handler(Looper looper) {
-
mLooper = looper;
-
mQueue = looper.mQueue;
-
mCallback = null;
-
}
-
-
-
-
-
public Handler(Looper looper, Callback callback) {
-
mLooper = looper;
-
mQueue = looper.mQueue;
-
mCallback = callback;
-
}
在上面的構造函數中,Handler中的消息隊列變量最終都會指向Looper的消息隊列,Handler爲啥要這麼做,我們繼續挖。
2,Handler的真正使命
通過分析Handler源代碼,可以發現它的作用就是提供一系列函數,方便我們完成消息的創建封裝和插入到消息隊列:
-
-
public final boolean hasMessages(int what) {
-
return mQueue.removeMessages(this, what, null, false);
-
}
-
-
public final Message obtainMessage(int what)
-
{
-
return Message.obtain(this, what);
-
}
-
-
public final boolean sendMessage(Message msg)
-
{
-
return sendMessageDelayed(msg, 0);
-
}
-
-
public final boolean sendMessageDelayed(Message msg, long delayMillis)
-
{
-
if (delayMillis < 0) {
-
delayMillis = 0;
-
}
-
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
-
}
選其一進行分析,就sendMessageDelayed()了,F3進去:
-
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
-
{
-
boolean sent = false;
-
MessageQueue queue = mQueue;
-
if (queue != null) {
-
-
msg.target = this;
-
sent = queue.enqueueMessage(msg, uptimeMillis);
-
}
-
else {
-
RuntimeException e = new RuntimeException(
-
this + " sendMessageAtTime() called with no mQueue");
-
Log.w("Looper", e.getMessage(), e);
-
}
-
return sent;
-
}
以上工作是往Looper的消息隊列中加入了一個消息,按照Looper類分析可知,它在獲取消息後會調用target的dispatchMessage函數,再把這個消息分發給Handle處理,接着繼續看Handler.java:
-
public void dispatchMessage(Message msg) {
-
-
if (msg.callback != null) {
-
handleCallback(msg);
-
} else {
-
-
if (mCallback != null) {
-
if (mCallback.handleMessage(msg)) {
-
return;
-
}
-
}
-
-
handleMessage(msg);
-
}
-
}
在絕大多數情況下,我們都是走的第三部流程,用一個從Handler派生的子類並重載handleMessage()來處理各種封裝的消息。
小結:1,Looper中有一個Message隊列,裏面儲存的是一個個待處理的Message。
2,Message中有一個Handler,這個Handler是用來處理Message的。
四,實戰篇
1,主線程給自己發消息
-
<span style="font-family:'Microsoft YaHei';font-size:12px;">public class handler1 extends Activity {
-
private Button btnTest;
-
private EditText textView;
-
private final String tag="hyj_handler1";
-
private Handler handler;
-
-
@Override
-
public void onCreate(Bundle savedInstanceState) {
-
super.onCreate(savedInstanceState);
-
setContentView(R.layout.main);
-
btnTest = (Button)this.findViewById(R.id.bt1);
-
textView = (EditText)this.findViewById(R.id.ed1);
-
-
btnTest.setOnClickListener(new View.OnClickListener() {
-
-
@Override
-
public void onClick(View arg0) {
-
-
Looper looper = Looper.getMainLooper();
-
-
-
handler = new MyHandler(looper);
-
handler.removeMessages(0);
-
-
-
-
-
Message msg = handler.obtainMessage(1,1,1,"主線程發消息了");
-
-
handler.sendMessage(msg);
-
Log.i(tag, looper.toString());
-
Log.i(tag, msg.toString());
-
Log.i(tag, handler.toString());
-
}
-
});
-
}
-
-
class MyHandler extends Handler{
-
-
public MyHandler(Looper looper){
-
super(looper);
-
}
-
-
public void handleMessage(Message msg){
-
super.handleMessage(msg);
-
textView.setText("我是主線程的Handler,收到了消息:"+(String)msg.obj);
-
}
-
}
-
}</span>
2,其他線程給主線程發消息
-
<span style="font-family:'Microsoft YaHei';font-size:12px;">public class handler2 extends Activity{
-
-
private Button btnTest;
-
private EditText textView;
-
-
private Handler handler;
-
-
@Override
-
public void onCreate(Bundle savedInstanceState) {
-
super.onCreate(savedInstanceState);
-
setContentView(R.layout.layout2);
-
btnTest = (Button)this.findViewById(R.id.bt2);
-
textView = (EditText)this.findViewById(R.id.ed2);
-
-
btnTest.setOnClickListener(new View.OnClickListener() {
-
-
@Override
-
public void onClick(View arg0) {
-
-
-
-
new MyThread().start();
-
}
-
});
-
}
-
-
class MyHandler extends Handler{
-
-
public MyHandler(Looper looper){
-
super(looper);
-
}
-
-
public void handleMessage(Message msg){
-
super.handleMessage(msg);
-
textView.setText("我是主線程的Handler,收到了消息:"+(String)msg.obj);
-
}
-
}
-
class MyThread extends Thread{
-
-
public void run(){
-
Looper looper = Looper.getMainLooper();
-
-
-
handler = new MyHandler(looper);
-
Message msg = handler.obtainMessage(1,1,1,"其他線程發消息了");
-
handler.sendMessage(msg);
-
}
-
}
-
}</span>
3,主線程給其他線程發消息
-
<span style="font-family:'Microsoft YaHei';font-size:12px;">public class handler3 extends Activity{
-
private Button btnTest;
-
private EditText textView;
-
-
private Handler handler;
-
-
@Override
-
public void onCreate(Bundle savedInstanceState) {
-
super.onCreate(savedInstanceState);
-
setContentView(R.layout.main);
-
-
btnTest = (Button)this.findViewById(R.id.bt1);
-
textView = (EditText)this.findViewById(R.id.ed1);
-
-
-
-
new MyThread().start();
-
-
btnTest.setOnClickListener(new View.OnClickListener() {
-
-
@Override
-
public void onClick(View arg0) {
-
-
-
Message msg = handler.obtainMessage(1,1,1,"主線程發送的消息");
-
handler.sendMessage(msg);
-
}
-
});
-
}
-
-
class MyHandler extends Handler{
-
-
public MyHandler(Looper looper){
-
super(looper);
-
}
-
-
public void handleMessage(Message msg){
-
super.handleMessage(msg);
-
textView.setText("我是主線程的Handler,收到了消息:"+(String)msg.obj);
-
}
-
}
-
-
class MyThread extends Thread{
-
-
public void run(){
-
Looper.prepare();
-
-
-
-
-
-
-
handler = new ThreadHandler(Looper.myLooper());
-
-
-
-
-
Looper.loop();
-
-
}
-
-
-
class ThreadHandler extends Handler{
-
-
public ThreadHandler(Looper looper){
-
super(looper);
-
}
-
-
public void handleMessage(Message msg){
-
-
-
handler = new MyHandler(Looper.getMainLooper());
-
-
Message msg2 = handler.obtainMessage(1,1,1,"子線程收到:"+(String)msg.obj);
-
-
handler.sendMessage(msg2);
-
}
-
}
-
}</span>
4,其他線程給其自己發消息
-
<span style="font-family:'Microsoft YaHei';font-size:12px;">public class handler4 extends Activity{
-
Button bt1 ;
-
EditText et1;
-
Handler handler;
-
public void onCreate(Bundle savedInstanceState){
-
super.onCreate(savedInstanceState);
-
setContentView(R.layout.main);
-
bt1 = (Button)findViewById(R.id.bt1);
-
et1 = (EditText)findViewById(R.id.ed1);
-
-
bt1.setOnClickListener(new View.OnClickListener() {
-
-
@Override
-
public void onClick(View v) {
-
new ThreadHandler().start();
-
}
-
});
-
-
-
}
-
-
class MyHandler extends Handler{
-
-
public MyHandler(Looper looper){
-
super(looper);
-
}
-
-
public void handleMessage(Message msg){
-
super.handleMessage(msg);
-
et1.setText("我是主線程的Handler,收到了消息:"+(String)msg.obj);
-
}
-
}
-
-
public class ThreadHandler extends Thread{
-
public void run(){
-
Looper.prepare();
-
handler = new Handlerthread(Looper.myLooper());
-
Message msg = handler.obtainMessage(1,1,1,"我自己");
-
handler.sendMessage(msg);
-
Looper.loop();
-
}
-
-
-
public class Handlerthread extends Handler{
-
public Handlerthread(Looper looper){
-
super(looper);
-
}
-
-
public void handleMessage(Message msg){
-
-
-
-
if(msg.what == 1 && msg.obj.equals("我自己")){
-
handler = new MyHandler(Looper.getMainLooper());
-
Message msg2 = handler.obtainMessage(1,1,1,"稟告主線程:我收到了自己發給自己的Message");
-
handler.sendMessage(msg2);
-
-
-
}}
-
}
-
-
}
-
</span>