Android开发之消息处理机制(二)——消息循环

 

Android开发之消息处理机制(二)——消息循环

/*

 *  Android开发之消息处理机制(二)——消息循环

 *  北京Android俱乐部群:167839253  

 *  Created on: 2011-9-1

 *  Author: blueeagle

 *  Email: [email protected]

 */

 

       先介绍一下如何在Android应用程序中使用后台线程和UI线程。

创建Android友好的后台线程时,最灵活的方式就是创建Handler子类的一个实例。每个Activity仅需要一个Handler对象,而且不需要手动注册他。

       后台线程可以与Handler通讯,Handler将在Activity的UI线程上执行自己所有的工作。这一点非常重要。因为UI的更改,只能发生在Activity的UI线程上。

       有两种与Handler通信的方式:消息和Runnable对象。

       关于消息:

       要向Handler发送一个Message,首先调用obtainMessage()从池中获取Message对象。obtainMessage()对象有许多特点,允许创建空的Message对象或填充了消息标示符和参数的对象。需要的Handler处理过程越复杂,就越需要将数据放在Message中,以帮助Handler区分不同的事件。

       然后可以通过消息队列将Message发送给Handler,此时需要使用sendMessage…()系列方法中的一个方法。

       sendMessage();立即将消息放在队列中。

       sendMessageAtFrontOfQueue();立即将消息放在队列中,并将其放在消息队列的最前面,这样该消息就会具有比所有其他消息更高的优先级。

       sendMessageAtTime();在规定的时间将消息放在队列中,这个时间用ms表示,基于系统正常工作时间。(SystemClock.uptimeMillis())

       sendMessageDelayed();在一段延迟之后将消息放在队列中,延迟用ms表示。

       要处理这些消息,Handler需要实现handleMessage(),将使用出现在消息队列中的每个消息来调用handleMessage()。此处Handler可以根据需要更新UI。但是,它仍然应该迅速完成此工作,因为在它完成之前,其他UI工作会暂停。

       关于Runnable :

       如果不愿意使用Message对象,也可以将Runnable对象传递给Handler,Handler将在ActivityUI线程上运行这些Runnable对象。Handler提供了一组post…()方法来传入Runnable对象供最终处理。

       对Android的消息循环机制的理解,可以仿照Windows消息循环的机制进行。

每个Handler都会和一个线程和线程的message queue关联起来,此时你可以传递messages 和 runnable对象到message queue中。后面可以从message queue中拿出该对象并且执行。Handler, Message queue这两个对象之间的交互,就涉及到了Looper这个东西。

关于Handler,Looper,Message Queue三者之间的关系,如下图所示:

Android开发之消息处理机制(二)——消息循环_第1张图片

Handler有很多构造函数

Handler在无参数的构造方法中调用Looper.myLooper()方法,里面就是从当前线程里面获取一个Looper的同时一并初始化MessageQueue,并且从中得到looper的MessageQueue。可以看出Handler就是Looper和MessageQueue的管理者和调度者。

       其中最重要的是sendMessageAtTime方法,当你往Handler中发送Message消息的时候,从代码看出他自己并不去处理Message,而是交给了MessageQueue,由queue.enqueueMessage来处理。

       Looper代码中有一个prepare()方法,通过ThreadLocal实现一个线程只有一个Looper。

Handler的消息发送

Handler是一个核心,负责message的创建,获得,处理。Handler的sendMessage()方法最终其实就是调用了queue.enqueueMessage(msg, uptimeMillis);把消息放入message queue中。Looper通过Looper.loop()检测到有消息并将消息广播后,Handler又负责接收到此消息并调用handleMessage进行处理接下来的事情。Message的创建一般都是通过如下代码建立的,把Handler传进去。

     public final Message obtainMessage()

               {

                 return Message.obtain(this);

       }

这种工作方式如下图所示:

Android开发之消息处理机制(二)——消息循环_第2张图片

Message在里面是一个链表的结构。在这个方法中,会首先去MessagePool(消息回收站)去找Message,如果有被回收的Message,则会将这个Message取出来进行再利用。如果没有被回收的,这时系统才会真正new一个Message对象出来当然MessagePool不是无限大的,它最多只能保存十条回收的消息,多出来的就直接删除了。

至此,我们看到,一个Message经由Handler创建、发送,MessageQueue的入队,Looper的抽取,又再一次地回到Handler进行处理。而绕的这一圈,也正好帮助我们将同步操作变成了异步操作。

在主线程(UI线程)里,如果创建Handler时不传入Looper对象,那么将直接使用主线程(UI线程)的Looper对象(系统已经帮我们创建了);在其它线程里,如果创建Handler时不传入Looper对象,那么,这个Handler将不能接收处理消息。在创建Handler之前,为该线程准备好一个Looper(Looper.prepare),然后让这个Looper跑起来(Looper.loop),抽取Message,这样,Handler才能正常工作。

因此,Handler处理消息总是在创建Handler的线程里运行。

下面根据上述理论知识,来举几个例子:

例1:

       在UI线程中调用Handler的Post方法。代码如下:

/*
 *  Android开发之消息处理机制(二)——消息循环
 *  MessageCircle.java  
 *  Created on: 2011-9-2
 *  Author: blueeagle
 *  Email: [email protected]
 */
package blueeagle.com;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;

public class MessageCircle extends Activity {
    /** Called when the activity is first created. */
	private Handler myHandler = new Handler();
	private Runnable myRunnable = new Runnable(){
		@Override
		public void run(){
			try{
				Thread.sleep(1000);
			}
			catch(InterruptedException e){
				e.printStackTrace();
			}
			System.out.println("Current Runnable Thread ID:"+Thread.currentThread().getId());
		}
	};
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);        
        System.out.println("Current Activity Thread ID:"+Thread.currentThread().getId());
        myHandler.post(myRunnable);
    }    
}


 

程序运行结果如下图:

在这个程序中,我们New了一个没有参数的Handler对象myHandler,这个myHandler自动与当前运行程序相关联,也就是说,这个myHandler将与当前运行的线程使用同一个消息队列,并且可以处理该队列中的消息。

在程序代码中,这个myHandler向消息队列中post了一个myRunnable对象。在myRunnable的run方法中,打印当前线程的ID。由结果得知,我们New出来的这个myHandler是把消息或者Runnable对象送到当前的线程中的。这里我们没有调用线程的start()函数,因此也就不会去创建一个新的线程。而是在原有线程的内部,直接调用run()方法。因此输出的ID是相同的。

那么如何创建新的线程呢?在Android开发之消息处理机制(一)中已经知道如何去创建一个新的线程,这里继续举一个例子来进行比较说明。

例2:

首先需要创建一个线程,这个线程可以是自己创建的线程,也可以是HandlerThread,这个线程需要继承于Thread类或者实现Runnable接口。其次需要有一个Handler,这个Handler可以是Handler也可以继承于Handler并且需要有一个有Looper参数的构造函数。然后就可以进行消息的分发执行了。

具体示例代码如下:

/*
 *  Android开发之消息处理机制(二)——消息循环
 *  MessageCircle.java  
 *  Created on: 2011-9-2
 *  Author: blueeagle
 *  Email: [email protected]
 */

package blueeagle.com;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.widget.TextView;

public class MessageCircle extends Activity {
    public class MyOwnThread extends Thread{
    	int sleepSpan = 2000;//休眠时间
    	boolean flag;// 线程执行标志位
    	//----------------------------------//
    	//构造方法,初始化类的主要成员变量
    	public MyOwnThread(){
    		this.flag = true;
    	}
    	//----------------------------------//
    	//方法,线程执行方法
    	@Override
    	public void run(){
    		TextView myTextView = (TextView)findViewById(R.id.mytextview);
    		Looper.prepare();
    		int i = 1;
    		while(flag){
    			try{
    				Thread.sleep(sleepSpan);//休眠一段时间
    	    		System.out.println("Current MyOwnThread Thread ID: "+Thread.currentThread().getId());
    	    		myTextView.setText(i);
    	    		i++;
    			}
    			catch(Exception e){
    				e.printStackTrace();//捕获异常并打印
    			}
    		}    			
    		Looper.loop();
    	}
	}
	public class MyHandler extends Handler{
    	public MyHandler(){}
    	public MyHandler(Looper looper){
    		super(looper);
    	}
    	@Override
    	public void handleMessage(Message msg){
    		System.out.println("Current MyHandle Thread ID: "+Thread.currentThread().getId());
    		TextView myTextView = (TextView)findViewById(R.id.mytextview);
       		int i = 1;
    		while(true){
    			try{
    				Thread.sleep(1000);//休眠一段时间
    	    		System.out.println("Current MyHandle Thread ID: "+Thread.currentThread().getId());
    	    		myTextView.setText("i");
    	    		i++;
    			}
    			catch(Exception e){
    				e.printStackTrace();//捕获异常并打印
    			}
    		}    	
    	}
	}
	/** Called when the activity is first created. */
	private MyHandler myHandler = null;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);        
        System.out.println("Current Activity Thread ID:"+Thread.currentThread().getId());
        //myHandler.post(myRunnable);
        HandlerThread myThread = new HandlerThread("myThread");//生成一个HandlerThread对象,使用Looper来处理消息队列。
        myThread.start();//启动这个线程
        myHandler = new MyHandler(myThread.getLooper());//将一个线程绑定到myHandler这个对象上。
        Message msg = myHandler.obtainMessage();//从myHandler这个对象中获取消息
        msg.sendToTarget();//将msg对象发送给目标的Handler
        
        //下面制造一个自己的线程
        MyOwnThread myOwnThread = new MyOwnThread();
        myOwnThread.start();
    }    
}


 

关于线程更新UI的方法,在(一)中已经说过。下面利用一个示例将消息循环过程进行展示,进而实现音乐播放,UI线程中更新歌词的例子来说明消息处理机制。例子较简单,没有实现歌词同步等内容。只是为了更深刻的理解线程运行情况。

按照上述理论:代码如下:

/*
 *  Android开发之消息处理机制(二)——消息循环
 *  MusicPlayer.java  
 *  Created on: 2011-9-3
 *  Author: blueeagle
 *  Email: [email protected]
 */

package blueeagle.com;

import android.app.Activity;
import android.media.MediaPlayer;
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.TextView;

public class MusicPlayer extends Activity {
    /** Called when the activity is first created. */
	Button myButton;
	TextView myTextView;
	private UIUpdateThread myUpdateThread = new UIUpdateThread(); //定义一个自己的UI更新的线程类
	MyHandler mHandler = new MyHandler();//定义自己的一个Handler类
	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        myButton = (Button)findViewById(R.id.myButton);
        myTextView = (TextView)findViewById(R.id.myTextView);
        myButton.setOnClickListener(new OnClickListener(){
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub 
                System.out.println("主线程运行ID:"+Thread.currentThread().getId());
				new Thread(myUpdateThread).start();//起一个自己定义的线程
			}        	
        });
    }           
	class MyHandler extends Handler{//继承Handler类时,必须重写handleMessage方法 
		public MyHandler(){
			
		}
		public MyHandler(Looper l){
			super(l);
			}
		@Override
		public void handleMessage(Message msg) {//执行接收到的通知,此时执行的顺序是按照队列进行,即先进先出 
			myTextView.setText(msg.toString());
			System.out.println("Current Handler Thread ID:"+Thread.currentThread().getId());
			} 
		}//该线程将会在单独的线程中运行
	
class UIUpdateThread implements Runnable {  
int i=1;
        public void run() {  
            while (i<11) {  
                Message msg = mHandler.obtainMessage();
                mHandler.sendMessage(msg);
                System.out.println("新线程运行ID:"+Thread.currentThread().getId());
				i++;
                try {  
                    Thread.sleep(4000);  
                } catch (InterruptedException e) {  
                    // TODO Auto-generated catch block   
                    e.printStackTrace();  
                }  
            }  
        }  
    }
}

关于简单的音乐播放的源码如下:

/*
 *  Android开发之消息处理机制(二)——消息循环
 *  MusicPlayer.java  
 *  Created on: 2011-9-3
 *  Author: blueeagle
 *  Email: [email protected]
 */

package blueeagle.com;

import android.app.Activity;
import android.media.MediaPlayer;
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.TextView;

public class MusicPlayer extends Activity {
    /** Called when the activity is first created. */
	private MediaPlayer mp;
	Button myButton;
	TextView myTextView;
	private UIUpdateThread myUpdateThread = new UIUpdateThread(); //定义一个自己的UI更新的线程类
	MyHandler mHandler = new MyHandler();//定义自己的一个Handler类
	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        myButton = (Button)findViewById(R.id.myButton);
        myTextView = (TextView)findViewById(R.id.myTextView);
        myButton.setOnClickListener(new OnClickListener(){
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				mp = MediaPlayer.create(MusicPlayer.this, R.raw.yinweiaiqing);
				mp.start();
                System.out.println("主线程运行ID:"+Thread.currentThread().getId());
				new Thread(myUpdateThread).start();

			}        	
        });
    }           
	class MyHandler extends Handler{//继承Handler类时,必须重写handleMessage方法 
		public MyHandler(){
		}
		public MyHandler(Looper l){
			super(l);
			}
		@Override
		public void handleMessage(Message msg) {//执行接收到的通知,此时执行的顺序是按照队列进行,即先进先出 
			myTextView.setText(msg.obj.toString());
			System.out.println("Current Handler Thread ID:"+Thread.currentThread().getId());
			} 
		}//该线程将会在单独的线程中运行
    class UIUpdateThread implements Runnable {  
    	int i = 0;
    	String myString[] = {
    			"这是一个实时播放的线程操作... ...",
    			"[00:08.79]《因为爱情》",
    			"[00:19.46]E 给你一张过去的CD",
    			"[00:28.68]听听那时我们的爱情",
    			"[00:34.12]有时会突然忘了",
    			"[00:37.48]我还在爱著你",
    			"[00:44.98]F 再唱不出那样的歌曲",
    			"[00:50.48]听到都会红著脸躲避",
    			"[00:55.83]虽然会经常忘了",
    			"[00:59.33]我依然爱著你",
    			"[01:05.49]F 因为爱情 不会轻易悲伤",
    			"[01:05.49]F 因为爱情 不会轻易悲伤",
    			"[01:12.09]E 所以一切都是幸福的模样",
    			"[01:17.24]F 因为爱情 简单的生长",
    			"[01:22.24]E 依然随时可以为你疯狂",
    			"[01:27.21]F 因为爱情 怎麼会有沧桑",
    			"[01:34.30]E 所以我们还是年轻的模样",
    			"[01:38.90]F 因为爱情 在那个地方",
    			"[01:44.32]E 依然还有人在那里游荡",
    			"[01:48.91]E&F 人来人往",
    			"[02:11.57]F 再唱不出那样的歌曲",
    			"[02:17.70]听到都会红著脸躲避",
    			"[02:23.14]虽然会经常忘了",
    			"[02:26.26]E&F 我依然爱著你",
    			"[02:32.60]F 因为爱情 不会轻易悲伤",
    			"[02:39.22]E 所以一切都是幸福的模样",
    			"[02:44.98]F 因为爱情 简单的生长",
    			"[02:49.36]E 依然随时可以为你疯狂",
    			"[02:54.38]F 因为爱情 怎麼会有沧桑",
    			"[03:00.94]E 所以我们还是年轻的模样",
    			"[03:06.04]F 因为爱情 在那个地方",
    			"[03:11.63]E 依然还有人在那里游荡",
    			"[03:17.04]E&F 人来人往",
    			"[03:21.98]E 给你一张过去的CD",
    			"[03:28.58]听听那时我们的爱情",
    			"[03:33.48]F 有时会突然忘了",
    			"[03:36.94]E&F 我还在爱著你"};
        public void run() {  
            while (mp.isPlaying()) {  
				Message msg = mHandler.obtainMessage(1, 1, 1, myString[i]);
                mHandler.sendMessage(msg);
                System.out.println("新线程运行ID:"+Thread.currentThread().getId());
                try {  
                    Thread.sleep(4000);  
                    i++;
                	if(i == myString.length){
                		i=myString.length-1;
                	} 
                } catch (InterruptedException e) {  
                    // TODO Auto-generated catch block   
                    e.printStackTrace();  
                }  
            }  
        }  
    }
}

综上,基本了解了Android下的关于线程,消息循环的一些概念,还需要深入了解Looper如何运用在程序中等。


 

你可能感兴趣的:(Android开发之消息处理机制(二)——消息循环)