Android的消息机制

Posted Jack-Chan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android的消息机制相关的知识,希望对你有一定的参考价值。

1. ANR异常

Application No Response:应用程序无响应。在主线程中,是不允许执行耗时的操作的,如果主线程阻塞的时间大于6秒,就很有可能出现anr异常。主线程,要完成界面的更新,事件的处理,窗体显示的回调,所以如果主线程阻塞时间较长,就不能很好的处理以上比较重要的事情,那么android有一个机制,就是如果他发现消息队列中有很多消息,主线程没办法响应的话,他就会抛出anr异常。所以,比较耗时的操作都必须要交给子线程。

解决办法:可以通过Handler来解决这个问题,将比较耗时的操作交给子线程,然后子线程通过Handler,发送消息给主线程,让主线程去更新界面。什么样的操作时比较耗时的?

1、访问网络,2、大文件的拷贝,3、阻塞式的请求,socket

2. Handler、Looper、Message、MessageQueue

Android 的Handler 机制(也有人叫消息机制)目的是为了跨线程通信,也就是多线程通信。之所以需要跨线程通信是因为在Android 中主线程通常只负责UI 的创建和修改,子线程负责网络访问和耗时操作,因此,主线程和子线程需要经常配合使用才能完成整个Android 功能。

在Android中,线程内部或者线程之间进行信息交互时经常会使用消息,这些基础的东西如果我们熟悉其内部的原理,将会使我们容易、更好地架构系统,避免一些低级的错误。在学习Android中消息机制之前,我们先了解与消息有关的几个类:

  • Handler:消息处理器,发送消息和处理消息。你可以构造Handler对象来与Looper沟通,以便push新消息到Message Queue里,或者接收Looper(从Message Queue取出)所送来的消息。
  • Looper:轮询器,从messagequeue取消息,分发给handler处理。一个线程可以产生一个Looper对象,由它来管理此线程里的Message Queue(消息队列)
  • Message 消息,数据的载体
  • MessageQueue 消息队列,存储消息

当我们的Android应用程序的进程一创建的时候,系统就给这个进程提供了一个Looper,Looper是一个死循环,它内部维护这个一个消息队列。Looper不停地从消息队列中取消息(Message),取到消息就发送给了Handler,最后Handler根据接收到的消息去修改UI。Handler的sendMessage方法就是将消息添加到消息队列中。

3. UI线程

线程:UI thread 通常就是main thread,而Android启动程序时会替它建立一个Message Queue。

每一个线程里可含有一个Looper对象以及一个MessageQueue数据结构。在你的应用程序里,可以定义Handler的子类别来接收Looper所送出的消息。在你的Android程序里,新诞生一个线程,或执行 (Thread)时并不会自动建立其Message Looper。

Android里并没有Global的Message Queue数据结构,例如,不同APK里的对象不能透过Massage Queue来交换讯息(Message)。

例如:线程A的Handler对象可以传递消息给别的线程,让别的线程B或C等能送消息来给线程A(存于A的Message Queue里)。线程A的Message Queue里的消息,只有线程A所属的对象可以处理。使用Looper.myLooper()可以取得当前线程的Looper对象。可以自定义Handler类,只要继承Handler即可。使用new EventHandler(Looper.myLooper()); 可用来构造当前线程的Handler对象(其中EventHandler是自定义的Handler类)。

4. Activity.runOnUiThread()

Activity中提供了一个runOnUiThread方法,用于进行消息处理。此方法是通过线程合并join来实现消息处理的。
线程合并:主线程将子线程的任务拿到自己这里来执行并终止子线程。实例代码如下:

/**
 * Runs the specified action on the UI thread. If thecurrent thread is
 * the UI thread, then the action is executedimmediately. If the
 * current thread is not the UI thread, the action is posted to the
 * event queue of the UI thread.
 *
 * 上面的意思为:在UI线程中运行我们的任务,如果当前线程是UI线程,则立即执行,如果
 * 不是则该任务发送到UI线程的事件队列。 
 */
runOnUiThread(new Runnable() 
    @Override
    public void run() 
        //自定义我们的业务代码
    
);

5. View.post()、View.postDelayed()

6. Message消息

消息对象,顾名思义就是记录消息信息的类。这个类有几个比较重要的字段:

  • arg1和arg2:我们可以使用两个字段用来存放我们需要传递的整型值,在Service中,我们可以用来存放Service的ID。
  • obj:该字段是Object类型,我们可以让该字段传递某个多项到消息的接受者中。
  • what:这个字段可以说是消息的标志,在消息处理中,我们可以根据这个字段的不同的值进行不同的处理,类似于我们在处理Button事件时,通过switch(v.getId())判断是点击了哪个按钮。

在使用Message时,我们可以通过new Message()创建一个Message实例,但是Android更推荐我们通过Message.obtain()或者Handler.obtainMessage()获取Message对象。这并不一定是直接创建一个新的实例,而是先从消息池中看有没有可用的Message实例,存在则直接取出并返回这个实例。反之如果消息池中没有可用的Message实例,则根据给定的参数new一个新Message对象。通过分析源码可得知,Android系统默认情况下在消息池中实例化10个Message对象。

//创建或获取消息的几种方式
Message msg = new Message();// 创建一个新的消息对象
Message msg = handler.obtainMessage();// 获取一个消息,如果消息池存在消息,则复用消息池中的消息,否则新创建一个消息对象
Message msg = Message.obtain();
Message.obtain(handler, what, obj).sendToTarget();

7. MessageQueue消息队列

消息队列,用来存放Message对象的数据结构,按照“先进先出”的原则存放消息。内部采用单链表的数据结构来存储消息列表,存放并非实际意义的保存,而是将Message对象以链表的方式串联起来的。MessageQueue对象不需要我们自己创建,而是有Looper对象对其进行管理,一个线程最多只可以拥有一个MessageQueue。我们可以通过Looper.myQueue()获取当前线程中的MessageQueue。

MessageQueue的管理者,在一个线程中,如果存在Looper对象,则必定存在MessageQueue对象,并且只存在一个Looper对象和一个MessageQueue对象。

public class Looper 
    MessageQueue mQueue;//Looper身上维持着一个消息队列
    ...

在Android系统中,除了主线程有默认的Looper对象,其它线程默认是没有Looper对象。如果想让我们新创建的线程拥有Looper对象时,我们首先应调用Looper.prepare()方法,然后再调用Looper.loop()方法。典型的用法如下:

class LooperThread extends Thread

    public Handler mHandler;
    public void run()
    
        Looper.prepare();
        //其它需要处理的操作
        Looper.loop();
    

倘若我们的线程中存在Looper对象,则我们可以通过Looper.myLooper()获取,此外我们还可以通过Looper.getMainLooper()获取当前应用系统中主线程的Looper对象。在这个地方有一点需要注意,假如Looper对象位于应用程序主线程中,则Looper.myLooper()和Looper.getMainLooper()获取的是同一个对象。

8. Handler消息处理器

消息的处理者。通过Handler对象我们可以封装Message对象,然后通过sendMessage(msg)把Message对象添加到MessageQueue中;当MessageQueue循环到该Message时,就会调用该Message对象对应的handler对象的handleMessage()方法对其进行处理。由于是在handleMessage()方法中处理消息,因此我们应该编写一个类继承自Handler,然后在handleMessage()处理我们需要的操作。

下面我们通过跟踪代码分析在Android中是如何处理消息。首先贴上测试代码:

public class MessageService extends Service

    private static final String TAG = "MessageService";
    private static final int KUKA = 0;
    private Looper looper;
    private ServiceHandler handler;
    /**
     * 由于处理消息是在Handler的handleMessage()方法中,因此我们需要自己编写类
     * 继承自Handler类,然后在handleMessage()中编写我们所需要的功能代码
     * @author coolszy
     */
    private final class ServiceHandler extends Handler
    
        public ServiceHandler(Looper looper)
        
            super(looper);
        
        @Override
        public void handleMessage(Message msg)
        
            // 根据what字段判断是哪个消息
            switch (msg.what)
            
            case KUKA:
                //获取msg的obj字段。我们可在此编写我们所需要的功能代码
                Log.i(TAG, "The obj field of msg:" + msg.obj);
                break;
            // other cases
            default:
                break;
            
            // 如果我们Service已完成任务,则停止Service
            stopSelf(msg.arg1);
        
    
    @Override
    public void onCreate()
    
        Log.i(TAG, "MessageService-->onCreate()");
        // 默认情况下Service是运行在主线程中,而服务一般又十分耗费时间,如果
        // 放在主线程中,将会影响程序与用户的交互,因此把Service
        // 放在一个单独的线程中执行
        HandlerThread thread = new HandlerThread("MessageDemoThread", Process.THREAD_PRIORITY_BACKGROUND);
        thread.start();
        // 获取当前线程中的looper对象
        looper = thread.getLooper();
        //创建Handler对象,把looper传递过来使得handler、
        //looper和messageQueue三者建立联系
        handler = new ServiceHandler(looper);
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId)
    
        Log.i(TAG, "MessageService-->onStartCommand()");
        //从消息池中获取一个Message实例
        Message msg = handler.obtainMessage();
        // arg1保存线程的ID,在handleMessage()方法中
        // 我们可以通过stopSelf(startId)方法,停止服务
        msg.arg1 = startId;
        // msg的标志
        msg.what = KUKA;
        // 在这里我创建一个date对象,赋值给obj字段
        // 在实际中我们可以通过obj传递我们需要处理的对象
        Date date = new Date();
        msg.obj = date;
        // 把msg添加到MessageQueue中
        handler.sendMessage(msg);
        return START_STICKY;
    
    @Override
    public void onDestroy()
    
        Log.i(TAG, "MessageService-->onDestroy()");
    
    @Override
    public IBinder onBind(Intent intent)
    
        return null;
    

运行结果:

注:在测试代码中我们使用了HandlerThread类,该类是Thread的子类,该类运行时将会创建looper对象,使用该类省去了我们自己编写Thread子类并且创建Looper的麻烦。下面我们分析下程序的运行过程:

8.1 onCreate()

首先启动服务时将会调用onCreate()方法,在该方法中我们new了一个HandlerThread对象,提供了线程的名字和优先级。紧接着我们调用了start()方法,执行该方法将会调用HandlerThread对象的run()方法:

public void run() 
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) 
        mLooper = Looper.myLooper();
        notifyAll();
    
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;

在run()方法中,系统给线程添加的Looper,同时调用了Looper的loop()方法:

public static final void loop() 
    Looper me = myLooper();
    MessageQueue queue = me.mQueue;
    while (true) 
        Message msg = queue.next(); // might block
        //if (!me.mRun) 
        //    break;
        //
        if (msg != null) 
            if (msg.target == null) 
                // No target is a magic identifier for the quit message.
                return;
            
            if (me.mLogging!= null) me.mLogging.println(
                    ">>>>> Dispatching to " + msg.target + " "
                    + msg.callback + ": " + msg.what
                    );
            msg.target.dispatchMessage(msg);
            if (me.mLogging!= null) me.mLogging.println(
                    "<<<<< Finished to    " + msg.target + " "
                    + msg.callback);
            msg.recycle();
        
    

通过源码我们可以看到loop()方法是个死循环,将会不停的从MessageQueue对象中获取Message对象,如果MessageQueue 对象中不存在Message对象,则结束本次循环,然后继续循环;如果存在Message对象,则执行 msg.target.dispatchMessage(msg),但是这个msg的.target字段的值是什么呢?我们先暂时停止跟踪源码,返回到onCreate()方法中。线程执行完start()方法后,我们可以获取线程的Looper对象,然后new一个ServiceHandler对象,我们把Looper对象传到ServiceHandler构造函数中将使handler、looper和messageQueue三者建立联系。

8.2 onStartCommand()

执行完onStart()方法后,将执行onStartCommand()方法。首先我们从消息池中获取一个Message实例,然后给Message对象的arg1、what、obj三个字段赋值。紧接着调用sendMessage(msg)方法,我们跟踪源代码,该方法将会调用sendMessageDelayed(msg, 0)方法,而sendMessageDelayed()方法又会调用sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)方法,在该方法中我们要注意该句代码msg.target = this,msg的target指向了this,而this就是ServiceHandler对象,因此msg的target字段指向了ServiceHandler对象,同时该方法又调用MessageQueue 的enqueueMessage(msg, uptimeMillis)方法:

final boolean enqueueMessage(Message msg, long when) 
    if (msg.when != 0) 
        throw new AndroidRuntimeException(msg
                + " This message is already in use.");
    
    if (msg.target == null && !mQuitAllowed) 
        throw new RuntimeException("Main thread not allowed to quit");
    
    synchronized (this) 
        if (mQuiting) 
            RuntimeException e = new RuntimeException(
                msg.target + " sending message to a Handler on a dead thread");
            Log.w("MessageQueue", e.getMessage(), e);
            return false;
         else if (msg.target == null) 
            mQuiting = true;
        
        msg.when = when;
        //Log.d("MessageQueue", "Enqueing: " + msg);
        Message p = mMessages;
        if (p == null || when == 0 || when < p.when) 
            msg.next = p;
            mMessages = msg;
            this.notify();
         else 
            Message prev = null;
            while (p != null && p.when <= when) 
                prev = p;
                p = p.next;
            
            msg.next = prev.next;
            prev.next = msg;
            this.notify();
        
    
    return true;

该方法主要的任务就是把Message对象的添加到MessageQueue中(数据结构最基础的东西,自己画图理解下)。

handler.sendMessage()–>handler.sendMessageDelayed()–>handler.sendMessageAtTime()–>msg.target = this;queue.enqueueMessage==>把msg添加到消息队列中

8.3 handleMessage(msg)

onStartCommand()执行完毕后我们的Service中的方法就执行完毕了,那么handleMessage()是怎么调用的呢?在前面分析的loop()方法中,我们当时不知道msg的target字段代码什么,通过上面分析现在我们知道它代表ServiceHandler对象,msg.target.dispatchMessage(msg);则表示执行ServiceHandler对象中的dispatchMessage()方法:

public void dispatchMessage(Message msg) 
    if (msg.callback != null) 
        handleCallback(msg);
     else 
        if (mCallback != null) 
            if (mCallback.handleMessage(msg)) 
                return;
            
        
        handleMessage(msg);
    

该方法首先判断callback是否为空,我们跟踪的过程中未见给其赋值,因此callback字段为空,所以最终将会执行handleMessage()方法,也就是我们ServiceHandler类中复写的方法。在该方法将根据what字段的值判断执行哪段代码。

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

9. Handler的源码分析

先看构造方法

public class Handler 
    private Looper       mLooper;
    private MessageQueue mQueue;

    public Handler() 
        mLooper = Looper.myLooper();
        if (mLooper == null) 
            throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
        
        mQueue = mLooper.mQueue;
    

在Handler的构造方法中,调用Looper.myLooper()方法获取一个Looper对象,如果Looper对象为空,则会抛异常,没有Looper对象不能创建Handler对象。但是我们在主线程new Handler的时候,并没有调用Looper.prepare()和Looper.loop()方法初始化Looper,也不会出现异常,这是因为Android系统在主线程创建的时候帮我们把Looper初始化了

9.1 主线程设置Looper,在ActivityThread类里面

public static final void main(String[] args) 
    ....
    // 1.主线程创建Looper 
    Looper.prepareMainLooper();
    if (sMainThreadHandler == null) 
        sMainThreadHandler = new Handler();
    
    ActivityThread thread = new ActivityThread();
    thread.attach(false);
    if (false) 
        Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));
    
    Looper.loop();

MainThread 是Android 系统创建并维护的,创建的时候系统执行了Looper.prepare();方法,该方法内部创建了MessageQueue 消息队列(也叫消息池),该消息队列是Message 消息的容器,用于存储通过handler发送过来的Message。MessageQueue 是Looper 对象的成员变量,Looper 对象通过ThreadLocal 绑定在MainThread 中。因此我们可以简单的这么认为:MainThread 拥有唯一的一个Looper 对象,该Looper 对象有用唯一的MessageQueue 对象,MessageQueue 对象可以存储多个Message。

MainThread 中需要程序员手动创建Handler 对象,并覆写Handler 中的handleMessage(Message msg)方法,该方法将来会在主线程中被调用,在该方法里一般会写与UI 修改相关的代码。

MainThread 创建好之后,系统自动执行了Looper.loop();方法,该方法内部开启了一个“死循环”不断的去之前创建好的MessageQueue 中取Message。如果一有消息进入MessageQueue,那么马上会被Looper.loop();取出来,取出来之后就会调用之前创建好的handler 对象的handleMessage(Message)方法。

newThread 线程是我们程序员自定new 出来的子线程。在该子线程中处理完我们的“耗时”或者网络访问任务后,调用主线程中的handler 对象的sendMessage(msg)方法,该方法一被执行,内部将就msg添加到了主线程中的MessageQueue 队列中,这样就成为了Looper.loop()的盘中餐了,等待着被消费。

上面的过程有点类似生产者和消费者的过程。newThread 属于生产者,负责生产Message,MainThread 属于消费者。这是一个很复杂的过程,但是Android 显然已经将这种模式给封装起来了,就叫Handler 机制。我们使用时只需要在主线程中创建Handler,并覆写handler 中的handleMessage 方法,然后在子线程中调用handler 的sendMessage(msg)方法即可。

获取Looper对象后,接着获取Looper身上的MessageQueue对象,Handler就是把消息发送到该消息队列

Handler对象创建后,就可以通过handler.sendXxx()发送消息,可以发送一个普通的消息,也可以发送一个空消息,可以发送一个延时消息,也可以发送一个定时消息

public class Handler 
    ...
    // 发送一个普通消息
    public boolean sendMessage(Message msg) 
        return sendMessageDelayed(msg, 0)
    
    // 发送一个空消息
    public boolean sendEmptyMessage(int what) 
        return sendEmptyMessageDelayed(what, 0);
    
    // 发送一个空的延时消息
    public boolean sendEmptyMessageDelayed(int what, long delayMillis) 
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, 0);
    
    // 发送一个延时消息
    public boolean sendMessageDelayed(Message msg, long delayTime) 
        if (delayTime < 0)
            delayTime = 0;
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayTime);
    
    // 发送一个定时消息
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) 
        MessageQueue queue = mQueue;
        return enqueueMessage(queue, msg, uptimeMillis);
    
    //发送消息几个方法sendXxx(),最终都是调用enqueueMessage()方法,消息入队
    public boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) 
        // 把Message的target置为当前发送的Handler,以便Looper取到message后根据target把message分发给正确的Handler
        msg.target = this;
        // 往队列里面添加消息Message
        return queue.enqueueMessage(msg, uptimeMillis);
    
    ...

发送消息几个方法sendXxx(),最终都是调用enqueueMessage()方法,在该方法内部调用的是消息队列MessageQueue的enqueueMessage()方法,把消息发送到消息队列

把this,也就是当前handler对象赋值给Message 的target属性,当多个Handler发送消息到消息队列的时候,可以通过该属性判断消息是哪个Handler发送的

9.2 MessageQueue.enqueueMessage

boolean enqueueMessage(Message msg, long when) 
        ...
        synchronized (this) 
            ...
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) 
                // 当前发送的message需要马上被处理调,needWake唤醒状态置true
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
             else 
                // 当前发送的message被排队到其他message的后面,needWake唤醒状态置false
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (; ; ) 
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) 
                        break;
                    
                    if (needWake && p.isAsynchronous()) 
                        needWake = false;
                    
                
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            

            if (needWake)  // 是否唤醒主线程
                nativeWake(mPtr);
            
        
        return true;
    

9.3 Handler的post()、postAtTime()、postDelayed()

public final boolean post(Runnable r)

   return  sendMessageDelayed(getPostMessage(r), 0);


public final boolean postAtTime(Runnable r, long uptimeMillis)

    return sendMessageAtTime(getPostMessage(r), uptimeMillis);


public final boolean postDelayed(Runnable r, long delayMillis)

    return sendMessageDelayed(getPostMessage(r), delayMillis);


// 把Runnable包装成一个消息Message
private static Message getPostMessage(Runnable r) 
    Message m = Message.obtain();// 获取消息对象
    m.callback = r;// 把消息赋值给Message的callback属性
    return m;

可以调用Handler的post()、postAtTime()、postDelayed()分别发送一个普通的、定时、延时的Runnable任务,Runnable会赋值给Message的callback属性,最终封装成一个消息发送出去

9.4 删除Callback和Message

  • Handler.removeCallbacks() 从消息队列中删除所有回调
  • Handler.removeMessages() 从消息队列删除所有消息
  • Handler.removeCallbacksAndMessages() 从消息队列中删除所有Message和Callback

一般在Activity销毁的时候调用

public void onDestroy()
    handler.removeCallbacks();
    handler.removeMessages();
    handler.removeCallbacksAndMessages();

10. Looper

轮询器,从messagequeue取消息,分发给handler处理。创建Handler对象,必须有Looper对象,而Looper对象的初始化需要调用Looper.prepare()和Looper.loop()方法

10.1 Looper.prepare()

ThreadLocal<Looper> sThreadLocal;

public static final void prepare() 
    if (sThreadLocal.get() != null) 
        throw new RuntimeException("Only one Looper may be created per thread");
    
    // 3、在主线程中设置Looper, new Looper()里面创建了一个MessageQueue
    sThreadLocal.set(new Looper());


public static final void prepareMainLooper() 
    // 2、调用prepare
    prepare();
    setMainLooper(myLooper());
    if (Process.supportsProcesses()) 
        myLooper().mQueue.mQuitAllowed = false;
    

先从ThreadLocal中获取一个Looper对象,如果该Looper对象不为空,则抛异常,这是因为一个线程仅能够绑定一个Looper对象。ThreadLocal是一个用于线程范围内共享数据的底层是一个map结构的类,key是当前线程,value是Looper。如果当前线程没有绑定Looper对象,则new Looper()创建一个Looper对象,并把该Looper对象设置sThreadLocal

10.2 Looper.loop()

public static void loop() 
    Looper me = myLooper(); // 获取当前线程的Looper对象,为空则抛异常
    if (me == null) 
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    
    MessageQueue queue = me.mQueue; // 获取Looper对象维持的消息队列
    for (; ; )  // 开启死循环从消息队列获取消息
        // 调用MessageQueue的next()取消息,如果没有消息,就阻塞
        Message msg = queue.next();
        // msg.target即Handler,获取消息后调用Handler的dispatchMessage()处理消息
        msg.target.dispatchMessage(msg);
    

主线程调用Looper.loop()方法,主线程就会阻塞,是一个死循环,使用管道(Pipe),是Linux中的一种进程间通信方式,使用了特殊的文件,有两个文件描述符(一个是读取,一个是写入)

应用场景;主进程拿着读取描述符等待读取,没有内容时就阻塞,另一个进程拿写入描述符去写内容,唤醒主进程,主进程拿着读取描述符读取到内容,继续执行。

Handler应用场景:Handler在主线程中创建,Looper会在死循环里等待取消息,1、没取到,就阻塞,2、一旦被子线程唤醒,取到消息,就把Message交给Handler处理。子线程用Handler去发送消息,拿写入描述符去写消息,唤醒主线程。

11. Handler.dispatchMessage

public void dispatchMessage(Message msg) 
    if (msg.callback != null) 
        handleCallback(msg);
     else 
        if (mCallback != null) 
            if (mCallback.handleMessage(msg)) 
                return;
            
        
        // 把Message交给Handler处理
        handleMessage(msg);
    

调用Looper.loop()会开启一个死循环,从消息队列MessageQueue取消息,取到消息后调用msg.target.dispatchMessage(msg);即调用Handler的dispatchMessage()方法,在该方法内部调用的是handleMessage(),对,就是我们new Handler的时候实现的handleMessage()方法

所以Android的消息机制大概流程是:Handler把消息Message发送到消息队列MessageQueue,Looper从消息队列取消息,取到消息后回调Handler的handleMessage()方法

11.1 消息处理的优先级

在dispatchMessage()方法中,如果msg.callback(一个Runnable)不为空,则先处理Message的Runnable;然后判断mCallback(通过Handler的构造方法传进来的Callback)是否为空,不为空,则执行Callback的handleMessage()方法,最后才是执行Handler的handleMessage()

所有消息处理的优先级是Message的callback –> Handler的mCallback –> Handler的handleMessage()

12. Handler机制的应用

12.1 在主线程中给子线程发送消息

public class MainActivity extends Activity 

    private Handler subHandler;//是在子线程中创建的Handler对象
    private Looper  myLooper;//子线程中的Looper对象

    private Handler handler = new Handler()
        @Override
        public void handleMessage(android.os.Message msg) 
            switch (msg.what) 
            case 1:
                Toast.makeText(MainActivity.this, msg.obj.toString(), Toast.LENGTH_SHORT).show();
                break;
            
        
    ;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        /*
         * 匿名内部类对象对外部类有一个隐式的强引用
         */
        new Thread(new Runnable() 
            @Override
            public void run() 
                // 1. 创建了Looper对象,然后Looper对象中创建了MessageQueue
                // 2. 并将当前的Looper对象跟当前的线程(子线程)绑定ThreadLocal
                Looper.prepare();

                // 1. 创建了handler对象,然后从当前线程中获取Looper对象,然后获取到MessageQueue对象
                subHandler = new Handler()
                    @Override
                    public void handleMessage(Message msg) 
                        Toast.makeText(MainActivity.this, msg.obj.toString(), Toast.LENGTH_SHORT).show();
                    
                ;

                myLooper = Looper.myLooper();//获取当前线程中的Looper对象

                /*
                 * 1. 从当前线程中找到之前创建的Looper对象,然后找到 MessageQueue
                 * 2. 开启死循环,遍历消息池中的消息
                 * 3. 当获取到msg的时候,调用这个msg的handler的disPatchMsg方法,让msg执行起来
                 */
                Looper.loop();
                Log.d("tag", "loop()方法执行完了");
            
        ).start();

    

    @Override
    protected void onDestroy() 
        super.onDestroy();
        if (myLooper!=null) 
            myLooper.quit();
            myLooper = null;
        
    

    public void sendMsg(View view)
        new Thread(new Runnable() 
            @Override
            public void run() 
                SystemClock.sleep(2000);//模拟一个耗时操作
                Message msg = new Message();
                msg.what = 1;//区分发送的消息
                msg.obj = "来自子线程的问候";
                handler.sendMessage(msg);
            
        ).start();
    

    public void sendMsg2(View view) 
        //从消息池中获取一个旧的msg,如果没有重新创建消息
        subHandler.obtainMessage(2, "我是主线程发送来的祝福").sendToTarget();
    

12.2 同线程内不同组件间的消息传递

Looper类用来管理特定线程内对象之间的消息交换(MessageExchange)。你的应用程序可以产生许多个线程。而一个线程可以有许多个组件,这些组件之间常常需要互相交换讯息。如果有这种需要,您可以替线程构造一个Looper对象,来担任讯息交换的管理工作。Looper对象会建立一个MessageQueue数据结构来存放各对象传来的消息(包括UI事件或System事件等)。每一个线程里可含有一个Looper对象以及一个MessageQueue数据结构。在你的应用程序里,可以定义Handler的子类别来接收Looper所送出的消息。

同线程不同组件之间的消息传递代码如下:

/**
 * ============================================================
 * Copyright:$TODO有限公司版权所有 (c) 2017
 * Author:   AllenIverson
 * Email:    815712739@qq.com
 * GitHub:   https://github.com/JackChen1999
 * 博客:     http://blog.csdn.net/axi295309066
 * 微博:     AndroidDeveloper
 * GitBook: https://www.gitbook.com/@alleniverson
 * <p>
 * Project_Name:HandlerDemo
 * Package_Name:com.github.handlerdemo.activity
 * Version:1.0
 * time:2017/3/1 16:23
 * des :
 * gitVersion:2.12.0.windows.1
 * updateAuthor:$Author$
 * updateDate:$Date$
 * updateDes:$TODO
 * ============================================================
 */
public classHandlerActivity extends Activity

    private Button sendBtn;
    private TextView tv;
    @Override
    protected void onCreate(Bundle savedInstanceState)
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        sendBtn=(Button)findViewById(R.id.send);
        tv=(TextView)findViewById(R.id.textview);
        sendBtn.setOnClickListener(newMyOnClickListener());
    
    class MyOnClickListener implements OnClickListener 
        @Override
        public void onClick(View v) 
            switch (v.getId()) 
                case R.id.send:
                    // 取得当前线程的Looper,此时的线程为主线程(UI线程)
                    Looper looper = Looper.myLooper();
                    // 构造一个Handler对象使之与Looper通信
                    MyHandler mHandler = newMyHandler(looper);
                    // 产生一个消息通过Handler传递给Looper
                    String msgStr = "main";
                    // 构造一个消息,这里what参数设为1,obj参数设为msgStr变量。
                    Message msg = mHandler.obtainMessage(1, 1, 1, msgStr);
                    // 发送消息,调用Handler对象的handleMessage方法
                    mHandler.sendMessage(msg);
                    break;
            
        
    
    // 自定义Handler类
    class MyHandler extends Handler 
        // 指定Looper对象来构造Handler对象,而我们平时直接使用的Handler无参构造方法实际上默认是本线程的looper,可通过查看SDk源代码了解。
        public MyHandler(Looper looper) 
            super(looper);
        
        @Override
        public void handleMessage(Message msg) 
            switch (msg.what) 
                case 1:
                    tv.setText(String.valueOf(msg.obj));
                    break;
            
        
    

说明:此程序启动时,当前线程(即主线程, mainthread)已诞生了一个Looper对象,并且有了一个MessageQueue数据结构。

  • 调用Looper类别的静态myLooper()函数,以取得目前线程里的Looper对象。looper = Looper.myLooper ();

  • 构造一个MyHandler对象来与Looper沟通。Activity等对象可以藉由MyHandler对象来将消息传给Looper,然后放入MessageQueue里;MyHandler对象也扮演Listener的角色,可接收Looper对象所送来的消息。mHandler = new MyHandler (looper);

  • 先构造一个Message对象,并将数据存入对象里。
    Message msg = mHandler.obtainMessage(1, 1, 1, msgStr);
    这里也可以这样写:

Message msg = new Message();
msg.what = 1;
msg.obj = msgStr;
  • 通过mHandler对象将消息m传给Looper,然后放入MessageQueue里。mHandler.sendMessage(msg);

此时,Looper对象看到MessageQueue里有消息m,就将它广播出去,mHandler对象接到此讯息时,会调用其handleMessage()函数来处理,于是让msgStr显示于TextView上(更新UI)。

12.3 子线程传递消息给主线程

/**
 * ============================================================
 * Copyright:$TODO有限公司版权所有 (c) 2017
 * Author:   AllenIverson
 * Email:    815712739@qq.com
 * GitHub:   https://github.com/JackChen1999
 * 博客:     http://blog.csdn.net/axi295309066
 * 微博:     AndroidDeveloper
 * GitBook: https://www.gitbook.com/@alleniverson
 * <p>
 * Project_Name:HandlerDemo
 * Package_Name:com.github.handlerdemo.activity
 * Version:1.0
 * time:2017/3/1 16:23
 * des :
 * gitVersion:2.12.0.windows.1
 * updateAuthor:$Author$
 * updateDate:$Date$
 * updateDes:$TODO
 * ============================================================
 */
public classHandlerActivity extends Activity

    private Button sendBtn;
    private TextView tv;
    private MyHandler mHandler = null;
    Thread thread;
    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        sendBtn = (Button)findViewById(R.id.send);
        tv = (TextView)findViewById(R.id.textview);
        sendBtn.setOnClickListener(newMyOnClickListener());
    
    class MyOnClickListener implements OnClickListener 
        @Override
        public void onClick(View v) 
            switch (v.getId()) 
                case R.id.send:
                    thread = new MyThread();
                    thread.start();
                    break;
            
        
    
    // 自定义Handler类
    class MyHandler extends Handler 
        // 指定Looper对象来构造Handler对象,而我们平时直接使用的Handler无参构造方法实际上默认是本线程的looper,可通过查看SDk源代码了解。  
        public MyHandler(Looper looper) 
            super(looper);
        
        @Override
        public void handleMessage(Message msg)
            switch (msg.what) 
                case 1:
                    tv.setText(String.valueOf(msg.obj));
                    break;
            
        
    
    private class MyThread extends Thread 
        @Override
        public void run() 
            // 获得当前线程的Looper对象  
            Looper curLooper =Looper.myLooper();
            // 获得主线程(UI线程)的Looper对象  
            Looper mainLooper =Looper.getMainLooper();
            String msgStr;
            if (curLooper == null) 
                mHandler = newMyHandler(mainLooper);
                msgStr = "curLooper isnull";
             else 
                mHandler = newMyHandler(curLooper);
                msgStr = "This iscurLooper";
            
            Message msg =mHandler.obtainMessage(1, 1, 1, msgStr);
            mHandler.sendMessage(msg);
        
    

Android会自动替主线程建立MessageQueue。在这个子线程里并没有建立Message Queue。所以curLooper值为null,而mainLooper则指向主线程里的Looper。于是执行mHandler= new MyHandler (mainLooper);此mHandler属于主线程

mHandler.sendMessage(msg);就将msg消息存入到主线程的MessageQueue里

mainLooper看到Message Queue里有讯息,就会作出处理,于是由主线程执行到mHandler的handleMessage()来处理消息。

12.4 在子线程中直接更新View不

以上是关于Android的消息机制的主要内容,如果未能解决你的问题,请参考以下文章

Android 面试收集录5 消息机制

Android消息机制(HandlerMessageQueueLooper)详细介绍

Android 线程与消息 机制 15问15答

Android Handler和他的小伙伴们,消息机制详解

Android的消息机制Handler

深入分析Android-Handler消息机制