Android的Handler Looper Message机制应用实例与详解(二)

上一篇博文给出了Android中基于Handler Looper机制实现线程间通信的两个典型实例。本文将对该机制的基本原理进行较深入的研究。个人认为,学好Android编程最好的老师就是Android的源代码,下面将基于Android-19的源码进行分析,重点阐述分析思路。

要分析Handler Looper机制,自然想到去看Handler类和Looper类的源码(分别位于Handler.java和Looper.java两个文件中)。简单阅读两个类的描述后,在Looper类的描述中能找到以下一段示例代码。

* <p>This is a typical example of the implementation of a Looper thread,
* using the separation of {@link #prepare} and {@link #loop} to create an
* initial Handler to communicate with the Looper.
*
* <pre>
*  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();
*      }
*  }</pre>
*/

这段代码给出了Handler Looper机制实现进程间通信的三大基本步骤,包括Looper的两个函数prepare()和loop(),以及Handler的handleMessage函数。上一篇博文中实例二模拟子线程向UI主线程传递信息的程序就基本上是直接copy这段示例代码实现的。

先看第一个步骤:调用Looper.prepare()函数,猜测应该是创建Looper对象,做些初始化工作。代码如下:

/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static void prepare() {
    prepare(true);
}

直接调用了重载函数prepare(true);

//ThreadLocal实例为多个线程共享,但保证每个线程的存储空间相互独立
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); 
//消息队列
final MessageQueue mQueue;
//当前线程引用
final Thread mThread;
private static void prepare(boolean quitAllowed) {
    //保证一个线程最多只能创建一个Looper对象。
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //创建Looper对象并存储到当前线程独立的存储空间中
    sThreadLocal.set(new Looper(quitAllowed));
}

private Looper(boolean quitAllowed) {
        //创建消息队列(线程间即通过该消息队列实现通信)
        mQueue = new MessageQueue(quitAllowed);
        //获得当前线程的引用
        mThread = Thread.currentThread();
    }

看到这里明白了,Looper的prepare函数实际上创建了Looper对象并把对象保存到当前线程独立的存储空间中。这里,Looper的构造函数是私有的,所以外部无法直接通过new Looper()随意创建Looper对象。而只能通过Looper的prepare()函数创建。这样做能够保证对于某一个线程,最多只会创建一个Looper对象的实例,这实际上就是设计模拟中的单体模式。此外,在Looper的私有构造函数中还创建了消息队列并获得当前线程(即创建Looper的线程)的引用。

先跳过Handler.handleMessage(Message msg),直接看Looper.loop()的实现。

/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {ui
    //myLooper()函数通过sThreadLocal.get()判断当前线程是否已经创建了Looper的实例
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn‘t called on this thread.");
    }
    final MessageQueue queue = me.mQueue;

    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    //这里开始无限循环
    for (;;) {
        //从消息队列中取出一条消息
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        // This must be in a local variable, in case a UI event sets the logger
        Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                      msg.callback + ": " + msg.what);
        }
        //这里是关键,调用了dispatchMessage函数对从消息队列取出的msg进行分派
        msg.target.dispatchMessage(msg);

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        // Make sure that during the course of dispatching the
        // identity of the thread wasn‘t corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
                    + Long.toHexString(ident) + " to 0x"
                    + Long.toHexString(newIdent) + " while dispatching to "
                    + msg.target.getClass().getName() + " "
                    + msg.callback + " what=" + msg.what);
        }
        //消息使命完成,将其放回消息池
        msg.recycle();
    }
}

到这里,Looper扮演的角色已经明朗了,主要就是通过loop()函数中的那个无限循环不断从消息队列中取出消息,并通过dispatchMessage()方法将消息派送出去。那么消息队列中的消息从哪里来,又会被派送到哪里呢?

先来分析第一个问题,消息从哪里来。上一篇博文的实例中,消息源线程均通过调用Handler的sendMessage()函数来发送消息。进入Handler.java文件看其中的sendMessage()函数。

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

这里调用了sendMessageDelayed,延时0毫秒。

public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
   if (delayMillis < 0) {
       delayMillis = 0;
   }
   return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

进一步调用了sendMessageAtTime,在当前时刻发出。

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
   //获得消息队列的引用
   MessageQueue queue = mQueue;
   if (queue == null) {
       RuntimeException e = new RuntimeException(
                 this + " sendMessageAtTime() called with no mQueue");
       Log.w("Looper", e.getMessage(), e);
       return false;
   }
   return enqueueMessage(queue, msg, uptimeMillis);
 }

再看enqueueMessage函数。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
   //设置target handler
   msg.target = this;
   if (mAsynchronous) {
        msg.setAsynchronous(true);
   }
   //将消息插入到消息队列中
   return queue.enqueueMessage(msg, uptimeMillis);
}

看到这里已经明朗了,消息源线程通过Handler.sendMessage发送消息,实际上就是把消息插入了与之关联的消息队列中。在enqueueMessage函数中有一条关键语句msg.target = this,通过这条语句就把Handler和Looper关联起来了(在Looper.loop()的循环中就是通过msg.target属性找到发送消息的Handler并调用其dispatchMessage()函数派发消息的).

搞清楚了消息从哪里来,接下来分析消息被派发到哪里,接着看dispatchMessage()函数的实现。

/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
    //若消息对象实现了其中的Runnable接口,调用对应的回调函数,即为message.callback.run())
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        //若实现了Handler类的Callback接口,调用接口的回调函数
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //将消息返回Handler的消息处理回调函数(是Handler的成员函数,示例代码中回调的就是该函数)
        handleMessage(msg);
    }
}

可见,dispatchMessage函数中根据msg,handler对象的设置情况调用相应的消息处理回调函数,我们只需要在这个回调函数中添加代码,就可以进行消息处理。示例代码的第二个步骤中的handleMessage函数就是在这里被回调的。

下面回到示例代码中的第二个步骤:

*    mHandler = new Handler() {
*         public void handleMessage(Message msg) {
*         // process incoming messages here
*         }
*    };

这段代码创建了Handler的对象,并覆盖了其中的HandleMessage方法,用户可以添加自己的消息处理函数。 handleMessage回调函数上面已经分析过了,下面主要看看创建handler对象时都做了哪些事情。转入Handler.java文件,看Handler的构造函数(找不带参数那个)。

/**
* Default constructor associates this handler with the {@link Looper} for the
* current thread.
*
* If this thread does not have a looper, this handler won‘t be able to receive messages
* so an exception is thrown.
*/
public Handler() {
    this(null, false);
}

调用了下面的双参数的构造函数。

final Looper mLooper;
final MessageQueue mQueue;
public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&(klass.getModifiers() & Modifier.STATIC) == 0) {
           Log.w(TAG, "The following Handler class should be static or leaks might occur: " +klass.getCanonicalName());
        }
    }
    //获得当前线程Looper对象的引用
    mLooper = Looper.myLooper();
    if (mLooper == null) {
         throw new RuntimeException(
             "Can‘t create handler inside thread that has not called Looper.prepare()");
    }
    //获得Looper关联的消息队列    
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

到这里发现Handler的构造函数主要做了两件事:1)获得当前线程Looper对象的应用.2)获得与Looper关联的消息队列的引用。到这里,Handler、Looper、消息队列三者就已经关联起来了。

下面通过一个示意图对上面的分析进行总结。

由图可见,基于Handler Looper机制传递消息主要包括以下几个步骤。

(1)目标线程调用Looper.prepare()创建Looper对象和消息队列。

(2)目标线程通过new Handler()创建handler对象,将Handler,Looper,消息队列三者关联起来。并覆盖其handleMessage函数。

(3)目标线程调用Looper.loop()监听消息队列。

(4)消息源线程调用Handler.sendMessage发送消息。

(5)消息源线程调用MessageQueue.enqueueMessage将待发消息插入消息队列。

(6)目标线程的loop()检测到消息队列有消息插入,将其取出。

(7)目标线程将取出的消息通过Handler.dispatchMessage派发给Handler.handleMessage进行消息处理。

到这里整个Android的Handler Looper机制传递消息原理就分析完毕了。还有一个问题值得一提,回顾一下上一篇博文的示例1模拟从网络上下载数据的程序,UI主线程只通过new Handler()创建了Handler对象的实例并覆盖了其handleMessage函数。在代码中并没有看到调用Looper.prepare和Looper.loop(),那么UI主线程中没有创建Looper对象吗?下面就来分析这个问题,既然是UI主线程,那么自然是在启动应用时候由系统自动创建的,创建过程中是否已经创建了Looper对象并调用loop()进行监听了呢?转到ActivityThread.java,找到其中的main函数,这里即为Android程序的入口。在其中能看到以下两行代码。

Looper.prepareMainLooper();
    ...... ...... 
Looper.loop();

再看prepareMainLooper函数的实现:

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
       if (sMainLooper != null) {
           throw new IllegalStateException("The main Looper has already been prepared.");
       }
       sMainLooper = myLooper();
    }
}

在这里调用了prepare创建Looper对象。所以说,对于UI主线程而言,其Looper对象是由系统创建好的,用户就无需自行创建了。

时间: 2024-10-13 05:09:58

Android的Handler Looper Message机制应用实例与详解(二)的相关文章

Android的Handler Looper Message机制应用实例与详解(一)

Android的UI操作不是线程安全的(出于提高性能考虑,避免实现多线程同步等机制所引入的延时),若多个线程同时对UI元素进行操作,可能导致线程安全问题.因此,Android中做了严格的规定:只有UI主线程才能对UI进行设置与操作. 在实际编程中,为了避免UI界面长时间得不到响应而导致的ANR(Application Not Responding)异常,通常将网络访问.复杂运算等一些耗时的操作被放在子线程中执行.这就需要子线程在运行完毕后将结果返回到主线程并通过UI进行显示.在Android中,

Android异步消息处理 Handler Looper Message关系源码分析

# 标签: 读博客 对于Handler Looper Message 之前一直只是知道理论,知其然不知所以然,看了hongyang大神的源码分析,写个总结帖. 一.概念.. Handler . Looper .Message 这三者都与Android异步消息处理线程相关的概念. 异步消息处理线程启动后会进入一个无限的循环体之中,每循环一次,从其内部的消息队列中取出一个消息,然后回调相应的消息处理函数,执行完成一个消息后则继续循环.若消息队列为空,线程则会阻塞等待. 说了这一堆,那么和Handle

Android研究之手势交互实例实现详解

 先来几张效果图: 一.没有抛掷: 二.向右抛掷一次 三.向右再抛掷一次 接下来说说Android开发中对于手势动作的识别参考资料... 首先,在Android系统中,每一次手势交互都会依照以下顺序执行. 1. 接触接触屏一刹那,触发一个MotionEvent事件. 2. 该事件被OnTouchListener监听,在其onTouch()方法里获得该MotionEvent对象. 3. 通过GestureDetector(手势识别器)转发次MotionEvent对象至OnGestureList

Android M新特性Doze and App Standby模式详解

参考: Optimizing for Doze and App Standby Android M新特性Doze and App Standby模式详解 深入android6.0 设备 idle状态 Android M 的 Doze 模式下第三方推送服务还能用吗? 一.Optimizing for Doze and App Standby 从Android6.0开始,Android提供了两种省电延长电池寿命的功能:Doze和App Standby: 表现形式:当设备没有连接到电源,设备进入Doz

(转)Java经典设计模式(3):十一种行为型模式(附实例和详解)

原文出处: 小宝鸽 Java经典设计模式共有21中,分为三大类:创建型模式(5种).结构型模式(7种)和行为型模式(11种). 本文主要讲行为型模式,创建型模式和结构型模式可以看博主的另外两篇文章:Java经典设计模式之五大创建型模式(附实例和详解).Java经典设计模式之七大结构型模式(附实例和详解). 行为型模式细分为如下11种:策略模式.模板方法模式.观察者模式.迭代子模式.责任链模式.命令模式.备忘录模式.状态模式.访问者模式.中介者模式.解释器模式. 接下来对11种行为型模式逐个进行介

Android开发之AudioManager(音频管理器)详解

AudioManager简介: AudioManager类提供了访问音量和振铃器mode控制.使用Context.getSystemService(Context.AUDIO_SERVICE)来得到这个类的一个实例. 公有方法: Public Methods int abandonAudioFocus(AudioManager.OnAudioFocusChangeListenerl) 放弃音频的焦点. void adjustStreamVolume(int streamType, int dir

Java设计模式之十一种行为型模式(附实例和详解)

Java经典设计模式共有21中,分为三大类:创建型模式(5种).结构型模式(7种)和行为型模式(11种). 本文主要讲行为型模式,创建型模式和结构型模式可以看博主的另外两篇文章:J设计模式之五大创建型模式(附实例和详解). 设计模式之七大结构型模式(附实例和详解). 行为型模式细分为如下11种:策略模式.模板方法模式.观察者模式.迭代子模式.责任链模式.命令模式.备忘录模式.状态模式.访问者模式.中介者模式.解释器模式. 接下来对11种行为型模式逐个进行介绍. 一.策略模式 策略模式定义了一系列

SpringBoot启动机制(starter机制)核心原理详解

作者:MyBug 一.前言 使用过springboot的同学应该已经知道,springboot通过默认配置了很多框架的使用方式帮我们大大简化了项目初始搭建以及开发过程.本文的目的就是一步步分析springboot的启动过程,这次主要是分析springboot特性自动装配.那么首先带领大家回顾一下以往我们的web项目是如何搭建的,通常我们要搭建一个基于Spring的Web应用,我们需要做以下一些工作:pom文件中引入相关jar包,包括spring.springmvc.redis.mybaits.l

Android基础入门教程——8.3.16 Canvas API详解(Part 1)

Android基础入门教程--8.3.16 Canvas API详解(Part 1) 标签(空格分隔): Android基础入门教程 本节引言: 前面我们花了13小节详细地讲解了Android中Paint类大部分常用的API,本节开始我们来讲解 Canvas(画板)的一些常用API,我们在Android基础入门教程--8.3.1 三个绘图工具类详解 中已经列出了我们可供调用的一些方法,我们分下类: drawXxx方法族:以一定的坐标值在当前画图区域画图,另外图层会叠加, 即后面绘画的图层会覆盖前