随笔-348  评论-598  文章-0  trackbacks-0
public class

Looper

extends Object
java.lang.Object
   ↳ android.os.Looper

Class Overview

Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare() in the thread that is to run the loop, and then loop() to have it process messages until the loop is stopped.

Most interaction with a message loop is through the Handler class.

This is a typical example of the implementation of a Looper thread, using the separation of prepare() and loop() to create an initial Handler to communicate with the Looper.

  class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
Looper提供了一种消息循环机制,主要为Handler服务。
你会问,在主线程里面可以创建Handler,那为什么还要启动一个线程去Loop另外一个Handler呢?
那是因为每个Handler实例只能服务于一个线程和他的消息队列(Each Handler instance is associated with a single thread and that thread's message queue),如果你单独启动了另外一个线程去进行某些操作,同时又要发送消息,那么就需要你创建一个Looper线程,里面声明一个Handler实例,然后在其他某个线程中去调用这个Handler,这样就不会有问题了。
当然这里需要注意的是,你不能在这个Handler中去修改UI控件的属性,那是因为,你这个Handler仍然处于非UI主线程中,如果要修改UI,那么你可以在这个Handler中调用主线程的一个UI Handler去Post一个消息,在那个消息中对UI控件进行修改等操作。

通过传递不同线程的Looper,可以使得Handler服务于不同的线程。

1.  Message Queue的角色

l   在你的Android程式裡,新誕生一個線程,或稱執行緒(Thread)時,並不會自動建立其Message Loop

l   Android裡並沒有GlobalMessage Queue資料結構,例如,不同APK裡的物件不能透過Massage Queue來交換訊息(Message)

l   一個線程可以誕生一個Looper之物件,由它來管理此線程裡的Message Queue

l   你可以誕生Handler之物件來與Looper溝通,以便push新訊息到Message Queue裡;或者接收Looper(Message Queue取出)所送來的訊息。

l   線程AHandler物件參考可以傳遞給別的線程,讓別的線程BC等能送訊息來給線程A(存於AMessage Queue)

l   線程AMessage Queue裡的訊息,只有線程A所屬的物件可以處理之。

l   使用Looper.myLooper可以取得目前線程的Looper物件參考值。

l   使用mHandler = new EevntHandler(Looper.myLooper()); 可誕生用來處理目前線程的Handler物件;其中,EevntHandlerHandler的子類別。

l   使用mHandler = new EevntHandler(Looper.getMainLooper()); 可誕生用來處理main線程的Handler物件;其中,EevntHandlerHandler的子類別。

2. 範例之一:Looper物件之角色

Looper類別用來管理特定線程內物件之間的訊息交換(Message Exchange)。你的應用程式可以誕生許多個線程,或稱執行緒(Thread)。而一個線程可以誕生許多個物件,這些物件之間常常需要互相交換訊息。如果有這種需要,您可以替線程誕生一個Looper類別之物件,來擔任訊息交換的管理工作。Looper物件會建立一個MessageQueue資料結構來存放各物件傳來的訊息(包括UI事件或System事件等)。如下圖:

     每一個線程(Thread,或稱「執行緒」)裡可含有一個Looper物件以及一個MessageQueue資料結構。在你的應用程式裡,可以定義Handler的子類別來接收Looper所送出的訊息。

//----- Looper_01範例 -----

package com.misoo.kx04;

import android.app.Activity;

import android.graphics.Color;

import android.os.Bundle;

import android.os.Handler;

import android.os.Looper;

import android.os.Message;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import android.widget.LinearLayout;

import android.widget.TextView;

publicclass ac01 extends Activity implements OnClickListener {

           privatefinalintWC = LinearLayout.LayoutParams.WRAP_CONTENT;

           privatefinalintFP = LinearLayout.LayoutParams.FILL_PARENT;

           public TextView tv;

    private EventHandler mHandler;

    private Button btn, btn2, btn3;

   

    publicvoid onCreate(Bundle icicle) {

                super.onCreate(icicle);

                LinearLayout layout = new LinearLayout(this);

                layout.setOrientation(LinearLayout.VERTICAL);

                              

                btn = new Button(this);

                btn.setId(101);

                btn.setBackgroundResource(R.drawable.heart);

                btn.setText("test looper");

                btn.setOnClickListener(this);

                LinearLayout.LayoutParams param =

                    new LinearLayout.LayoutParams(100,50);

                param.topMargin = 10;

                layout.addView(btn, param);

               

                btn2 = new Button(this);

                btn2.setId(102);

                btn2.setBackgroundResource(R.drawable.ok_blue);

                btn2.setText("exit");

                btn2.setOnClickListener(this);

                layout.addView(btn2, param);

               

                tv = new TextView(this);

                tv.setTextColor(Color.WHITE);

                tv.setText("");

                LinearLayout.LayoutParams param2 =

                   new LinearLayout.LayoutParams(FP, WC);

                param2.topMargin = 10;

                layout.addView(tv, param2);

                setContentView(layout);     

               }

              publicvoid onClick(View v) {

                     switch(v.getId()){

                     case 101:

                                Looper looper;

                          looper = Looper.myLooper();

                          mHandler = new EventHandler(looper);

                          mHandler.removeMessages(0);

                          // 清除整個MessageQueue裡的事件,確保不會通知到別人

                          String obj = "This my message!";

                          Message m = mHandler.obtainMessage(1, 1, 1, obj);

                          // 組裝成一個Message物件

                          mHandler.sendMessage(m);

                          // Message物件送入MessageQueue

                   break;

                     case 102:

                  finish();

                                break;

                     }

           }

//------------------------------------------------------             

class EventHandler extends Handler

               {

                   public EventHandler(Looper looper) {

                       super(looper);

                   }

                   @Override

                   publicvoid handleMessage(Message msg) {

                      tv.setText((String)msg.obj);

               }

           }

}

//-------------------------------------------------------

說明:

    此程式啟動時,目前線程(即主線程, main thread)已誕生了一個Looper物件,並且有了一個MessageQueue資料結構。

    指令:looper = Looper.myLooper();

就呼叫Looper類別的靜態myLooper()函數,以取得目前線程裡的Looper物件之參考值。

指令:mHandler = new EventHandler(looper);

誕生一個EventHandler之物件來與Looper溝通。Activity等物件可以藉由EventHandler物件來將訊息傳給Looper,然後放入MessageQueue裡;EventHandler物件也扮演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!"於畫面上,如下:

3. 範例之二:由別的線程送訊息到主線程的Message Queue

//----- Looper_02範例 -----

package com.misoo.kx04;

import android.app.Activity;

import android.graphics.Color;

import android.os.Bundle;

import android.os.Handler;

import android.os.Looper;

import android.os.Message;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import android.widget.LinearLayout;

import android.widget.TextView;

publicclass ac01 extends Activity implements OnClickListener {

           privatefinalintWC = LinearLayout.LayoutParams.WRAP_CONTENT;

           privatefinalintFP = LinearLayout.LayoutParams.FILL_PARENT;

           public TextView tv;

   private myThread t;

    private Button btn, btn2, btn3;

   

    publicvoid onCreate(Bundle icicle) {

                super.onCreate(icicle);

                LinearLayout layout = new LinearLayout(this);

                layout.setOrientation(LinearLayout.VERTICAL);

                              

                btn = new Button(this);

                btn.setId(101);

                btn.setBackgroundResource(R.drawable.heart);

                btn.setText("test looper");

                btn.setOnClickListener(this);

                LinearLayout.LayoutParams param =

                    new LinearLayout.LayoutParams(100,50);

                param.topMargin = 10;

                layout.addView(btn, param);

               

                btn2 = new Button(this);

                btn2.setId(102);

                btn2.setBackgroundResource(R.drawable.ok_blue);

                btn2.setText("exit");

                btn2.setOnClickListener(this);

                layout.addView(btn2, param);

               

                tv = new TextView(this);

                tv.setTextColor(Color.WHITE);

                tv.setText("");

                LinearLayout.LayoutParams param2 =

                   new LinearLayout.LayoutParams(FP, WC);

                param2.topMargin = 10;

                layout.addView(tv, param2);

                setContentView(layout);     

               }

              publicvoid onClick(View v) {

                     switch(v.getId()){

                     case 101:

                                 t = new myThread();

                          t.start();

                          break;

                     case 102:

                  finish();

                                break;

                     }

           }

//------------------------------------------------------             

class EHandler extends Handler {

                   public EHandler(Looper looper) {

                       super(looper);

                   }

                   @Override

                   publicvoid handleMessage(Message msg) {

                      tv.setText((String)msg.obj);

               }

           }

//------------------------------------------------------             

class myThread extends Thread{

            private EHandler mHandler;

            publicvoid 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);

            }

 }

}

//-------------------------------------------------------

Android會自動替主線程建立Message Queue。在這個子線程裡並沒有建立Message Queue。所以,myLooper值為null,而mainLooper則指向主線程裡的Looper物件。於是,執行到指令:

mHandler = new EHandler(mainLooper); mHandler屬於主線程。

     指令:mHandler.sendMessage(m);

就將m訊息存入到主線程的Message Queue裡。mainLooper看到Message Queue裡有訊息,就會處理之,於是由主線程執行到mHandlerhandleMessage()函數來處理訊息。此程式輸出畫面為:

4. 結語:

l   Message Loop的用途很廣。請你參閱高煥堂所寫的Android系列書籍,尤其是其中的第4本書:<<Android設計招式之美>>

l   以上只是本文的前半段而已,請你繼續閱讀後半段。



---------------------------------------------------------
专注移动开发

Android, Windows Mobile, iPhone, J2ME, BlackBerry, Symbian
posted on 2010-02-18 10:52 TiGERTiAN 阅读(3531) 评论(0)  编辑  收藏 所属分类: JavaAndroid

只有注册用户登录后才能发表评论。


网站导航: