android线程管理四(Looper,Handler,Message)

本文深入解析Android中的消息机制,包括Message、Handler、MessageQueue和Looper等核心组件的工作原理及交互流程,帮助读者理解如何在多线程环境中实现UI更新。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

      我们知道在android中每个Activity都有一个UI线程,在这里线程中主要就是初始化UI、更新UI、处理事件等。而更新UI的数据大多都是从网络上获取,但 进行网络操作都是在子线程中进行的,以此来避免ANR异常。在子线程是不能直接进行修改UI的,在《进程与线程》这篇文章中提到过修改UI元素有三种方式:
(1)使用runOnUiThread、post等方法;
(2)使用Handle机制;
(3)使用 异步任务AsyncTask;
本篇主要分析下 Handle机制工作的原理, 转载请注明出处:小石头的博客   http://blog.youkuaiyun.com/lu1024188315/article/details/74518599

一  消息机制的核心类

      消息机制核心类有:Message、Handler、MessageQueue、Looper。

Message

      从字面理解就是消息,它的作用就是储存信息,它是 线程间交流的信息,子线程从网络上获取数据,把数据存储在Message中,然后发送这个Message给UI线程进而就可以更新UI元素。
(1) 成员变量
它实现了Parcelable接口,它有几个重要的成员变量:
public final class Message implements Parcelable {
    //信息码用于区分Message
    public int what;
    //存储int 数据
    public int arg1;
    //存储int 数据 
    public int arg2;
    //存储Object类型数据 例如从网络上获取的数据
    public Object obj;
    ......
    /*package*/ Handler target;//存储Handler对象
    /*package*/ Runnable callback;//存储Runnable对象
    ......
}
(2)获取实例
      一般情况下获取一个对象的实例,首先会想到通过构造方法,没错Message也可以通过构造方法:
Message#构造方法:
public Message() {
}
    但这样会有个问题,在一个App中可能访问网络比较频繁,每次获取数据后,都通过new 实例化一个Message,这样将会大大增加了内存开销。那么我们应该怎么更好地获取这个对象的实例呢?当然是通过obtain方法:
Message# obtain:
public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) 
            //sPool是Message类型,如果sPool不为null,那么直接使用它,
           //如果为null那么就通过构造方法new一个实例,其实这个方法本质就是一个单例模式
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
}

public static Message obtain(Handler h, Runnable callback) {
        Message m = obtain();
        m.target = h;
        //保存callback实例到本地变量m.callback中,如果m.callback不为null在消息处理的过程中首先调用它进行处理Message
        m.callback = callback;
        return m;
    }
     说明,当然还有其他 obtain方法拱使用,这里就不一一列出了,同时Handle中也有获取 Message实例的方法,这个方法也很常用:
Handle # obtainMessage:
public final Message obtainMessage(){
        return Message.obtain(this);
}
public final Message obtainMessage(int what, Object obj){
        return Message.obtain(this, what, obj);
}
public final Message obtainMessage(int what, int arg1, int arg2){
        return Message.obtain(this, what, arg1, arg2);
}
public final Message obtainMessage(int what, int arg1, int arg2, Object obj){
        return Message.obtain(this, what, arg1, arg2, obj);
}
       说明,其本质就是调用了 Message的 obtain方法。

Handler

      Handler处理者,是Message的主要处理者,负责Message的发送,Message内容的执行处理。子线程就是通过Handler对象实例调用sendMessage(Message)发送信息。处理信息是在该类的 handleMessage(Message)方法,它是处理这些Message的操作内容,例如Update UI。通常需要子类化Handler来实现handleMessage方法。
(1)重要成员变量
mCallback: Callback类型,作用就是保存构造函数传过来的 Callback对象引用,以便对 Message进行处理。
(2)构造函数
Handler#构成函数:
public Handler() {
     this(null, false);
}
public Handler(Callback callback) {
        this(callback, false);
}
public Handler(Looper looper) {
        this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
}
public Handler(boolean async) {
        this(null, async);
}
public Handler(Callback callback, boolean async) {
       ......
       //获取当前线程的looper对象引用
        mLooper = Looper.myLooper();
        if (mLooper == null) {//如果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;
}
public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        //获取当前线程looper的消息队列
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
}
(3)发送信息
Handler# sendMessage:
public final boolean sendMessage(Message msg){
        return sendMessageDelayed(msg, 0);
}
说明,这里发送的是一个Message对象,其实它还可以发送一个Runnable对象,如下:
Handler#post
public final boolean post(Runnable r){
       return  sendMessageDelayed(getPostMessage(r), 0);
}
说明,发送信息的详细流程下面具体分析。
(4)处理信息
    Handler处理信息是在handleMessage方法中进行的,在使用的过程一般是重写Handler中handleMessage方法
Handler# handleMessage:

  
public void handleMessage(Message msg) {
}
说明, 处理 信息的详细流程下面具体分析。

Looper

      顾名思义它是循环着,它 Handler和消息队列之间通讯的桥梁 首先通过Handler发送的消息,会被传递给Looper,Looper把消息放入队列,然后 Looper 把消息队列里的消息广播给所有的 Handler,进而在 handleMessage方法中对消息进行处理。   Looper是每条线程里的Message Queue的管家。Android没有全局的Message Queue,而Android会自动替主线程(UI线程)建立Message Queue,但在子线程里并没有建立Message Queue。所以调用Looper.getMainLooper()得到的主线程的Looper不为NULL,但调用Looper.myLooper() 得到当前线程的Looper就有可能为NULL。每个线程中只能有一个 Looper对象引用
(1)重要的成员变量
mQueue :MessageQueue类型,消息队列用于存储消息,它在Looper中创建的,创建过程如下:
Looper#prepare:
 //初始化当前的线程为looper线程
    public static void prepare() {
        prepare(true);
    }
Looper#构造函数:
 private Looper(boolean quitAllowed) {//创建消息队列对象引用
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
说明,在这里 MessageQueue与线程的Looper关联一起了,这是非常重要的一步,无论是把消息放置到 MessageQueue中,还是从 MessageQueue取消息都是在以这个关联的情况下进行的。
获取这个 MessageQueue对象引用有两种方式:
Looper#myQueue:
//通过Looper获取当前线程的消息队列
    public static @NonNull MessageQueue myQueue() {
        return myLooper().mQueue;
    }
Looper#getQueue:
 //获取当前线程的消息队列
    public @NonNull MessageQueue getQueue() {
        return mQueue;
    }
(2)源码分析
public final class Looper {
    // 线程局部变量在《线程管理三(ThreadLocal)》中,ThreadLocal作用已经讲过
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    //UI线程Looper对象
    private static Looper sMainLooper; 
    //线程队列 
    final MessageQueue mQueue;
    final Thread mThread;
    ......
    //初始化当前的线程为looper线程
    public static void prepare() {
        prepare(true);
    }
   //初始化当前的线程为looper线程,和上面的方法不同之处就是可以自己设置是否允许退出looper。
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {//一个线程中只能有一个looper对象引用
throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); } //初始化当前的UI线程为looper线程 public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) {//试图在UI线程再创建一个looper实例,抛出异常
throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } } //获取UI线程中的Looper对象引用,此对象引用一定不会为null public static Looper getMainLooper() { synchronized (Looper.class) { return sMainLooper; } } //循环处理消息队列 public static void loop() { 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; } ....... try {//target就是handle类型,调用handle的dispatchMessage对消息进行处理 msg.target.dispatchMessage(msg); } finally { ....... } ......//回收资源 msg.recycleUnchecked(); } } //获取当前线程的looper对象引用,此对象引用可能为null public static @Nullable Looper myLooper() { return sThreadLocal.get(); } //通过Looper获取当前线程的消息队列 public static @NonNull MessageQueue myQueue() { return myLooper().mQueue; } private Looper(boolean quitAllowed) {//创建消息队列对象引用 mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } //判断当前线程是否是looper线程 public boolean isCurrentThread() { return Thread.currentThread() == mThread; } ...... //退出循环 public void quit() { mQueue.quit(false); } //安全地退出循环 public void quitSafely() { mQueue.quit(true); } //获取当前环境下的线程 public @NonNull Thread getThread() { return mThread; } //获取当前线程的消息队列 public @NonNull MessageQueue getQueue() { return mQueue; } ...... }

4 MessageQueue

       用来存放通过Handler发布的消息,通常附属于某一个创建它的线程,可以通过Looper.myQueue()得到当前线程的消息队列, 按照先进先出执行。
      每个 MessageQueue 都会有一个对应的Handler。Handler会向 MessageQueue 通过两种方法发送消息:sendMessage或post。这两种消息都会插在 MessageQueue 队尾并按先进先出执行。但通过这两种方法发送的消息执行的方式略有不同:通过sendMessage发送的是一个message对象,会被 Handler的handleMessage()函数处理;而通过post方法发送的是一个runnable对象,则会自己执行。
(1)重要的成员变量
mMessages:这是一个非常重要的成员变量,它对我们理解Message是如何放置到消息队列中,和如何取出起到关键性的作用,它Message类型,
用于存储消息队列中队头的Message,遍历 消息队列以它为坐标开始的。

二 发送消息的流程

      在上面已经提到了发送消息的方法,是通过调用 Handler的方法 sendMessage:
Handler# sendMessage:
public final boolean sendMessage(Message msg){
        return sendMessageDelayed(msg, 0);
}
说明,那么接着往下看:
Handler# sendMessageDelayed:
public final boolean sendMessageDelayed(Message msg, long delayMillis){
        if (delayMillis < 0) {//延迟时间小于0时,让其默认值为0
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
}
Handler# sendMessageAtTime:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;//mQueue就是刚刚提到的handle的重要成员变量,这个变量是在looper中实例化的
        if (queue == null) {//如何mQueue为null 则抛出异常
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
}
Handler# enqueueMessage:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis){
        //保存当前的handle对象引用到msg的target中,target是handle重要的成员变量上文已经提到过了
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //调用MessageQueue的enqueueMessage方法完成发送信息的过程
        return queue.enqueueMessage(msg, uptimeMillis);
}
MessageQueue#enqueueMessage:
boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {//msg.target为null即msg.target中存储的handle对象引用为null抛异常
            throw new IllegalArgumentException("Message must have a target.");
        }
        ......
        synchronized (this) {
            if (mQuitting) {
                //如何该线程looper已经结束,那么将抛出异常,例如在这之前你调用了looper的quit/quitSafely方法
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;//把当地保存的Message赋值给p
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // p == null证明在这之前还没有谁发送过Message
               //让msg.next指向p,其实就是指向了null(可以看作是指向队尾),
              //这个过程直接把msg插入队列中了,这时队列中还没有Message
                msg.next = p;
                mMessages = msg;//保存Message到本地变量mMessages中,即保存队头Message
                needWake = mBlocked;
            } else {
                // p != null这时队列中已经有Message
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;// 临时变量,保存当前p的指向 
                for (;;) {
                    //这时队列中已经有Message,要遍历这个消息队列到队尾,然后把msg插入到队尾
                   //为什么要进行这一步?因为一个线程中可能有多个handle对象引用,
                  //如果多个handle对象引用同时调用sendMessage方法发送信息的话,如果不遍历
                 //直接往消息队列中,那么msg插入的位置可能就不是队尾了,也就是说msg.next指向的不一定是队尾
                    prev = p;//保存当前p的指向到prev中
                    p = p.next;//指针下移
                    if (p == null || when < p.when) {
                        break;//p == null证明已经到队尾,停止遍历
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // 修改msg.next的指向,即指向队尾
                prev.next = msg;//把msg插入到消息队列,此时prev.next的指向就是msg要插入的位置
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                //调用native函数完成把msg放置到消息队列中的过程
                nativeWake(mPtr);
            }
        }
        return true;
}
说明,把消息放置到消息队列中时,按照队列的规则是要把 消息插入到队尾,这种情况下就 不得不考虑多个handle的情况,需要对消息队列进行遍历,准确来说是从p当前的指向即mMessages(这里把p看作是指针更好理解些)遍历到队尾,最终把msg插入到队尾。过程如下图:

     由handle创建的Message通过Looper把Message插入到消息队列中,此时消息队列已经有了五Message。

三 处理消息的流程

      到这里应该已经知道 Message是在handle的handleMessage的方法中进行处理的,那么它又是如何与MQ(MessageQueue)关联一起的呢?如果够细心,你会发现在Looper的loop方法调用了dispatchMessage方法,如下:
Looper#loop:
/循环处理消息队列
    public static void loop() {
        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(); // 调用next获取下一个msg 下面会详细介绍这个方法
            if (msg == null) {//msg == null 遍历结束
                // No message indicates that the message queue is quitting.
                return;
            }

           .......
            try {//target就是handle类型,调用handle的dispatchMessage对消息进行处理
                msg.target.dispatchMessage(msg);
            } finally {
               .......
            }
           ......//回收资源
            msg.recycleUnchecked();
        }
    }
Handle#dispatchMessage:
public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            //第一步:callback为Message的重要成员变量上文有介绍,其类型为Runnable,如果其不为null,
           //则调用handleCallback方法处理msg,其实内部就是调用Runnable的run方法。
            handleCallback(msg);
        } else {//第二步:如果callback为null,mCallback不为null,mCallback为Handle的重要成员变量,
             //其类型为Callback,上文有介绍,这样情况下调用Callback的handleMessage方法。
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //第三步:如果callback为null,mCallback也为null,那么将会调用Handle中handleMessage方法进行处理Message
            handleMessage(msg);
        }
}
说明,Message的处理过程可分为三步,如上,其实在介绍Handle的时候,提到的Message处理方式就是这里的第三种情况,说明我们使用的是最低级的Message处理方式。
MessageQueue#next分析:
Message next() {
        final long ptr = mPtr;
        if (ptr == 0) {//如果ptr == 0 证明已经退出looper,返回null
            return null;
        }
       ......
        for (;;) {//循环遍历消息队列
           ......
            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;//队头Message赋值给msg
                if (msg != null && msg.target == null) {//如果设置了handle不会执行这块代码
                    ......
                }
                if (msg != null) {
                    if (now < msg.when) {
                        ......
                    } else {
                        //这一块才是关键性的代码,在上面已经介绍过,往消息队列中存放消息的时候p的指针从队头往队尾走,
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;//msg.next指向的Message保存到变量mMessages,即修改队头
                        }
                        msg.next = null;//队头的Message已经取过,使它指向null,即销毁队头,
                        //此时队头变为msg,即msg.next的 后一位
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    ......
                }
                ......
            }
             ......
        }
    }
Message取的过程如下图:


四 总结

Handle机制的大概流程如下:
 (1) 在Looper.loop()方法运行开始后,循环地按照接收顺序取出MessageQueue里面的非NULL的Message。
 (2)一开始Message Queue里面的Message都是NULL的。当Handler.sendMessage(Message)到MessageQueue,该函数里面设置了那个Message对象的target属性是当前的Handler对象。随后Looper取出了那个Message,则调用 该Message的target指向的Hander的dispatchMessage函数对Message进行处理。在dispatchMessage方法里,如何处理Message则由用户指定,三个判断,优先级从高到低:
    a:Message里面的Callback,一个实现了Runnable接口的对象,其中run函数做处理工作;
    b:Handler里面的mCallback指向的一个实现了Callback接口的对象,由其handleMessage进行处理;
    c: 处理消息Handler对象对应的类继承并实现了其中handleMessage函数,通过这个实现的handleMessage函数处理消息。
    由此可见,我们常使用的handleMessage方法是优先级最低的!
( 3)Handler处理完该Message (update UI) 后,Looper则设置该Message为NULL,以便回收!
    在网上有很多文章讲述主线程和其他子线程如何交互,传送信息,最终谁来执行处理信息之类的,个人理解是最简单的方法——判断Handler对象里面的Looper对象是属于哪条线程的,则由该线程来执行!
好,本篇到此为止。

参考文献:






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值