http://blog.csdn.net/program035/article/details/7165583
一. 使用Handler的流程
1,创建Handler对象
创建Handler的两种方法
使用无参构造函数创建;继承Handler类,并实现handlerMessage方法
2,发送消息
在事件监听器中调用Handler的post方法,将要执行的线程对象添加到线程队列中,将要执行的操作写在线程对象的run方法中,一般是一个Runnable对象,复写其中的run方法
Handler对象管理了两个队列,一个是线程队列(post),一个是消息队列(sendMessage)
注:如果想要这个流程一直执行的话,可以在run方法内部执行postDelayed或者post方法,再将该线程对象添加到消息队列中,重复执行。想要线程停止执行,调用Handler对象的removeCallbacks(Runnable r) 方法从线程队列中移除线程对象,使线程停止执行。Handler为Android提供了一种异步消息处理机制,当向消息队列中发送消息(sendMessage)后就立即返回,而从消息队列中读取消息时会阻塞,其中从消息队列中读取消息时会执行Handler中的public void handleMessage(Message msg) 方法,因此在创建Handler时应该使用匿名内部类重写该方法,在该方法中写上读取到消息后的操作,使用Handler的obtainMessage() 来获得消息对象。
3,消息处理
Message
即Message,可以传递一些信息,可以使用arg1.arg2,Object传递一些整形或者对象,还可以使用Message对象的setData(Bundle bundle)来讲Bundle对象传递给新创建的线程,新创建的线程在执行handleMessage(Message msg)时可以从message中利用getData()提取出Bundle对象来进行处理。
Handler与线程的关系
Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue
在main thread中执行消息处理过程
使用Handler的post方法将Runnable对象放到Handler的线程队列中后,该Runnable的执行其实并未单独开启线程,而是仍然在当前Activity线程中执行的,Handler只是调用了Runnable对象的run方法
创建新的线程来执行消息处理过程
首先生成一个HandlerThread对象,实现了使用Looper来处理消息队列的功能,这个类由Android应用程序框架提供
HandlerThread handlerThread = new HandlerThread("handler_thread");
在使用HandlerThread的getLooper()方法之前,必须先调用该类的start();
handlerThread.start();
根据这个HandlerThread对象得到其中的Looper对象。
创建自定义的继承于Handler类的子类,其中实现一个参数为Looper对象的构造方法,方法内容调用父类的构造函数即可。
使用第三步得到的Looper对象创建自定义的Handler子类的对象,再将消息(Message)发送到该Handler的消息队列中,Handler复写的handleMessage()将会执行来处理消息队列中的消息。
4,代码示例
public class HandlerTest2 extends Activity {
@Override
<span style="white-space:pre"> </span>protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//打印了当前线程的ID
System.out.println("Activity-->" + Thread.currentThread().getId());
//生成一个HandlerThread对象,实现了使用Looper来处理消息队列的功能,这个类由Android应用程序框架提供
HandlerThread handlerThread = new HandlerThread("handler_thread");
//在使用HandlerThread的getLooper()方法之前,必须先调用该类的start();
handlerThread.start();
MyHandler myHandler = new MyHandler(handlerThread.getLooper());
Message msg = myHandler.obtainMessage();
//将msg发送到目标对象,所谓的目标对象,就是生成该msg对象的handler对象
Bundle b = new Bundle();
b.putInt("age", 20);
b.putString("name", "Jhon");
msg.setData(b);
msg.sendToTarget();
}
class MyHandler extends Handler{
public MyHandler(){
}
public MyHandler(Looper looper){
super(looper);
}
@Override
public void handleMessage(Message msg) {
Bundle b = msg.getData();
int age = b.getInt("age");
String name = b.getString("name");
System.out.println("age is " + age + ", name is" + name);
System.out.println("Handler--->" + Thread.currentThread().getId());
System.out.println("handlerMessage");
}
}
}
二. Android消息队列--异步消息处理
异步消息处理
简介:
对于普通的线程来说,执行完run()方法内的代码后线程就结束了。而异步消息处理线程是指:线程启动后会进入一个无限循环体之中,每执行一次,从线程内部的消息队列中取出一个消息,并回调相应的消息处理函数,执行完一个消息后则继续循环。如果消息队列为空,线程会暂停(一般也就是我们调用休眠方法),直到消息队列中又新的消息。
从上面的描述可以看出,异步消息处理其实就是一种线程机制,只不过这种机制用的上的地方非常多,最后就单独提炼了“异步消息处理”这个名词。
异步消息处理的使用情况:
一般情况下,如果任务具有以下两个特点,就可以使用异步消息处理机制:
1.任务常驻内存(编程中体现就是run()方法中是无限循环),比如用于处理用户事件的任务。
2.任务需要根据外部传递的消息做不同的操作。
通用的实现异步消息处理机制的方式:
1.每个异步线程内部包含一个消息队列,用来缓存消息。
2.线程的执行中使用while(true)进行无限循环,循环体中从消息队列取出消息,并根据消息的来源,回调相应的消息处理函数(从这里可以看出:异步消息处理,消息的具体处理并不是异步消息处理机制负责的,异步消息处理机制只是负责转发消息给处理函数)
3.其他外部线程可以向本线程的消息队列发送消息,由于有两个或以上的线程访问消息队列,那么,消息队列内部的读写操作必须进行加锁。
Android中异步消息处理的实现方式:
在线程内部有一个或多个Handler对象,外部线程通过该Handler对象的引用向本线程发送异步消息,消息通过Handler对象加入消息队列(MessageQueue)。线程内部只有一个MessageQueue对象,线程的run()方法从MessageQueue中读取消息,并回调Handler对象中的回调函数handleMessage()处理消息。
在编程中应该注意的问题:
1.由于异步消息处理机制的一个重要环节是MessageQueue,所以,在发送消息之前,必须确定MessageQueue已经创建。在android中,给应用程序员提供的创建MessageQueue的接口是:Looper.prepare()。
2.由于异步消息处理机制是一个循环线程,而循环的启动也是由程序员控制的,所以,在你要处理消息之前,应该启动循环。在android中,给应用程序员提供的启动循环的接口是:Looper.loop()。
3.在activity中使用Handler发送,处理消息时,并没有上面两步,其实是系统帮我们做了的。在创建Activity之前,系统就会为Activity创建一个异步消息处理线程。
三. Android消息队列--多线程与消息处理
原文地址:http://www.e800.com.cn/articles/2011/0720/491429_2.shtml
Android系统中Looper负责管理线程的消息队列和消息循环,具体实现请参考Looper的源码。 可以通过Loop.myLooper()得到当前线程的Looper对象,通过Loop.getMainLooper()可以获得当前进程的主线程的Looper对象。
前面提到Android系统的消息队列和消息循环都是针对具体线程的,一个线程可以存在(当然也可以不存在)一个消息队列 和一个消息循环(Looper),特定线程的消息只能分发给本线程,不能进行跨线程,跨进程通讯。但是创建的工作线程默认是没有消息循环和消息队列的,如果想让该线程具有消息队列和消息循环,需要在线程中首先调用Looper.prepare()来创建消息队列,然后调用Looper.loop()进入消息循环。如下例所示:
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();
}
}
这样你的线程就具有了消息处理机制了,在Handler中进行消息处理。
Activity是一个UI线程,运行于主线程中,Android系统在启动的时候会为Activity创建一个消息队列和消息循环(Looper)。详细实现请参考ActivityThread.java文件。
Handler的作用是把消息加入特定的(Looper)消息队列中,并分发和处理该消息队列中的消息。构造Handler的时候可以指定一个Looper对象,如果不指定则利用当前线程的Looper创建。详细实现请参考Looper的源码。
一个Activity中可以创建多个工作线程或者其他的组件,如果这些线程或者组件把他们的消息放入Activity的主线程消息队列,那么该消息就会在 主线程中处理了。因为主线程一般负责界面的更新操作,并且Android系统中的weget不是线程安全的,所以这种方式可以很好的实现Android界 面更新。在Android系统中这种方式有着广泛的运用。
那么另外一个线程怎样把消息放入主线程的消息队列呢?
答案是通过Handle对象,只要Handler对象以主线程的Looper创建,那么调用 Handler的sendMessage等接口,将会把消息放入队列都将是放入主线程的消息队列。并且将会在Handler主线程中调用该handler 的handleMessage接口来处理消息。
这里面涉及到线程同步问题,请先参考如下例子来理解Handler对象的线程模型:
package com.simon;
import android.app.Activity;
import android.os.Bundle;
import android.os.Message;
import android.util.Log;
import android.os.Handler;
public class MyHandler extends Activity {
static final String TAG = "Handler";
Handler h = new Handler(){
public void handleMessage (Message msg)
{
switch(msg.what)
{
case HANDLER_TEST:
Log.d(TAG, "The handler thread id = " + Thread.currentThread().getId() + "\n");
break;
}
}
};
static final int HANDLER_TEST = 1;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "The main thread id = " + Thread.currentThread().getId() + "\n");
new myThread().start();
setContentView(R.layout.main);
}
class myThread extends Thread
{
public void run()
{
Message msg = new Message();
msg.what = HANDLER_TEST;
h.sendMessage(msg);
Log.d(TAG, "The worker thread id = " + Thread.currentThread().getId() + "\n");
}
}
}
在这个例子中我们主要是打印,这种处理机制各个模块的所处的线程情况。如下是我的机器运行结果:
09-10 23:40:51.478: DEBUG/Handler(302): The main thread id = 1 09-10 23:40:51.569: DEBUG/Handler(302): The worker thread id = 8 09-10 23:40:52.128: DEBUG/Handler(302): The handler thread id = 1
我们可以看出消息处理是在主线程中处理的,在消息处理函数中可以安全的调用主线程中的任何资源,包括刷新界面。工作线程和主线程运行在不同的线程中,所以必须要注意这两个线程间的竞争关系。
上例中,你可能注意到在工作线程中访问了主线程handler对象,并在调用handler的对象向消息队列加入了一个消息。这个过程中会不会出现消息队 列数据不一致问题呢?答案是handler对象不会出问题,因为handler对象管理的Looper对象是线程安全的,不管是加入消息到消息队列和从队 列读出消息都是有同步对象保护的,具体请参考Looper.java文件。上例中没有修改handler对象,所以handler对象不可能会出现数据不 一致的问题。
通过上面的分析,我们可以得出如下结论:
1、如果通过工作线程刷新界面,推荐使用handler对象来实现。
2、注意工作线程和主线程之间的竞争关系。推荐handler对象在主线程中构造完成(并且启动工作线程之后不要再修改之,否则会出现数据不一致),然后在工作线程中可以放心的调用发送消息SendMessage等接口。
3、除了2所述的hanlder对象之外的任何主线程的成员变量如果在工作线程中调用,仔细考虑线程同步问题。如果有必要需要加入同步对象保护该变量。
4、handler对象的handleMessage接口将会在主线程中调用。在这个函数可以放心的调用主线程中任何变量和函数,进而完成更新UI的任务。
5、Android很多API也利用Handler这种线程特性,作为一种回调函数的变种,来通知调用者。这样Android框架就可以在其线程中将消息发送到调用者的线程消息队列之中,不用担心线程同步的问题。
深入理解Android消息处理机制对于应用程序开发非常重要,也可以让你对线程同步有更加深刻的认识。