Android消息机制,Handler源码分析

一、消息机制概述
在Android中使用消息机制,Handler是是其中一个也是最常用的一个。Handler是Android消息机制的上层接口。Handler的使用过程很简单,通过它可以轻松地将一个任务切换到Handler所在的线程中去执行。通常情况下,Handler的使用场景就是更新UI。

public class Activity extends android.app.Activity {
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            System.out.println(msg.what);
        }
    };
    @Override
    public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
        super.onCreate(savedInstanceState, persistentState);
        setContentView(R.layout.activity_main);
        new Thread(new Runnable() {
            @Override
            public void run() {
                Message message = Message.obtain();
                message.what = 1;
                mHandler.sendMessage(message);
            }
        }).start();
    }
}

在onCreate 方法中 创建的子线程中 进行一个耗时操作,比如网络请求等等一系列,将请求结果由Handler进行发送,在由主线程中接口并且更新UI显示。再次传递的过程中,还需要几大助手来完成。
2.消息机制的模型
消息机制主要包含:MessageQueue,Handler和Looper这三大部分,以及Message,下面我们一一介绍。
MessageQueue:消息队列,但是它的内部实现并不是用的队列,实际上是通过一个单链表的数据结构来维护消息列表,因为单链表在插入和删除上比较有优势。主要功能有传递获取消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next);
Handler:消息辅助类,主要功能向消息池发送各种消息事件(Handler.sendMessage)和处理相应消息事件
(Handler.handleMessage); 使用what 标识进行 对应的处理
Looper:不断循环执行(Looper.loop),从MessageQueue中读取消息,按分发机制将消息分发给目标处理者。
Message:需要传递的消息,可以传递数据;如我们网络请求的数据 传递

3.消息机制的来龙去脉

Thread(new Runnable() {
            @Override
            public void run() {
                Message message = Message.obtain();
                message.what = 1;
                mHandler.sendMessage(message);
            }
        }).start();

首先在子线程中使用handler的sendMessage发送消息。中间人由MessageQueue.enqueueMessage来进行完成
消息队列的添加。当通过Looper.loop方法进行轮循的时候 MessageQueue.next 方法会从线程池中一个一个的进行读取消息。然后会调用Handler.dispatchMessage 进行传递消息。然后Handler在主线程中进行接收消息,进行处理。此时整个消息传递处理就结束了。
下面一张图片来看看整个工作流程
在这里插入图片描述
其中主要的还是由MessageQueue,Handler和Looper三者组成,我们来分析一下这三者其中的关系
每个线程中只能存在一个Looper,Looper是保存在ThreadLocal中的。主线程(UI线程)已经创建了一个Looper,所以在主线程中不需要再创建Looper,但是在其他线程中需要创建Looper或者使用主线程的Looper(Looper.getMainLooper())。每个线程中可以有多个Handler,即一个Looper可以处理来自多个Handler的消息。 Looper中维护一个MessageQueue,来维护消息队列,消息队列中的Message可以来自不同的Handler。
在这里插入图片描述
这是一张整体的执行流程图我们下面来分析

二、消息机制的源码解析
1.Looper
要使用消息机制,首先我们需要创建一个Looper。
1.在主线程中我们不需要创建Looper ,Activity在初始化的时候已经为我们创建好Looper对象

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

2.在其他线程中我们需要自己初始化Looper 对象

  1. Looper.prepare() 在子线程中新建Lopper方法
 public static void prepare() {
     prepare(true);
 }

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

这段代码中我们可以看出一个线程只能拥有一个Looper对象 创建好的Lopper对象储存在ThreadLocal中。
ThreadLocal其实是一个本地的线程存储区域(简称TSL)不同的线程之间的TSL是不能进行相互访问的

1.1开启Looper

public static void loop() {
 final Looper me = myLooper();  //获取TLS存储的Looper对象 
 if (me == null) {
     throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
 }
 final MessageQueue queue = me.mQueue;  //获取Looper对象中的消息队列

 Binder.clearCallingIdentity();
 final long ident = Binder.clearCallingIdentity();

 for (;;) { //进入loop的主循环方法
     Message msg = queue.next(); //可能会阻塞,因为next()方法可能会无限循环
     if (msg == null) { //消息为空,则退出循环
         return;
     }

     Printer logging = me.mLogging;  //默认为null,可通过setMessageLogging()方法来指定输出,用于debug功能
     if (logging != null) {
         logging.println(">>>>> Dispatching to " + msg.target + " " +
                 msg.callback + ": " + msg.what);
     }
     msg.target.dispatchMessage(msg); //获取msg的目标Handler,然后用于分发Message 
     if (logging != null) {
         logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
     }

     final long newIdent = Binder.clearCallingIdentity();
     if (ident != newIdent) {

     }
     msg.recycleUnchecked(); 
 }
}

这一大段代码中解析了,从TSL中获取Looper对象 然后从Looper对象中获取消息对象 对消息队列进行一个循环
同时由MessageQueue.next 方法进行获取队列池中的消息
2.创建Handler

public Handler() {
    this(null, false);
}

public Handler(Callback callback, boolean async) {
   .................................
    //必须先执行Looper.prepare(),才能获取Looper对象,否则为null.
    mLooper = Looper.myLooper();  //从当前线程的TLS中获取Looper对象
    if (mLooper == null) {
        throw new RuntimeException("");
    }
    mQueue = mLooper.mQueue; //消息队列,来自Looper对象
    mCallback = callback;  //回调方法
    mAsynchronous = async; //设置消息是否为异步处理方式
}

上面一段代码中两个Handler初始化方法对于无参构造 默认采用当前线程的Looper对象 回调为空。
3.发送消息
发送消息有多种方式,但是归根结底都是调用了sendMessageAtTime()方法。
post方法

 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 postAtTime(Runnable r, Object token, long uptimeMillis)
    {
        return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
    }
 public final boolean postDelayed(Runnable r, long delayMillis)
    {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }

send方法

public final boolean sendMessage(Message msg)
 {
     return sendMessageDelayed(msg, 0);
 }
public final boolean sendEmptyMessage(int what)
 {
     return sendEmptyMessageDelayed(what, 0);
 } 
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
     Message msg = Message.obtain();
     msg.what = what;
     return sendMessageDelayed(msg, delayMillis);
 }
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
     Message msg = Message.obtain();
     msg.what = what;
     return sendMessageAtTime(msg, uptimeMillis);
 }
public final boolean sendMessageDelayed(Message msg, long delayMillis)
 {
     if (delayMillis < 0) {
         delayMillis = 0;
     }
     return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
 }

当前在子线程更新UI的方法 runOnUIThread其实也是发送消息通知主线程更新UI,最终也会调用sendMessageAtTime()方法。

 public final void runOnUiThread(Runnable action) {
       if (Thread.currentThread() != mUiThread) {
           mHandler.post(action);
       } else {
           action.run();
       }
   }

所有的发送方式都会调用sendMessageAtTime方法,我们来看看这个方法是如何来实现的

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

从这方法看出,其实还是调用了enqueueMessage方法 向对队列中添加一个消息

   private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
       msg.target = this;
       if (mAsynchronous) {
           msg.setAsynchronous(true);
       }
       //调用MessageQueue的enqueueMessage方法
       return queue.enqueueMessage(msg, uptimeMillis);
   }
boolean enqueueMessage(Message msg, long when) {
    // 每一个Message必须有一个target
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }
    synchronized (this) {
        if (mQuitting) {  //正在退出时,回收msg,加入到消息池
            msg.recycle();
            return false;
        }
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            //p为null(代表MessageQueue没有消息) 或者msg的触发时间是队列中最早的, 则进入该该分支
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked; 
        } else {
            //将消息按时间顺序插入到MessageQueue。一般地,不需要唤醒事件队列,除非
            //消息队头存在barrier,并且同时Message是队列中最早的异步消息。
            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;
            prev.next = msg;
        }
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

enqueueMessage 中主要是手机Message 中的消息,先确认Message 中是否设置了target标识然后在对消息进行一个添加到MessageQueue
4.获取消息
当发送消息后,消息都会存储在MessageQueue队列中,这是我们需要开始创建好的Looper中loop方法进行一个循环不断获取其中的消息,在loop循环中主要由MessageQueue.next 方法来进行获取下一条消息。Looper我们已经在上面已经分析过了,下面我们来看看 next方法是如何实现的。

Message next() {
    final long ptr = mPtr;
    if (ptr == 0) { //当消息循环已经退出,则直接返回
        return null;
    }
    int pendingIdleHandlerCount = -1; // 循环迭代的首次为-1
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        //阻塞操作,当等待nextPollTimeoutMillis时长,或者消息队列被唤醒,都会返回
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                //当消息Handler为空时,查询MessageQueue中的下一条异步消息msg,为空则退出循环。
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    //当异步消息触发时间大于当前时间,则设置下一次轮询的超时时长
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // 获取一条消息,并返回
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    //设置消息的使用状态,即flags |= FLAG_IN_USE
                    msg.markInUse();
                    return msg;   //成功地获取MessageQueue中的下一条即将要执行的消息
                }
            } else {
                //没有消息
                nextPollTimeoutMillis = -1;
            }
         //消息正在退出,返回null
            if (mQuitting) {
                dispose();
                return null;
            }
            ...............................
    }
}

因为是循环操作,不能保证消息消息的及时性,nativePollOnce方法对当前线程进行一个阻塞操作其中 nextPollTimeoutMillis表示下一个消失来前还需要等待的消息
5.分发消息
在next方法获取到消息后,Loop方法 会调用dispatchMessage方法进行一个消息的分发位于(上文Loop方法中下位置)
下面就来具体看下dispatchMessage(msg)方法的执行流程。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        //当Message存在回调方法,回调msg.callback.run()方法;
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            //当Handler存在Callback成员变量时,回调方法handleMessage();
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //Handler自身的回调方法handleMessage()
        handleMessage(msg);
    }
}
private static void handleCallback(Message message) {
        message.callback.run();
    }

dispatchMessage方法中我们看到是否有callback 回调进行一个判断
当Message的msg.callback不为空时,则回调方法msg.callback.run();
当Handler的mCallback不为空时,则回调方法mCallback.handleMessage(msg);
最后调用Handler自身的回调方法handleMessage(),该方法默认为空,Handler子类通过覆写该方法来完成具体的逻辑。
消息分发的优先级:
Message的回调方法:message.callback.run(),优先级最高;
Handler中Callback的回调方法:Handler.mCallback.handleMessage(msg),优先级仅次于1;
Handler的默认方法:Handler.handleMessage(msg),优先级最低。
在这里插入图片描述
部分信息来自于 https://github.com/LRH1993/

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值