Android开发中Handler、Looper、MessageQueue的原理
来源:互联网 发布:淘宝保健品怎么发布 编辑:程序博客网 时间:2024/05/24 01:49
今天对Android中的Handler Message Looper这三个类从源码角度深入理解其用法。handler类是Android更新ui状态经常用到的异步方式,也是android进程之间通信的一种方式,是一种异步消息处理线程,实现异步线程需要解决的具体问题如下:
1,每个异步线程内部包含一个消息队列(MessageQueue),队列的消息一般采用排队机制,就是先进先出(FIFO)。
2,线程的执行体重使用while(true)进行无限循环,循环体重从消息队列中取出消息,并根据Message的来源进行相应的回调函数处理(CallBack接口)。
3,其他外部线程可以向本线程的消息队列发送消息,消息队列内部读/写操作必须进行加锁,就是不能同时读/写操作。
为了更好理解Handler的工作原理先简单介绍下Handler、Loop、MessageQueue这三个的关系
1、MessageQueue:消息队列,采用FIFO的方式管理Message;
2、Loop:管理MessageQueue,不断从队列里面取出消息,根据Message的信息找到发送该消息的Handler的回调函数进行相应处理
3、Handler:发送Message到MessageQueue,并处理Message信息
这样一来,关于Handler来处理异步消息的过程我们可以这样总结:首先创建一个MessageQueue队列,然后Handler将Message发送到MessageQueue中,然后Loop来从MessageQueue中取出Message,根据消息的target信息去回调相应Handler的函数。接下来我们从源码去看这个过程是怎么一步一步实现的。
首先来看个简单的例子便于我们讲解:
`public class MainAcitivty extends Acitivity{ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn=(Button)findViewById(R.id.btn); btn.setOnClickListener(new OnClickListener(){ @Override public void onClick(View arg0) { Message msg=Message.obtain(); msg.what=0x123; handler.sendMessage(msg); }});} final Handler handler=new Handler() { public void handleMessage(Message msg) { if(msg.what==0x123) btn.setBackgroundColor(Color.BLUE); } };}/*这段代码的主要实现点击一下按钮,按钮就会变蓝色,这是通过handler来实现的,当然实际项目中肯定不是这么写,这里只是为了讲解handler的使用才特别写的例子。*/首先来看下Handler的构造函数: public Handler() { this(null, false); //继续调用重载的构造函数 } public Handler(Callback callback, boolean async) { 。。。 //省略了部分代码,着重看下面的代码 mLooper = Looper.myLooper();` 1 if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; 2 mCallback = callback; mAsynchronous = async; } /* 这段代码中看到标记1那行代码,非常重点!!mLooper是Looper类型这个代码就是给mLooper获取当前线程的Looper对象,那Looper怎么产生的呢又是什么时候产生的呢,我们去看Looper这个类的源码的myLooper方法*/ public static @Nullable Looper myLooper() { return sThreadLocal.get(); }/*这个地方我们看到有个sThreadLocal变量,这个是什么东西呢,原来这个是Looper的线程局部变量,ThreadLocal,简单介绍下这个类型,这是个线程私有变量,可以将线程内部的变量和当前线程匹配起来,只有当前线程能够访问,这里可以简单的理解为sThreadLocal存了Looper对象,那么什么时候存的Looper对象呢,通过查找找到这么个方法*/ private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { /*这句是说如果调用prepare这个方法的时候,sThreadLocal里面已经有Looper对象了那么就会抛出异常,这就是说在每个线程中只会有一个Looper对象,这也好理解,因为Looper对象是管理队列的,而每个线程要保证只有一个队列,不然就会造成队列管理混乱呀,所以线程中只会有一个Looper对象和一个队列。所以不要重复去调用prepare()这个方法,否则要抛出异常的*/ throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); /*看到这句没,就是这里new了一个Looper对象,但是这个方法是private啊,外部是没法方位的,只有内部方法才可以访问啊, 别急再找找 ,果然找到了,先来看看Lopper的构造函数*/ } /*这个构造函数就是说了每个Looper对象生成的时候就会创建一个队列,所以队列的创建不是在别的地方就是在Looper对象的构造函数里面的,这也就解释了Looper管理队列的原理,线程的队列是存在Looper对象的mQueue变量里面的*/ private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread();//存了当前的线程 }/*下面这个方法就调用了上面 prepare(boolean quitAllowed)的方法,继而new 了一个Loooper对象,所以在很多参考书上会在new Handler对象之前一定要有这么一句Looper.prepare(),现在懂了吧。*/ public static void prepare() { prepare(true); }
好了这些工作做完后我们再回到刚才标记1的地方,就是mLooper = Looper.myLooper();`这句,经过前面的Looper的方法调用,我们已经生成了Looper对象和MessageQueue队列了,接下来继续看下面的代码,下面的判断里面说if(mLooper==null)就抛出异常 “Can’t create handler inside thread that has not called Looper.prepare()”这个也说了需要调用Looper.prepare(),这也验证了刚才我们走的源码流程。标记2那个地方就是将mQueue队列添加到当前这个Handler对象里面,而CallBack这里我们是没有设置那么就是null,以后写代码的时候可以自己实现这个回调函数哦。好了,这样在new一个Handler之后我们就完成了这么几个工作,在当前线程中创建了一个Looper对象和MessageQueue队列,并且将队列和Looper对象都存到当前的Handler对象里面了,这样就可以让Handler将消息发送到队列里面了。
接下来我们来看看Handler是怎么将消息发送到队列里面,Looper是怎么管理队列取消息然后再回调相应的函数呢。
我们前面说过Message是通过Handler来发送的,那么必然我们就要去看看Handler这个类里面关于发送的一些函数了
我们就以sendMessage(Message msg)这个为例子好了
public final boolean sendMessage(Message msg) { /*继续调用重载函数,根据函数名字就可以判断这是一个延迟发送的函数,只是我们没设置延迟的时间,默认就为0了*/ 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); } private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { /*这句很重点,这个msg的target是什么东东呢,所以这里就是我们前面说的关于Message的源头信息,就是说我得知道这个Message是谁发的呀,毕竟有时候Handler对象有很多对吧,这里就是设置Message的Handler对象。 */ msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } /*继续看下面的重载函数, 其实所有的Handler向队列发送消息最后都会调用下面的队列函数, queue是个MessageQueue对象,去看看这个源码*/ return queue.enqueueMessage(msg, uptimeMillis); }
/*下面的代码就是在MessageQueue这个类里面的,就是queue.enqueueMessage(msg, uptimeMillis)这个方法,首先是获得自身的同步锁synchronized (this),接着这个msg跟MessageQueue实例的头结点Message进行触发时间先后的比较,
如果触发时间比现有的头结点Message前,则这个新的Message作为整个MessageQueue的头结点,如果阻塞着,则立即唤醒线程处理
如果触发时间比头结点晚,则按照触发时间先后,在消息队列中间插入这个结点
接着如果需要唤醒,则调用nativeWake函数,这个函数会经过一系列的调用最终会唤醒Looper对象的Loop方法里面取message消息*/
boolean enqueueMessage(Message msg, long when) { 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) { 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; 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 { // Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. 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; }
到这里Handler的发送消息完成了,那么在哪里实现了从队列取数据呢,前面我们说过Looper对象是管理队列的,那么很显然,肯定是Looper对象里面来实现取数据的。看下的代码
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;//获得Looper管理的队列,其实就是当前线程的队列 // 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); } msg.target.dispatchMessage(msg); /*这个地方获取到消息后就去调用target的dispatchMessage的方法,就是发送该消息的Handler,这里就是去执行Handler里面的方法了。我们着重看handler里面的就行了*/ 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.recycleUnchecked(); } }
/*看下面的Handler的dispatchMessage这个方法 ,message的callback是个CallBack是个Runnable类型,就是说如果你设置了一个Runnable类型的对象,将它添加到Message里面了,那么Handler处理的时候就会回调改Runnable对象的run方法,举个例子说 msg=Message.obtain(Handler handler,Runnable r);这样就会执行r.run方法里面的操作了
*/
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); 1 } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { 2 return; } } handleMessage(msg); 3 } }
我们先来看看标记1的代码,就是去执行了Runnable对象的run方法了
private static void handleCallback(Message message) {
message.callback.run();
}
我们再来看看标记2的代码,此处的CallBack类型不是Runnable类型了,是个Handler的内部接口,看下这个内部接口的定义吧,这里有个函数,是handleMessage方法,显然如果设置了Handler的这个接口,就需要实现这个方法,什么时候设置这个CallBack接口呢,就是Handler的构造函数的时候我们那会说CallBack=null了,所以如果你要将处理消息的代码设置到CallBack这个接口里的话,就需要将这个接口在Handler的构造函数中传入就行了。
public interface Callback { public boolean handleMessage(Message msg); }
显然我们这里是没有设置CallBack接口的,所以我么你直接看标记3这个代码handleMessage(msg);是不是很眼熟,就是我们在MainActivity里面new Handler的时候重写的handleMessage(msg)这个方法,所以到这里就会调用这个方法,而源码里面的这个方法是什么都没做,所以需要我们去具体实现相应的逻辑处理代码
public void handleMessage(Message msg) { }
至此,关于Handler的整个发送消息然后消息入队列,然后从队列里面取出消息,最终在Handler里面处理消息,这个过程都已经说完了,那么前面说了在new一个Handler对象之前必须要Looper.prepare()生成当前线程的Looper对象和队列,那么我们在MainActivity里面好像没有执行这个啊,因为在ui线程启动的时候,AcitiviyThread已经帮我们生成了Looper对象和队列,也已经在Looper.loop()了,不断在读取队列的消息了,只是刚开始没消息,队列是处于阻塞状态下,所以在子线程中如果需要new一个Handler的话,必须要先Looper.prepare(),生成Looper和相应的队列,而且只能一次不能重复,然后再开启loop()这个方法不断去队列读取消息,具体例子的话我不举例了,因为是第一次写博客,所以很多语言组织上存在很多瑕疵,如果有什么建议或者错误的地方恳请大家帮忙指正,我们在后面继续更新我的博客,写一些关于Java和Android方面的知识,也是自己最近学习的心得吧,和大家一起交流交流
- Android开发中Handler、Looper、MessageQueue的原理
- Android中Thread、Handler、Looper、MessageQueue的原理分析
- Android中Thread、Handler、Looper、MessageQueue的原理分析
- Android中Thread、Handler、Looper、MessageQueue的原理分析
- Android中Thread、Handler、Looper、MessageQueue的原理分析
- Android中Thread、Handler、Looper、MessageQueue的原理分析
- android Handler Looper MessageQueue机制的原理
- Android中Looper, Handler, MessageQueue的理解
- Android开发:Handler、Looper、MessageQueue
- Android开发 Handler MessageQueue Looper消息循环原理分析
- Android中Handler,Looper和MessageQueue工作原理解析
- Android中线程间通信原理分析:Looper,MessageQueue,Handler
- Android 中线程间通信原理分析:Looper, MessageQueue, Handler
- Android中的Handler、Looper和MessageQueue的使用以及原理
- Android中的基础----Handler、Looper、MessageQueue的工作原理
- Android开发知识(五)消息处理机制Handler+Looper+MessageQueue的原理分析(上)
- Android开发知识(六)消息处理机制Handler+Looper+MessageQueue的原理分析(下)
- Android Handler Looper MessageQueue原理分析
- Swift - 字典排序方法
- 在jsp页面中加入下拉列表
- hdu3397 Sequence operation--区间操作 & 双标记(待解决)
- C#入门5.3——分支语句之三元运算符
- JavaIDE_MyEclipse 设置编码
- Android开发中Handler、Looper、MessageQueue的原理
- mysql 存储过程 REPEAT ... UNTIL ... END REPEAT
- oracle数据库学习1:表分区
- html实战-制作静态网页
- Objective-C Runtime(概述)
- 介词
- [总结]视音频编解码技术零基础学习方法
- 戏(细)说Executor框架线程池任务执行全过程(下)
- Java系列(一)Annotation(注解)