Android异步消息处理机制之looper机制
在android系统的应用程序中,与java程序相似,线程之间都是靠消息来驱动的,与此工作相关的是由handler,looper以及message,messagequene来完成。简单来说就是每一个线程中有且只有一个消息队列MessageQueue,利用looper来获取当前的线程和消息队列,我们可以不断向这个消息队列中添加消息Message,接着再利用handler从looper得知当前的消息队列,后再从中取出消息,处理消息。本文中代码示例参考了http://blog.csdn.net/smbroe/article/details/44239961中的代码并对其稍作改动。
什么是looper?looper里主要有两部分,一部分是线程,一部分是消息队列,一个looper就对应了一个当前的线程和当前线程下的消息队列。可以将looper比喻为工厂生产时用到的机械臂,不停地将从生产带上获取最新的产品(在消息列表中读取最新的消息),同时间最新的产品不停地打包(即将最新的消息不停地分发给handler),起着枢纽的作用。
我们通过一个简单的例子来学习如何使用Looper和Handler(handler的工作原理在下一篇博客中会讲,在这里只需大概理解为handler是发送消息队列中的消息并处理消息的就行了,本文只介绍下Looper),并通过这个例子来研究一下其工作原理;
首先,我们在LooperThread中为消息循环做准备,并创建一个Handler用于处理消息,注意Handler的创建要在调用Looper.prepare()之后,具体原因在等会会说明:
public class LooperThread extends Thread {public Handler mHandler;@Overridepublic void run() {// TODO Auto-generated method stubLooper.prepare();synchronized (this) {mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {// TODO Auto-generated method stubsuper.handleMessage(msg);Log.w("LooperThread", "handleMessage::Thread id---" + getId());}};}Looper.loop();notifyAll();}}
然后我们在主线程中创建一个新的线程,并通过新线程中的Handler向LooperThread线程发送消息;
final LooperThread mLooperThread = new LooperThread();mLooperThread.start();new Thread() {@Overridepublic void run() {// TODO Auto-generated method stubwhile (mLooperThread.mHandler == null) {try {wait();//防止在发送消息时Handler还没建立} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}mLooperThread.mHandler.sendEmptyMessage(0);Log.w(TAG, "Send Message::Thread id ---" + getId());}}.start();
程序运行的结果如下:
可以看到线程之间通信成功了,下面我们再来审视整个过程,通过查看关键函数的源代码来去理解整个运作机制。
首先在LooperThreda中调用Looper.prepare()方法,我们查看其源代码:
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));
}private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();}
其中sThreadLocal就是looper里内置的数据管理类,里面存储着looper,定义如下
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
学过数据结构的同学应该很容易能根据其get(),set()方法想起链表,这里我就不对其多做分析了,总之就是用来存looper的数据结构罢了。接着是looper的构造器,从代码中我们可以看到主要就是新建一个消息队列并存于内部数据mQuene和获得当前的线程存于mThread,在这里消息队列和线程是相互绑定的,因此也验证了我们开头的结论,一个looper就对应了一个当前的线程和当前线程下的消息队列。回到示例代码中,接着就是新建了一个handler(代码中的扩展:synchronized是java里同步机制的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码,可以理解为两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码)
,并重写了handler的handleMessage方法用来对消息进行处理,这里我们先手动忽略这个方法一会再来分析。
接着调用Looper的loop方法。该方法实现了消息循环,源码如下:
public static void loop() {//myLooper()方法就是通过sThreadLocal.get()返回我们刚刚设置的Looperfinal Looper me = myLooper();if (me == null) {throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}final MessageQueue queue = me.mQueue;// 确保线程和本地进程是一致的,并且记录这个identity tokenBinder.clearCallingIdentity();final long ident = Binder.clearCallingIdentity();for (;;) {Message msg = queue.next(); // 可能会阻塞if (msg == null) {// 没有消息则表明这个消息队列退出了.return;}// This must be in a local variable, in case a UI event sets the logger
//Printer表示打印事件Printer logging = me.mLogging;if (logging != null) {logging.println(">>>>> Dispatching to " + msg.target + " " +msg.callback + ": " + msg.what);}//分发该消息msg.target.dispatchMessage(msg);if (logging != null) {logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);}// 确保在分发的过程中该线程没有崩溃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.recycle();}}
这段代码中涉及到的知识比较多,我们只对与本文有关的关键方法进行讲解,其中通过mylooper()方法来获得刚在prepaer()方法中新建的looper,然后获得当前的消息队列,接着用一个for( ; ; )空循环来不断从消息队列中读取消息存于msg(msg=quene.next()),最后调用msg.target.dispatchMessage(msg)来讲消息分发出去,分发到哪里?分发到handler里交给它处理,有兴趣的同学可以先看看其源代码(具体分析下一篇才会讲):
public void dispatchMessage(Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}}
可以看到最后又是通过调用我们重写的handleMessage来进行处理的。
接着是调用 notifyAll()来告诉等着调用被之前synchronized锁起来的代码的线程们,告诉它们我用完了,你们可以去抢着用了。
最后来看主线程中的示例代码中的 mLooperThread.mHandler.sendEmptyMessage(0);当我们调用了sendMessage方法之后就向Looper发送了一条消息,让我们看看这个方法,
消息是如何被传递的。sendMessage方法最终会调用到sendMessageAtTime方法来,来看看源代码:
ublic 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 = this;if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);}
可以看到,sendMessage方法最终调用了queue.enqueueMessage方法将消息加入到了Looper中的消息队列。那么整个线索就联系起来了,给handler
发消息,改消息到了looper的消息队列里,然后looper把这个消息取出来在交给looper来处理。这就是示例代码的整个运作过程,因此来总结一下吧。
1.首先是调用Looper.prepare()方法来创建一个新的队列和获取到当前的线程,
2.然后我们创建handler并重写其handleMessage方法,在我们构建handler的时候会获取到当前的looper对象(通过该looper对象来获取消息队列),消息队列和Callback对象(当然,在上述代码中Callback对象为null),紧接着我们在主线程里开的新线程中调用sendMessage方法来向handler中的消息队列来传递消息(当然,查看源代码可以知道其最终调用的是sendMessageAtTime方法,并通过返回enqueueMessage方法来讲消息加入到消息队列中),我们重写的handleMessage方法就是用来拦截我们感兴趣的消息并对消息进行处理的方法
3.最后是调用Looper.loop()来实现消息的循环分发,其通过queue.next()来不断获取消息队列中的最新消息并且调用 msg.target.dispatchMessage(msg)方法来实现消息的分发
4.最后是dispatchMessage方法实现消息的分发,在该方法中进行了一系列判断:
如果Message自带了Callback,则交给Message的Callback处理;
如果Handler了设置了Callback,则交给Handler的Callback处理;
如果两者都没有,则调用handleMessage方法处理(默认是不处理的)。
也可以参考下以下图片:
Android异步消息处理机制之looper机制相关推荐
- Android异步消息处理机制 深入理解Looper、Handler、Message三者关系
转载子:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自[张鸿洋的博客] 很多人面试肯定都被问到过,请问Android中 ...
- Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
很多人面试肯定都被问到过,请问Android中的Looper , Handler , Message有什么关系?本篇博客目的首先为大家从源码角度介绍3者关系,然后给出一个容易记忆的结论. 1. 概述 ...
- Android异步消息处理机制 全解析
Android异步消息处理机制主要是指Handler的运行机制以及Hanlder所附带的MessageQueue和Looper的工作过程. 本文将通过分析源码(api-28)的形式,全面解析Handl ...
- Android异步消息处理机制
Android异步消息常用汇总 android常用异步框架分为handler.AsyncTask.handlerThread.IntentService. 什么是handler android消息机制 ...
- Android异步消息处理机制完全解析,带你从源码的角度彻底理解
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9991569 之前也是由于周末通宵看TI3比赛,一直没找到时间写博客,导致已经有好久 ...
- [学习总结]6、Android异步消息处理机制完全解析,带你从源码的角度彻底理解
开始进入正题,我们都知道,Android UI是线程不安全的,如果在子线程中尝试进行UI操作,程序就有可能会崩溃.相信大家在日常的工作当中都会经常遇到这个问题,解决的方案应该也是早已烂熟于心,即创建一 ...
- Android异步处理:Handler+Looper+MessageQueue深入详解
为什么80%的码农都做不了架构师?>>> 转载自:http://blog.csdn.net/mylzc/article/details/6771331,在原文基础上修改整理再发布. ...
- Android 异步消息处理机制(Handler 、 Looper 、MessageQueue)源码解析
1.Handler的由来 当程序第一次启动的时候,Android会同时启动一条主线程( Main Thread)来负责处理与UI相关的事件,我们叫做UI线程. Android的UI操作并不是线程安全的 ...
- Android Handler 异步消息处理机制的妙用 创建强大的图片载入类
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38476887 ,本文出自[张鸿洋的博客] 近期创建了一个群.方便大家交流,群号: ...
最新文章
- PAT(甲级)2020年秋季考试 7-4 Professional Ability Test
- 王洪超:WPF催熟整个软件生态链
- php mysql 检索跳转_jQuery+AJAX+PHP+MySQL数据库开发搜索功能,无跳转无刷新搜索。...
- Linux7如何手动建库,Centos 7系列删除数据库并重新安装
- 移动端通过ajax上传图片(文件)并在前台展示——通过H5的FormData对象
- 解决IntelliJ IDEA报错Error: java: 错误: 不支持发行版本 XX
- https 与 http
- 我国自主播放软件暴风影音挑落微软
- Java 算法 质因数2
- linux ls 目录结构,linux 系统目录结构 ls命令 文件类型 alias命令
- c++求数组中出现频率最高的数
- MySQL中的mysqldump命令使用详解
- 无源蜂鸣器c语言编程,电磁式蜂鸣器驱动原理与简单蜂鸣器编程及电路设计案例...
- java 邮件接收,用Java接收电子邮件
- 示波器的实时采样和等效采样
- 大数据可视化-Tableau
- Python获取高德POI(关键词搜索法)
- 交换机端口mtu值最大_mtu出现网络故障案例
- 高数_第3章重积分__二重积分_怎样交换积分次序
- matlab体会,Matlab心得体会