Android中消息系统模型和Handler Looper

作为Android中大量使用的Handler,结合Thread使其具有众多的使用形式和方法,

让我一时感觉这个东西有些玄乎,不明所以然,这到底是一个什么样的存在呢?通过网上

资料和源码的学习,这个Handler也差不多弄清楚了,现在总结下这个学习结果。

一 Handler作用和概念

通过官方文档了解到Handler的大致概念是:

  Handler能够让你发送和处理消息,以及Runnable对象;每个Handler对象对应一个Thread和

Thread的消息队列。当你创建一个Handler时,它就和Thread的消息队列绑定在一起,然后就可以

传递消息和runnable对象到消息队列中,执行消息后就从消息队列中退出。

  Handler的作用就是:调度消息和runnable对象去被执行;使动作在不同的线程中被执行。

  当一个应用程序中进程被创建时,它的主线程专门运行消息队列(messageQueue),去管

理顶层的应用程序相关的对象如:activity,broadcastReceiver,windows等,你可以创建你

的Thread,和主线程进行交互——通过Handler,交互的方法就是通过post或者sendMessage。

但是在你的新线程中,给定的Message或者Runnable,会在适当的时候的被调度和处理。

(即不会被立即处理——阻塞式)。

  这是官方文档中对Handler描述的大致意思(英文比较烂翻译不定正确)。

从这些文档中我们大概了解到handler干了些什么:

  •   运行在某个线程上,共享线程的消息队列;
  •   接收消息、调度消息,派发消息和处理消息;
  •   实现消息的异步处理;

基本上就是和消息有关,那么这实际上是在干什么呢?

  ——建立消息处理模型/系统。

  

  要学习Handler,看到肯定是和消息有关,可能还是需要先熟悉一下消息系统的构成和简单原理。

下面就先学习一下消息系统的基本原理。

二 消息系统的基本原理和构成

从一般的消息系统模型的建立大致构成以下几个部分:

    l  消息原型

    l  消息队列

    l  发送消息

    l  消息循环

    l  消息获取

    l  消息派发

    l  消息处理

大致模型图如下:

    

  消息系统模型一般会包括以上七个部分(消息原型,消息队列,消息发送,消息循环,消息获取,

消息派发,消息处理)。实际上的核心是消息队列和消息循环,其余部分都是围绕这两部分进行的。

  从前面文档的分析中我们知道Handler就是用来建立消息处理的系统模型,那么和这里基本消息

系统模型相比,那么Handler又是如何囊括这七个部分的呢?

  在Android中对这六个部分进行了抽象成四个独立的部分:

    Handler,Message,MessageQueue,Looper;

  •   Message就是消息原型,包含消息描述和数据,
  •   MessageQueue就是消息队列,
  •   Looper完成消息循环
  •   Handler就是驾驭整个消息系统模型,统领Message,MessgeQueue和Looper;

  

  Handler能够实现消息系统模型,那么具体是如何进行工作的呢,下面探究一下这其中工作的方法和原理。

三 Handler工作原理分析

  要了解Handler工作原理,先看一下这个系统模型具体组成的层次结构框架是个什么样的。

      

Looper

  实现Thread的消息循环和消息派发,缺省情况下Thread是没有这个消息循环的既没有Looper;

需要主动去创建,然后启动Looper的消息循环loop;与外部的交互通过Handler进行;

MessageQueue

  消息队列,由Looper所持有,但是消息的添加是通过Handler进行;

  

  消息循环和消息队列都是属于Thread,而Handler本身并不具有Looper和MessageQueue;

但是消息系统的建立和交互,是Thread将Looper和MessageQueue交给某个Handler维护建立消息系统模型。

  所以消息系统模型的核心就是Looper。消息循环和消息队列都是由Looper建立的,

而建立Handler的关键就是这个Looper。

  一个Thread同时可以对应多个Handler,一个Handler同时只能属于一个Thread。Handler属于哪个

Thread取决于Handler在那个Thread中建立。

  在一个Thread中Looper也是唯一的,一个Thread对应一个Looper,建立Handler的Looper来自哪个Thread,

Handler属于哪个Thread。

  故建立Thread消息系统,就是将Thread的Looper交给Handler去打理,实现消息系统模型,完成消息的异步处理。

  

Handler与Thread及Looper的关系可以用下面图来表示:

    

Handler并不等于Thread,必须通过Thread的Looper及其MessageQueue,

用来实现Thread消息系统模型,依附于Thread上。

在线程建立Handler时:

  使Handler满足消息系统需要的条件,将Thread中的Looper和MessageQueue交给Handler来负责维护。

在线程中建立Handler,需要做以下工作:

  l  获取Thread中的Looper交给Handler的成员变量引用维护;

  l  通过Looper获取MessageQueue交给Handler的成员变量引用维护。

  那么消息系统模型建立完成之后,按照消息系统运行,

从Handler来看就是发送消息派发消息,与此线程消息系统的交互都由Handler完成。

消息发送和派发接口:

  l  post(runnable)消息,Runnable是消息回调,经过消息循环引发消息回调函数执行;

  l  sendMessage(Message)消息,经过消息循环派发消息处理函数中处理消息;

  l  dispatchMessage       派发消息,若是post或带有回调函数则执行回调函数,否则执行

      消息处理函数Handler的handleMessage(通常派生类重写)。

  以上就是Handler如何实现Thread消息系统模型的大致介绍。

下面将具体分析是如何实现消息系统模型运行的。

四 Handler实现流程分析

  我们知道Handler就是一个消息系统的外壳,属于某个Thread并包装了Thread的Looper

及其MessageQueue;与外部进行交互(同一个线程内或者线程之间),接收派发和处理消息,

消息系统模型的核心是Looper。

  下面看看Handler是如何建立跑起来的,以msg消息为例,runnable实质是一样。

1 Handler的建立



  Handler唯一属于某个Thread,在某个Thread中建立Handler时,需要获取Thread的Looper

及其MessageQueue,建立Handler关键是Looper的来源。

  Handler提供了好几个构造函数但其本质一致:

由外部传入Looper:当前线程或其他线程

  public Handler(Looper looper) {        //初始化构建消息系统参数
              mLooper = looper;
              mQueue = looper.mQueue;
              mCallback = null;
  }     

从当前线程获取:由创建Handler的Thread决定

  public Handler() {        //初始化构建消息系统参数
              mLooper = Looper.myLooper();
              mQueue = mLooper.mQueue;
              mCallback = null;
  }

  public static Looper myLooper() {
        return sThreadLocal.get();
    }

  不管哪种方式,我们知道Thread在默认情况下是没有建立消息循环Looper实例的。

要实现消息循环必须确保Thread的Looper建立。如何确保呢?

  Looper提供了静态函数:

public static void prepare() {
     if (sThreadLocal.get() != null) {
              throw new RuntimeException("Only one Looper may be created per thread");
     }

     sThreadLocal.set(new Looper());
}

//存储线程的局部变量
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

看到这里刚开始让我很是奇怪和迷惑:

  Looper一个独立的类,又不属于某个Thread,而这里创建Looper的函数又是静态的,

属于整个Looper类;创建Looper之后交给静态成员变量sThreadLocal保存,获取

sThreadLocal.get(),那么一个静态变量属于整个类,属性更改始终有效。一次创建之后

sThreadLocal.get()永远都不等于null!

  

  而Thread和Looper是唯一对应的,那这里岂不是所有的Thread都是用同一个Looper,不可能!

所以肯定这个ThreadLocal是有玄机的。网上一查:

  ThreadLocal:

    维护线程的变量,为每个使用该变量的线程实例提供独立的变量副本,每个线程都能够独立使用该变量,

    而互不影响。(详细可参考:http://blog.csdn.net/qjyong/article/details/2158097

  所以每一个线程调用Looper.prepare时,都会创建为其唯一的Looper。

  要建立Handler,需要先创建线程的Looper,才能建立消息系统模型。通过Looper我们建立了

Thread上的消息系统模型Handler,可以来进行消息系统的一系列流程了。

2 消息发送



消息发送两种方式:post和sendMessage;

post针对runnable对象;Runnable是一个接口,就是一个回调函数(提供了run方法)

sendMessage针对Message对象;

下面通过代码具体看一下这个过程:

public final boolean post(Runnable r){
       return  sendMessageDelayed(getPostMessage(r), 0);
}

public final boolean sendMessage(Message msg){
    return sendMessageDelayed(msg, 0);
}

  看到post和sendMessage发送消息时,仅仅是对象不同而已,Runnable和Message;

但实际上都是Message的形式来描述。

这跟我通常理解的消息机制不同:

  通常post消息是将消息加入到消息队列中并不立即执行就返回,send消息是立即执行等待消息执行完才返回。

  而这里post或者send都是将消息放入到消息队列中,然后立即返回,等待消息循环时获取消息被执行。

  这里提供了众多的消息发送方法来指定消息的执行时间和顺序,具体可以查看源代码。

消息执行顺序是根据消息队列中消息的排列顺序而定。

  下面看一下发送消息后将消息加入到消息队列中的代码:

Handler调用MessageQueueenqueueMessage方法:

  final boolean enqueueMessage(Message msg, long when) {

              Message p = mMessages;

              if (p == null || when == 0 || when < p.when) {
                 msg.next = p;
                 mMessages = msg;
              }
              else {
                     Message prev = null;
                     while (p != null && p.when <= when) {
                            prev = p;
                            p = p.next;
                     }

                     msg.next = prev.next;
                     prev.next = msg;
              }
              ……
  }

  

  可以看到是按照时间顺序将消息加入到MessageQueue中;

现在将消息加入到消息队列中存储起来,消息并未得到处理,下一步必然是如何派发消息和处理消息。

3 消息派发



建立Thread消息循环由Looper完成,存在一个消息调度死循环:

  public static void loop() {
       MessageQueue queue = me.mQueue;
       while (true) {
              Message msg = queue.next(); // might block
              if (msg != null) {
                     if (msg.target == null) {
                            // No target is a magic identifier for the quit message.
                            return;
                     }

                     //派发消息 到target(Handler)
            msg.target.dispatchMessage(msg);

            //回收Msg到msgPool
                     msg.recycle();
              }
       }
  }

  

  这里看到消息派发是由Message的target完成,这个target是什么呢?是一个Handler。

消息系统是通过Handler用来与外部交互,把消息派发出去。可以看到没有这个Handler,消息循环将结束。

 

消息派发由Looper通过Handler完成:

  public void dispatchMessage(Message msg) {

       //首先判断runnable对象
       if (msg.callback != null) {
              handleCallback(msg);
       }
       else {
              //整个消息系统的回调函数 可以不用实现自己Handler
              if (mCallback != null) {
                     if (mCallback.handleMessage(msg)) {
                            return;
                     }
              }
              //消息处理 通常交给Handler派生类
              handleMessage(msg);
       }
  }

 

  通过消息派发,这样就实现消息的异步处理。

4 消息原型



前面看到消息发送有两种方式:

  post(Runnable对象),sendMessage(Message对象),而中间都是通过Message对象

保存在MessageQueue中。然后消息派发时处理方式不同。如果在sendMessage时将将消息对象

附上Runnable对象,则post和sendMessage没有区别了。所以这两种方式很好理解基本一致,处理的方式不同罢了。

  

  消息系统模型中,我们的真正的消息原型是什么,都具有那些功能,下面看一下Message中到底

包含了那些东西,能有效帮助我们合理的运用消息系统来完成一些任务和处理。

Message消息原型:

  public final class Message implements Parcelable {
         //标识消息
         public int what;
         int flags;
         long when;

         //传递简单数据
         public int arg1;
         public int arg2;

         //传递较复杂数据 对象
         public Object obj;
         Bundle data;

         //处理消息的目标Handler
         Handler target;   

         //消息派发时 执行的Runnable对象
         Runnable callback;  

         //使消息形成链表
         Message next;

         //建立一个消息pool,回收msg,以避免重复创建节约开销
         private static Message sPool;
         private static int sPoolSize = 0;
         private static final int MAX_POOL_SIZE = 10;
  }  

  

  看到提供了很丰富的属性来描述消息,针对具体问题选择使用那些属性去怎么样描述消息了。

  获取新的Message对象时,Message提供了obtain方法:避免我们自己去分配Message新的对象,

通过obtain获取,可能从MessagePool中获取,节约开销。

下面看一下这个MessagePool是如何建立的:

  通常消息处理完毕的时候,消息也基本上处于无用状态可以释放回收了。对于需要频繁的创建释放的对象来说,

创建和释放类实例都是要开销的,太频繁的使开销增大不好,像Message这种很有可能会频繁的创建。

  于是我们可以将创建的对象用完之后保存在一个Pool里面,以便再重复利用节约频繁创建释放开销。

是如何建立的呢?必然是在消息处理完毕之后才能进行。

MessagePool建立:

public static void loop() {
       while (true) {
              //派发消息
              msg.target.dispatchMessage(msg);

              //消息处理完毕 回收
        msg.recycle();
    }
}     

public void recycle() {
       //回收Message 建立全局的MessagePool
       if (sPoolSize < MAX_POOL_SIZE) {
           next = sPool;
           sPool = this;
           sPoolSize++;
       }
}

五 Handler的应用

  以上这就是整个Handler作用及消息系统模型的建立。

使用也非常简单,虽然有很多方式,但只要理解Handler是建立在Looper上,实现Thread的

消息系统处理模型,实现消息异步处理,我想对与Handler基本应用上没有什么不能理解的了。

其他方面可以去看源码了。

  Handler使用起来是非常简单的,关键就是如何利用消息的异步处理,来合理的完成我们

需要功能和任务。对于一个Thread,我们使用好几个Handler来进行异步处理,也可以创建新的Thread,

通过Handler来实现消息异步处理等等,应用场景很多如何用的好用的合理,这就没什么经验了。

  至于如何使用,源码中很多例子可以看一下AsyncQueryHandler这个类,其中两个线程,

完成查询工作,通过Handler进行线程之间有消息传递。感觉这个利用的很好很巧妙。

参考文章:http://blog.csdn.net/maxleng/article/details/5552976

时间: 2024-10-12 16:27:50

Android中消息系统模型和Handler Looper的相关文章

android中调用系统的发送短信、发送邮件、打电话功能

1 调用发送短信功能: Uri smsToUri = Uri.parse("smsto:"); Intent sendIntent = new Intent(Intent.ACTION_VIEW, smsToUri); sendIntent.putExtra("address", "123456"); //电话号码,这行去掉的话,默认就没有电话 sendIntent.putExtra("sms_body","短信内容

Android中调用系统所装的软件打开文件(转)

Android中调用系统所装的软件打开文件(转) 在应用中如何调用系统所装的软件打开一个文件,这是我们经常碰到的问题,下面是我所用到的一种方法,和大家一起分享一下! 这个是打开文件的一个方法: Java代码 /** * 打开文件 * @param file */ private void openFile(File file){ Intent intent = new Intent(); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //设置in

Android消息传递源码理解。Handler,Looper,MessageQueue,Message

Android中消息传递模块差不多看了好几次,虽然每次看的方式都差不多但是还是发觉的到每次看了之后,理解的更清晰一点. 关于这个模块的文章数不胜数,但是最重要的还是自己动手理解一遍更好. 会牵扯到的几个类: Handler.java  , Looper.java , MessageQueue.java , Message.java 源代码路径: xxx/frameworks/base/core/java/android/os  看的过程中你会发现高版本和低版本系统代码有些地方比较大的差距.从中我

[转载] 从Handler.post(Runnable r) ,Handler.sendEmptyMessage()梳理Android的消息机制(以及handler的内存泄露)

Handler 每个初学Android开发的都绕不开Handler这个"坎",为什么说是个坎呢,首先这是Android架构的精髓之一,其次大部分人都是知其然却不知其所以然.今天看到Handler.post这个方法之后决定再去翻翻源代码梳理一下Handler的实现机制. 异步更新UI 先来一个必背口诀"主线程不做耗时操作,子线程不更新UI",这个规定应该是初学必知的,那要怎么来解决口诀里的问题呢,这时候Handler就出现在我们面前了(AsyncTask也行,不过本质

Android中的多线程编程(二)Handler的原理(附源码)

Android中Handler的原理 一.Handler的原理: 1.Handler.Looper.MessageQueue之间的关系. (1).Handler类:向MessageQueue消息队列中发送消息,接收Looper返回来的消息并处理. (2).Looper类: 存储消息队列的容器.负责接收Handler发送的消息,并直接把消息回传给Handler自己. (3).MessageQueue类:存储消息. 2.关系: (1).创建Handler对象的时候,它就会绑定到默认的线程(UI线程)

Android中GUI系统的Event路由机制

前两天在论坛上看到有人发了一个帖子,询问一个Android GUI Event处理的问题:有一个LinearLayout,里面有很多的child view,他问如何监听这个LinearLayout的Click事件?他的做法是: setClickable(true); setOnClickListener(listener); 最后他发现listener中的回调函数根本不会被调用. 事实上,在Android的GUI系统的中,硬件触发的Event(KeyEvent. TouchEvent. Trac

入门篇:11.Android中日志系统和权限系统

一.安卓中的日志系统 1.java中常用的两个日志 System.out.println();//普通日志 System.err.println();//警告日志 2.android中常用的日志种类 Log.e(Tag,"错误信息"); Log.w(Tag,"警告信息"); Log.i(Tag,"普通信息"); Log.d(Tag,"调试信息"); Log.v(Tag,"无用信息"); ps:这个log.v

Android中区分系统程序和安装程序

在google上输入以上的关键字+ android,可以搜到的代码 List<PackageInfo> packs = getPackageManager().getInstalledPackages(0); 虽然,有些代码号称可以过滤掉系统自身的应用程序,但是只要细看代码就会发现,好像里面的那个布尔变量没有起到什么过滤的作用. 方法一:通过获取的安装包(包括安装的与系统自身的应用程序),对其android.content.pm.PackageInfo的packageName 进行过滤, 但是

我的Android进阶之旅------&gt;Android中Dialog系统样式讲解

今天在维护公司的一个APP的时候,有如下场景. 弹出一个AlertDialog的时候,在系统语言是中文的时候,如下所示: 弹出一个AlertDialog的时候,在系统语言是English的时候,如下所示: 可以发现在系统语言为英语的时候,对话框中的白色文字已经完全看不清楚,对话框的背景颜色也变成了白色.因此需要修改对话框的主题. 修改之前代码如下: AlertDialog commedialog = new AlertDialog.Builder( WalkieTalkieActivity.th