Android消息队列

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消息处理机制对于应用程序开发非常重要,也可以让你对线程同步有更加深刻的认识。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值