活用Android的Message Queue(来自高焕堂的讲义)

活用Android的Message Queue(1/3)


1.  Message Queue的角色

 

   <1> 在你的Android程序里,新诞生一个线程,或称执行(Thread)时,并不会自动建立其Message Loop。

  <2>Android里并没有Global的Message Queue数据结构,例如,不同APK里的对象不能透过Massage Queue来交换讯息(Message)。

  <3>一个线程可以诞生一个Looper之对象,由它来管理此线程里的Message Queue。

  <4>你可以诞生Handler之对象来与Looper沟通,以便push新讯息到Message Queue里;或者接收Looper(从Message Queue取出)所送来的讯息。

  <5>线程A的Handler对象参考可以传递给别的线程,让别的线程B或C等能送讯息来给线程A(存于A的Message Queue里)。

  <6>线程A的Message Queue里的讯息,只有线程A所属的对象可以处理之。

  <7>使用Looper.myLooper可以取得目前线程的Looper对象参考值。

  <8>使用mHandler = new EevntHandler(Looper.myLooper()); 可诞生用来处理目前线程的Handler对象;其中,EevntHandler是Handler的子类别。

  <9>使用mHandler = new EevntHandler(Looper.getMainLooper()); 可诞生用来处理main线程的Handler对象;其中,EevntHandler是Handler的子类别。

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;
public class ac01 extends Activity implements OnClickListener {
private final int WC = LinearLayout.LayoutParams.WRAP_CONTENT; 
private final int FP = LinearLayout.LayoutParams.FILL_PARENT; 
public TextView tv;
    private EventHandler mHandler;
    private Button btn, btn2, btn3;
    
    public void 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);      
               }
       	public void 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
        public void 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;
public class ac01 extends Activity implements OnClickListener {
private final int WC = LinearLayout.LayoutParams.WRAP_CONTENT; 
private final int FP = LinearLayout.LayoutParams.FILL_PARENT; 
public TextView tv;
    private myThread t;
    private Button btn, btn2, btn3;
    
    public void 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);      
               }
       	public void 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
        public void handleMessage(Message msg) {
           tv.setText((String)msg.obj);
    }
}
//------------------------------------------------------	   
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);
  }
  }
}
//-------------------------------------------------------

Android会自动替主线程建立Message Queue。在这个子线程里并没有建立Message Queue。所以,myLooper值为null,而mainLooper则指向主线程里的Looper物件。于是,执行到指令:

mHandler = new EHandler(mainLooper); 此mHandler属于主线程。

     指令:mHandler.sendMessage(m);

     就将m讯息存入到主线程的Message Queue里。mainLooper看到Message Queue里有讯息,就会处理之,于是由主线程执行到mHandler的handleMessage()函数来处理讯息。此程序输出画面为:

4. 结语:

Message Loop的用途很广。请你参阅高焕堂所写的Android系列书籍,尤其是其中的第4本书:<<Android设计招式之美>> 。

 以上只是本文的前半段而已,请你继续阅读后半段。


活用Android的Message Queue(2/3)

1. 复习Message Queue的角色

    在上一篇里,介绍了Android的Thread、Looper、Message Queue和Handler四者间之关系。

    兹先复习如下:

 <1> UI thread 通常就是main thread,而Android启动程序时(即诞生Process时)会替它建立一个Message Queue。

 <2>当然需要一个Looper之对象,来管理该Message Queue。

 <3>我们可以诞生Handler之对象来push新讯息到Message Queue里;或者接收Looper(从Message Queue取出)所送来的讯息。

 <4>线程A的Handler对象参考可以传递给别的线程,让别的线程B或C等能送讯息来给线程A(存于A的Message Queue里)。

 <5>线程A的Message Queue里的讯息,只有线程A所属的对象可以处理之。

了解了四者间之关系后,在本篇里,就能来思考如何让主线程与子线程之间互相沟通了。包括,子线程push讯息到主线程的Message Queue里,并触发主线程去执行某项工作(即执行某个函数)。

2.  由别的线程送讯息到主线程的Message Queue(续)

    在上一篇文章里,使用如下程序片段:

// class ac01 extends Activity {
          // ………
       	public void 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
        public void handleMessage(Message msg) {
           tv.setText((String)msg.obj);
    }
}
//------------------------------------------------------	   
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);
  }
  }
}

这个mHandler定义于myThread类别里,而且由子线程执行指令:

      mHandler = new EHandler(mainLooper);

来诞生EHandler之物件;但是这个mHandler确是属于main线程的(用来存取主线程的MessageQueue),所以指令:

          mHandler.sendMessage(m);

是将m丢到主线程的MessageQueue里。

 此外,我们也可以将mHandler定义于ac01类别里。如下程序范例:

//----- Looper_03范例 -----
package com.misoo.kx04;
import android.app.Activity;
import android.content.Context;
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;
public class ac01 extends Activity implements OnClickListener {
private final int WC = LinearLayout.LayoutParams.WRAP_CONTENT; 
private final int FP = LinearLayout.LayoutParams.FILL_PARENT; 
public TextView tv;
    private myThread t;
    private Button btn, btn2;
    EventHandler h;
    Context ctx;
    public void onCreate(Bundle icicle) {
    	        super.onCreate(icicle);
    	        ctx = this;
                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);      
               }
       	public void onClick(View v) {
switch(v.getId()){
case 101:
 h = new EventHandler(Looper.myLooper());
 t = new myThread();
     t.start();
     break;
case 102:
    	finish();
break;
}
}
//------------------------------------------------      
public class EventHandler extends Handler {
            public EventHandler(Looper looper) {
                 super(looper);
             }
             @Override
             public void handleMessage(Message msg) {
                  	 ((Activity)ctx).setTitle((String)msg.obj);
             }
          }
//------------------------------------------------------	   
class myThread extends Thread{
 public void run() {
    String obj = "from myThread";
    Message m = h.obtainMessage(1, 1, 1, obj);
    h.sendMessage(m);
  	 }
  }
}
//------------------------------------------------------

指令:

     h = new EventHandler(Looper.myLooper());

此h是属于main线程的(用来存取主线程的MessageQueue)。在myThread类别里的指令:

     h.sendMessage(m);

虽然是由子线程执行该指令,还是将m丢到主线程的MessageQueue里。于是,子线程所执行的run()函数,就顺利将m丢给主线程(的Message Queue),并触发了主线程去执行handleMessage()函数了。显示出画面如下:

 图1 

 

上述的指令:

  

myLooper = Looper.myLooper();
mainLooper = Looper.getMainLooper();
………
mHandler = new EHandler(mainLooper);
………
mHandler = new EHandler(myLooper);
………


都明显地指明mHandler是负责存取哪一个线程的Message Queue。不过,有时候并不需要特别指明。例如上述的onClick()函数和EventHandler类别,可改写为:


//----- Looper_03aa 范例 -----
// class ac01 extends Activity {
          // ………
      	public void onClick(View v) {
switch(v.getId()){
case 101:
 h = new EventHandler();
 t = new myThread();
     t.start();
     break;
case 102:
      	finish();
break;
}
}
//------------------------------------------------      
public class EventHandler extends Handler {
             @Override
             public void handleMessage(Message msg) {
                  	 ((Activity)ctx).setTitle((String)msg.obj);
             }
          }
//------------------------------------------------------	   
class myThread extends Thread{
 public void run() {
    String obj = "from myThread";
    Message m = h.obtainMessage(1, 1, 1, obj);
    h.sendMessage(m);
  	 }
  }
}

指令:h = new EventHandler();

就等于:h = new EventHandler(Looper.myLooper());

它建立了目前线程(Current Thread)的EventHandler对象。于此,是由main线程执行此指令的,所以此EventHandler对象是用来存取main线程的Message Queue。

上述程序将handleMessage()定义于EventHandler类别内,也可以直接定义于ac01类别之内。于是上述程序,也相当于:

//----- Looper_03bb 范例 -----
// class ac01 extends Activity {
          // ………
      	public void onClick(View v) {
switch(v.getId()){
case 101:
 h = new Handler(){
 public void handleMessage(Message msg) {
                  	 ((Activity)ctx).setTitle((String)msg.obj);
             }};
          t = new myThread();
     t.start();
     break;
case 102:
    	finish();
break;
}
}
//------------------------------------------------------	   
class myThread extends Thread{
 public void run() {
    String obj = "from myThread...";
    Message m = h.obtainMessage(1, 1, 1, obj);
    h.sendMessage(m);
  	 }
  }
}

其执行结果是一样的。

Anrdroid 的Message Queue(3/3)

3.  由主线程送讯息给子线程(续)

    上述范例里,是由子线程丢讯息给主线程。本节将介绍如何从主线程丢讯息给子线程。其方法是:当子线程执行run()函数时,就诞生一个子线程的Handler物件。之后,当主线程执行ac01::onClick()函数时,就藉由此Handler对象参考而push讯息给子线程。例如下述范例:

//----- Looper_04范例 -----
package com.misoo.kx04;
import android.app.Activity;
import android.content.Context;
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;
public class ac01 extends Activity implements OnClickListener {
private final int WC = LinearLayout.LayoutParams.WRAP_CONTENT; 
private final int FP = LinearLayout.LayoutParams.FILL_PARENT; 
public TextView tv;
    private myThread t;
    private Button btn, btn2;
    private Handler h;
    private Context ctx;
    public void onCreate(Bundle icicle) {
    	        super.onCreate(icicle);
    	        ctx = this;
                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);  
                //------------------------
                t = new myThread();
   	         t.start();
          }
       	public void onClick(View v) {
switch(v.getId()){
case 101:
 String obj = "mainThread";
 	 Message m = h.obtainMessage(1, 1, 1, obj);
 	 h.sendMessage(m);
     break;
case 102:
    h.getLooper().quit();
    	finish();
break;
}
}
//------------------------------------------------      
public class EventHandler extends Handler {
          public EventHandler(Looper looper) {
  	                 super(looper);
          }
           @Override
       	    public void handleMessage(Message msg) {
       	         	 ((Activity)ctx).setTitle((String)msg.obj);
            }
        }
//------------------------------------------------      
class myThread extends Thread{
 public void run() {
     Looper.prepare();
     h = new Handler(){
           public void handleMessage(Message msg) {
        	    EventHandler ha = new 
                     EventHandler(Looper.getMainLooper());
                String obj = (String)msg.obj + ", myThread";
   	  	    Message m = ha.obtainMessage(1, 1, 1, obj);
   	  	    ha.sendMessage(m);
     }
     };
 Looper.loop();
  	 }
  }
}

当子线程执行run()函数时,诞生一个主线程的EventHandler对象,并且藉之而push讯息给主线程了。就进行了两个线程之间的互相交换讯息,也是两个函数或对象间之交换讯息。此程序输出画面为:

图2

     上述范例定义了Thread的子类别。也可以将子线程包含到Runnable类别里,如下:

//----- Looper_04aa范例 -----
package com.misoo.kx04;
import android.app.Activity;
import android.content.Context;
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;
public class ac01 extends Activity implements OnClickListener {
private final int WC = LinearLayout.LayoutParams.WRAP_CONTENT; 
private final int FP = LinearLayout.LayoutParams.FILL_PARENT; 
public TextView tv;
    private RR r;
    private Button btn, btn2;
    private Handler h;
    private Context ctx;
    public void onCreate(Bundle icicle) {
    	        super.onCreate(icicle);
    	        ctx = this;
                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);  
                //------------------------
                r = new RR();
   	 }
       	public void onClick(View v) {
switch(v.getId()){
case 101:
 String obj = "mainThread";
 	 Message m = h.obtainMessage(1, 1, 1, obj);
 	 h.sendMessage(m);
     break;
case 102:
    h.getLooper().quit();
    	finish();
break;
}
}
//------------------------------------------------      
public class EventHandler extends Handler {
          public EventHandler(Looper looper) {
  	                 super(looper);
          }
           @Override
       	    public void handleMessage(Message msg) {
       	         	 ((Activity)ctx).setTitle((String)msg.obj);
            }
        }
//------------------------------------------------      
public class RR implements Runnable {
public RR() {
    Thread aThread = new Thread(null, this, "RR");
    aThread.start(); 
}
    public void run() {
     Looper.prepare();
     h = new Handler(){
           public void handleMessage(Message msg) {
        	    EventHandler ha = new EventHandler(Looper.getMainLooper());
                String obj = (String)msg.obj + ", myThread";
   	  	    Message m = ha.obtainMessage(1, 1, 1, obj);
   	  	    ha.sendMessage(m);
     }
     };
 Looper.loop();
  	 }
  }
}

  

当子线程执行到RR()函数时,诞生一个子线程,并执行run()函数,就将讯息丢给主线程了。

4. 结语:

     Message Loop的用途很广。请你参阅高焕堂所写的Android系列书籍,尤其是其中的第4本书:<<Android设计招式之美>> 。





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值