Android的消息机制
一、简介
①、我们不能在子线程中去访问UI空控件,这是时候只能通过Handler将更新UI的操作放到主线程中去执行
②、Handler的组成:messageQueue和Looper的支持
③、MessageQueue:作用:存储了一组消息,以队列的形式对外提供插入和删除的工作。实际上是运用单链表的数据结构来存储消息列表的。
④、Looper 作用:由于MesageQueue无法处理消息,Looper填补了这一个功能,Looper会无限循环的形式去查找是否有新消息,如果有就处理消息,没有的话就一直等待
⑤、Looper的特殊概念ThreadLocal:并不是线程,但是可以在线程中存储数据。
举例:当Handler创建的时候默认会采用当前线程的Looper来构造其循环系统。那么如何获取当前线程的Looper,或者指定线程的Looper呢?
这就要使用到ThreadLocal,ThreadLocal能够在不同线程中互不干扰地存储和提取数据。所以通过ThreadLocal能够轻松获取每个线程的Looper。
注意:线程是默认没有Looper的,需要使用Handler就必须先为线程创建Handler
class LooperThread extends Thread
{
public Handler mHandler;
public void run()
{
Looper.prepare();
mHandler = new Handler()
{
public void handleMessage(Message msg)
{
// process incoming messages here
}
};
Looper.loop();
}
}
线程中创建Looper
为什么主线程就不需要呢?
因为在ActivityThread被创建是就已经初始化了Looper
二、消息机制概述
Android的消息机制:主要指Handler的运行机制和Handler所附带了MessageQueue和Looper的工作过程。
Handler的主要作用:讲一个任务切换到某个指定的线程中去执行,
问:Android为什么要有这个功能呢?
Android规定访问UI只能在主线程中执行:
在ViewRootImpl中对UI验证做了判断(P373 ①)。
如果没有Handler,我们在子线程中进行一些数据处理,之后要根据数据修改UI,如果没有Handler我们就无法将访问UI的工作切换到主线程中去。
问:为什么规定UI只能在主线程中执行?
因为如果多线程中并发访问可能会导致UI控件处于不可预期状态。
Handler的运行机制
1.当Handler创建完毕,说明其内部的Looper和MessageQueue可以和Handler协同工作了
2.通过Handler.post()方法将一个Runnable投递到Handler内部的Looper中去处理,或者使用send()方法。因为post()方法最终也是通过send方法来完成的。
3.send()方法的工作流程:当Handler的send()方法被调用时,它会调用MessageQueue的enqueueMessage()方法,将消息放入消息队列中,然后Looper发现有新消息来到的时候就会处理这条消息,最终消息中的Runnable()或者Handler的handler的handleMessage()方法就会被调用。
调用图:

三、Android的消息机制分析
ThreadLocal的工作原理
使用情景:当某些数据是以线程为作用域并且不同线程具有不同数据副本的时候。
举例:
private static final String TAG = "MainActivity";
private ThreadLocal<Boolean> mBooleanThreadLocal = new ThreadLocal<Boolean>();
//在mainThread中放入true类型的数据
mBooleanThreadLocal.set(true);
Log.d(TAG,"MainActivity"+mBooleanThreadLocal.get()); new Thread("Thread#1"){
public void run(){
mBooleanThreadLocal.set(false);
Log.d(TAG,"Thread#1"+mBooleanThreadLocal(.get());
}
} new Thread("Thread#2"){
publci void run(){
Log.d(TAG,"Thread#1"+mBooleanThreadLocal(.get());
}
}
MainActivity
返回结果:
MainActivity:true Thread#1:false Thread#2:null
说明:虽然不同线程访问的是同一个ThreadLocal但是根据线程不同,返回的数据是根据在对应线程中设置的数据。(就好像有副本一样)
工作原理:
1.查看ThreadLocal的set()方法(P377 ①)
①、首先获取当前线程
②、根据当前线程调用value()方法返回当前线程的value对象(其中存储了了该Thread的数据)。
Value类:Thread内部专门存储ThreadLocal的数据 (表示当前线程中存储了ThreadLocal的数据,而不是在ThreadLocal中存储了线程的数据)
③、将数据放入value.table这个数据结构中。
table数据结构:数组private Object[] table,ThreadLocal的值就存储在其中。
2.查看ThreadLocal的get()方法(P379 ①)
同样是取出当前线程的value.table()然后,根据索引获取数据
消息队列的工作原理(MessageQueue)
①、MessageQueue的操作
插入(enqueueMessage())和读取(next())(读取操作会伴随着删除操作)
②、enqueueMessage()的操作:简单的单链表的插入操作
next():寻找未被锁的Message,并返回给调用者,发现next()是一个无线循环的方法,如果存在Message则return跳出循环,不存在Message则进入阻塞
Looper的工作原理
作用:不同的调用MessageQueue的next()方法查看是否有新的信消息,如果有新的消息就会立即处理,如果没有就一直阻塞在那。
构造方法(P383 ①)
1.保存当前Thread对象。2.创建一个MessageQueue
线程中创建Looper:(P383 ②)
小提示:Looper提供了getMainLooper()方法,通过它可以在任何地方获取到主线程的Looper。
Looper的退出:
quit():直接退出Looper
quitSafely:是在消息队列中设定一个标记,当消息队列中的所有消息处理完毕之后才安全退出。
退出后,Handler的send()会返回false。
注:如果手动为子线程创建Looper,那么在所有的事情完成后,应该调用quit()方法终止消息循环,否则子线程会一直处于等待状态。如果退出Looper这个线程就会立刻终止。
分析Looper的loop()(P384 ①)
1、loop方法是一个死循环
2、再循环内通过调用MessageQueue.next();方法获取Message,如果next()中没有方法的时候,loop()方法会随next()方法一同阻塞。当next()方法返回null的时候,loop就会自动终止。
3、所以说调用Looper.quit(),就是调用MessageQueue.quit()或quitSafely()。
4、当获得msg之后,利用msg.target获取发送该msg的Handler,然后调用该handler的dispatchMessage(msg)方法。
Handler的工作原理
作用:发送消息和接收消息
主要方法:post一系列方法和send一系列方法,post一系列方法是通过send()一系列方法实现的;
分析send系列方法(P385 ①)
内部都是通过向MessageQueue添加一条msg
分析处理消息方法
1、首先检查msg对象的callback是否为null,callback就是Runnable对象(Handler.post()传递的Runnable对象)。不为null就交给handleCallback(msg)处理
2、其次检查mCallback是否为null,不为null,则调用mCallback的handleMessage()。
mCallback是一个接口,可以通过Callback创建Handler对象:Handler mHandler = new Handler(callback);
这样就可以在不用派生子类的情况下创建Handler。
3、最后调用Handler的handleMessage来处理消息
流程图:

Handler特殊的构造方法(P388 ①)
通过特定的Looper构造Handler
public Handler(Looper looper){
this(looper,null,false);
}
主线程的消息循环(P389 ①)
主线程的main()方法入口,通过Looper.parepareMainLooper()创建主线程的Looper和MessageQueue,通过Loop.loop()消息循环。之后通过ActivityThread.H这个Handler与消息队列交互。
AcitivtyThread通过ApplicationThread和AMS进行进程通信,AMS完成ActivityThread请求后会回调ApplicationThread的Binder方法,然后向Activity.H发送消息,将Binder线程池环境切换到主线程环境。
Android的消息机制的更多相关文章
- 《Android开发艺术探索》读书笔记 (10) 第10章 Android的消息机制
第10章 Android的消息机制 10.1 Android消息机制概述 (1)Android的消息机制主要是指Handler的运行机制,其底层需要MessageQueue和Looper的支撑.Mes ...
- 【原创】源码角度分析Android的消息机制系列(一)——Android消息机制概述
ι 版权声明:本文为博主原创文章,未经博主允许不得转载. 1.为什么需要Android的消息机制 因为Android系统不允许在子线程中去访问UI,即Android系统不允许在子线程中更新UI. 为什 ...
- 【原创】源码角度分析Android的消息机制系列(五)——Looper的工作原理
ι 版权声明:本文为博主原创文章,未经博主允许不得转载. Looper在Android的消息机制中就是用来进行消息循环的.它会不停地循环,去MessageQueue中查看是否有新消息,如果有消息就立刻 ...
- 【原创】源码角度分析Android的消息机制系列(二)——ThreadLocal的工作过程
ι 版权声明:本文为博主原创文章,未经博主允许不得转载. 在上一篇文章中,我们已经提到了ThreadLocal,它并非线程,而是在线程中存储数据用的.数据存储以后,只能在指定的线程中获取到数据,对于其 ...
- Android 基础 十一 Android的消息机制
Handler是Android消息机制的上层接口,这使得在开发应用过程中我们只需要和Handler交互即可.Handler的使用过程很简单,通过它可以轻松地将一个任务切换到Handler所在的线程中去 ...
- Android的消息机制简单总结
参考文章: http://gityuan.com/2015/12/26/handler-message-framework/#next 参考资料: Android Framework的源码: Mess ...
- 聊一聊Android的消息机制
聊一聊Android的消息机制 侯 亮 1概述 在Android平台上,主要用到两种通信机制,即Binder机制和消息机制,前者用于跨进程通信,后者用于进程内部通信. 从技术实现上来说,消息机制还是比 ...
- Android开发——Android的消息机制详解
)子线程默认是没有Looper的,Handler创建前,必须手动创建,否则会报错.通过Looper.prepare()即可为当前线程创建一个Looper,并通过Looper.loop()来开启消息循环 ...
- Android之消息机制Handler,Looper,Message解析
PS:由于感冒原因,本篇写的有点没有主干,大家凑合看吧.. 学习内容: 1.MessageQueue,Looper,MessageQueue的作用. 2.子线程向主线程中发送消息 3.主线程向子线程中 ...
随机推荐
- Oracle中MERGE语句的使用
Oracle在9i引入了merge命令, 通过这个merge你能够在一个SQL语句中对一个表同时执行inserts和updates操作. 当然是update还是insert是依据于你的指定的条件判断的 ...
- 三大主流ETL工具选型
ETL(extract, transform and load)产品乍看起来似乎并不起眼,单就此项技术本身而言,几乎也没什么特别深奥之处,但是在实际项目中,却常常在这个环节耗费太多的人力,而在后续的维 ...
- Scrapinghub执行spider抓取并显示图片
序 最近在学习Scrapy的时候发现一个很有意思的网站,可以托管Spider,也可以设置定时抓取的任务,相当方便.于是研究了一下,把其中比较有意思的功能分享一下: 抓取图片并显示在item里: 下面来 ...
- yii2安装与初始化-Yii2学习笔记(一)
一.安装项目: 使用composer下载安装yii2 advanced安装包: composer create-project yiisoft/yii2-app-advanced advanced(自 ...
- 【2】开发环境的搭建,Ubuntu14.04
这里使用的是Ubuntu14.04 Unity 更新源 首先,将更新源更换为国内更新源,我这里使用的是网易的更新源 sudo gedit /etc/apt/sources.list deb http: ...
- Mysql 6.7.7 + EntityFramework 5.0 Code First 不能 Update-Database 问题的解决
1.修改 Migrations/Configuration.cs 文件 namespace DataModel.Migrations { using System; using System.Data ...
- request.getParamer()
eturns the value of a request parameter as a String, or null if the parameter does not exist. Reques ...
- javascript之Arguments
一.Arguments.callee //获取当前正在执行的函数,也就是这个函数自身,常用于获取匿名函数自身 语法:arguments.callee var factorial = function ...
- 【Xamarin挖墙脚系列:打造独特的Xamarin.IOS开发环境】
苹果的产品,依赖特定的开发环境. Mac + Xcode 不可缺少.所以,必须有Mac系统. 1-虚拟机搭建 2-土豪有Mac电脑设备 但是我觉得还是顺带上Visual Studio才是完美.. ...
- 2015第10周四-CSS小结
这两天做前台页面发现个人在CSS前端方法很多基础知识都忘了,晚上又搜索学习了下,把相关内容摘录总结. CSS 规则由两个主要的部分构成:选择器,以及一条或多条声明. selector {declara ...