Android消息机制源码分析
来源:互联网 发布:炉石传说盒子mac 编辑:程序博客网 时间:2024/05/08 09:09
在Android应用程序中,存在一个主线程我们通常叫做UI线程,可以进行界面的更新等,进行系统的消息发送。接触Android久了,就会知道,Activity的生命周期就是通过系统内部的Handler发送消息来进行回调,其中消息传递过程是Handler的消息机制;
为什么要使用Handler的消息机制?
—它的设计避免了多线程的并发执行操作,我们知道Android规定:UI只是支持单线程模型。
假如多个线程都更新UI的界面,就会发生线程不安全问题,造成数据的丢失等等;
有了Handler之后一些好事的的操作都可以放到其他子线程里面执行,执行完毕之后可通过Handler与UI主 线程进行交互,这个过程很好的避免的多线程的操作,同时增强了用户的体验。
下图是消息机制总体流程图:
它主要涉及下面四个类 Handler、MessageQueue、Message、Looper;
Message:消息
MessageQueue:消息队列,负责存储消息,它内部其实是一个单链表的结构
Handler:负责消息的发送和处理.必须要关联当前线程中唯一的Looper对象,才可以,否则抛异常
Looper:负责消息队列中消息的轮询操作,每一个线程只能有唯一的Looper对象(比如UI线程)
在UI线程被创建的时候,就会初始化一个Looper对象(是唯一的),然后在通过Looper.looper()方法(这个方法是一个无限循环方法),发现MessageQueue中有消息的时候,就会把它取出发送给Handler的handleMessage()方法处理;
对于消息队列,当有消息的时候,它会被唤醒,来处理消息,当没有消息的时候,他就会处于阻塞状态。
消息基本传递过程大体是这样的:
1.Handler发送一个Message(消息)同时这个消息会插入MessageQueue(消息队列)中;
2.然后Looper会轮询这个消息队列,发现有消息,会取出这个消息;
3.取出来之后会把它传递给原来的Handler对象处理。
通过上面我们会有一个整体认识,下面通过源代码来进行剖析:
最开始UI主线程被创建的时候,在ActivityThread内部会创建一个唯一的Looper对象:
public static void main(String[] args) { //代码省略... Process.setArgV0("<pre-initialized>"); Looper.prepareMainLooper();//创建消息循环Looper ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } AsyncTask.init(); //代码省略... Looper.loop();//执行循环消息 throw new RuntimeException("Main thread loop unexpectedly exited"); }
上面有注释的两块地方,开始了对Looper的操作处理—
第一个方法Looper.prepareMainLooper()是初始化一个Looper对象和一个消息队列
第二个方法Looper.loop()是Looper对象对消息队列进行轮询操作,发现里面有消息的时候就会发给Handler处理,没有消息的时候就Looper对象本身就会处于阻塞状态
再接着看一下Looper.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(); } } 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)); }
从这里我们可以看到,这个方法内部首先调用了prepare(false),
prepare(false)内部它先调用了ThreadLocal的get()方法,来进行判断当前UI线程中是否有Looper对象变量值,如果有的话,get方法返回就不等于null,那么这里就会抛一个异常“only one Looper may be created per thread ”意思就是每个线程只允许创建一个Looper对象,如果Looper对象已经有了那么就不能再调用这个方法了,否则会抛上面异常。这里因为是第一次启动创建,所以一定是null,不会报异常。
再往下面执行的时候调用 sThreadLocal.set(new Looper(quitAllowed));
ThreadLocal会把新new 的Looper对象设置进去,下次调用sThreadLocal.get()的时候可以取出的取出这个变量的值,当然前提是在同一个线程中(这里是UI线程)。熟悉线程的朋友,知道这个ThreadLocal类作用就是存储与当前线程有关的变量,不涉及其他线程!
执行到最后的时候,会调用Looper.myLooper()将looper对象赋值给到Looper类里面的sMainLooper变量。
public static Looper myLooper() { return sThreadLocal.get();//再当前线程中通过ThreadLocal取出我们设置的Looper对象 }
通过new Looper(quitAllowed)我们看一下Looper的带参构造方法,就会发现原来消息队列MessageQueue被Looper内部创建了.
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed);//Looper封装了消息队列 mThread = Thread.currentThread(); }
到这里,我们就会发现Android的UI线程中会有一个默认的Looper对象(而且是唯一的),它里面还封装了一个消息队列。这样Looper和MessageQueue就关联上了
还有一个Looper.loop()方法,我们到后面提到再说…
下面我们在看看Handler是如何与Looper和MessageQueue关联上的呢?
当我们在UI主线程中创建一个Handler对象的时候(比如在一个Actvity中创建 Handler mHandler=new Handler();)其实在这个过程中会把主线程里面的Looper和MessageQueue分别赋值给到Handler里面的对应变量,这个看一下 Handler的构造方发就会发现
public Handler() { this(null, false); } public Handler(Callback callback, boolean async) { //省略次要代码... mLooper = Looper.myLooper();//这里赋值Looper if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue;//这里赋值MessageQueue mCallback = callback; mAsynchronous = async; }
1,mLooper = Looper.myLooper();//这里赋值Looper,当前是在UI主线程里面,会把当前线程中唯一的一个Looper传递过来(上面已经分析了);
同理mQueue = mLooper.mQueue;//这里赋值MessageQueue,也会把与Looper关联的唯一MessageQueue传递进来;
到这里也就是说我们创建的Handler对象里面包含了主线程的Looper和MessageQueue,这样Handler就和它们关联上了;
当使用Handler对象发送消息的时候会调sendMessage();
public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); } public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } 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); }
发送消息的方法内部调用了发送延迟消息的方法,从内部知道是从开机到现在时间的上延迟了0秒,接着把这个时间当做是消息发送时间,接着调定时发送消息的方法,这个方法里面会先拿出消息队列,后面又会把这条消息插入消息队列里面。这一点从以上代码看还是比较容易理解的。接着上面代码往下继续看代码enqueueMessage(queue, msg, uptimeMillis)…
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this;//这个this不就是当前的Handler if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
msg.target = this,把Handler对象赋给了Message中的target引用,这样这条消息中的target指向Handler.
而queue.enqueueMessage(msg, uptimeMillis)就是消息队列自己插入消息的方法了。
这条条消息Message里面的target(Handler类型)引用指向了我们最开始创建的Hanlder对象
看看消息队列MessageQueue的queue.enqueueMessage(msg, uptimeMillis)是如何插入消息的:
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) { // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; } else { 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; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; }
代码比较长,其实也没有什么,就不全部贴出来了,只看重要的部分,用心看,你会明白,MessageQueue内部存储其实使用的单链表存储结构,它并不是一个什么队列容器,(而是一个消息内部包含另一个消息的引用,通过这个引用可以查找到下一个消息),通过这个方法可以将一个消息插入到消息的队尾里去,这样一条Message消息就成功插入到里面去了。
好了,前面的过程我们都了解的差不多了,我来看看最重要的一个方法 Looper.loop(),
上面已经说了,一旦消息队列俩面有消息,Looper就会轮询出来发送给Handler处理。
public static void loop() { final Looper me = myLooper();//拿到当前UI线程中唯一的Looper对象 if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue;//拿到当前UI线程中唯一MesssageQueue对象 //省略次要代码... for (;;) {//看到了吗是一个无限循环 //阻塞的方法,没有消息的时候,MessageQueue会阻塞在这里 Message msg = queue.next(); //might block if (msg == null) { // No message indicates that the message queue is quitting. return; } //省略次要代码... msg.target.dispatchMessage(msg);//这条msg.target指向我们创建的Handler //省略次要代码.. msg.recycle(); } }
在这个方法里面先是取得前线程中的Looper对象,然后拿到消息队列,之后进入到for循环,这个for循环是一个死循环,第一行代码是注释可以知道,会阻塞,其实是一旦没有消息的时候就会休眠阻塞,一旦消息来了就会唤醒处理,这个next()方法里面也是消息队列链表的取出和删除操作,大家可以自己去读原码。这里取出刚才那条Message之后会调用 msg.target.dispatchMessage(msg)方法,msg.target指向的是放送这条消息的 Hanlder对象!呵呵,继续往下看看Handler类里面这个代码吧
public void dispatchMessage(Message msg) { if (msg.callback != null) {//判断Message自身Callback接口是否为null handleCallback(msg); } else {//到了Handler来处理消息了 if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
别的不说了,就看看handleMessage(msg),这个是Handler的接口回调方法。我们最熟悉的吧~~~
复述上述过程:Handler发送消息Message的时候,消息的内部持有Handler引用targer,而target会指向当前的Handler对象,而这个Handler对象又是关联了当前线程(这里是主线程)的Looper对象和消息队列MessageQueue。
而Looper是为了重复不断从消息队列里面取出消息…
当消息被从消息对列取出的时候,会让消息Message内部持有Handler引用target指向的Handler对象处理;
这样正好是Handler自己发送和自己处理消息。
注意这里Handler是关联的主线程的Looper;所以无论是哪里的消息,都会在主线程里面处理;
当然我们自己可以让Handler关联子线程这种的Looper;所以无论是哪里的消息,都会在这个子线程里面处理;比如下面代码;
new Thread() { @Override public void run() { Looper.prepare(); Handler mThreadHandler = new Handler(Looper.myLooper()); Looper.loop(); } }.start();/**或者下面这样都可以,为什么可以自己看看源码分析有助于加深对消息机制的理解*/ HandlerThread thread = new HandlerThread("myHandlerThread"); thread.start(); Handler mHandler = new Handler(thread.getLooper());
最后总结一下:
Handler负责消息的放送和处理,Looper负责轮询消息队列中的消息。消息队列负责消息的插入和取出删除。
- android消息机制源码分析
- Android 消息机制源码分析
- Android消息机制源码分析
- Android消息机制源码分析
- Android 消息机制源码分析
- Android消息机制源码分析
- Android消息机制源码分析
- Android消息机制源码分析
- Android中的消息机制-源码分析
- Android消息机制及源码分析
- Android消息机制 Handler源码分析
- Android异步消息机制及源码分析
- Android消息处理机制(源码分析为主)
- Android的消息处理机制源码分析
- 结合源码分析android的消息机制
- Android 消息循环机制源码分析
- Android 消息循环机制源码分析
- Android 消息循环机制源码分析
- 韩顺平 javascript教学视频_学习笔记8_js系统函数_js函数调用方式
- 字符串的序列化
- 网页设计和网页开发人员常用到的Google Chrome扩展
- 树莓派+DHT11温湿度传感器+yeelink物联网云
- 使用Meshlab 的网格细分
- Android消息机制源码分析
- 虚幻4全局函数类蓝图(蓝图函数宏库)
- jersey获取各个参数的总结
- myeclipse10 2014(2015),终极破解方案
- 【YouXue 1035】计算cosx的近似值
- LeetCode 63. Unique Paths II
- Spring和springmvc父子容器关系
- 【C#基础】装箱与拆箱
- componentsJoinedByString和componentsSeparatedByString的使用