Android的消息机制主要是指Handler的运行机制以及Handler所附带的MessageQueue和Looper的工作过程。

  1.为什么要使用Handler?

Android规定访问UI只能在主线程中进行,如果在子线程中访问UI,则会抛出异常。同时Android建议不能在主线程中进行耗时的操作,不然会导致程序无法响应即ANR。因此耗时的工作只能交给子线程去做,而子线程却不能直接访问UI,为了解决这个矛盾,Android提供了Handler。Handler的主要作用是将某个任务切换到某个指定的线程中去执行。例如,我们在主线程中创建Handler,在子线程执行完耗时任务后,就可以通过Handler将更新UI的操作切换到主线程中去执行。

2.为什么系统不允许在子线程中访问UI?

因为Android的UI控件不是线程安全的,如果在多线程中并发访问可能会导致UI控件处于不可预期的状态。

3.Handler与MessageQueue,Looper

Android的消息机制主要是指Handler的运行机制,Handler的运行需要消息队列(MessageQueue)和消息循环(Looper)的支撑。

消息队列的内部存储结构并不是真正的队列,而是单链表。消息队列只是一个消息的存储单元,不能处理消息,而Looper就是来处理消息的。Looper会无限循环的去查看是否有消息,如果有,就处理,否则一直等待。

Handler创建时会采用当前线程的Looper来构造消息循环系统,如果当前线程没有Looper,则会报错(有的时候,也可以通过构造函数指定一个Looper)。这个当前线程的Looper是通过ThreadLocal来获取的。ThreadLocal并不是线程,它是线程内部的一个数据存储类,通过它可以在指定线性中存储数据和获取数据。

4.MessageQueue工作原理

         MessageQueue主要包含两个操作:插入和读取,分别对应enqueueMessage和next两个方法。enQueueMessage是往消息队列中插入一条消息,而next是从消息队列中取出一条消息并将其从消息队列中删除。前面说过,消息队列的内部实现是iyge单链表,它在插入和删除上比较有优势。

这里注意,next方法是一个无限循环的方法,如果消息队列中没有消息,那么next方法会一直阻塞,当有新消息到来时,它会返回这条消息并将其从单链表中删除。

5.Looper的工作原理

  (1)Looper会一直不停的从MessageQueue中查看是否有消息,如果有,则会立刻处理,否则就一直阻塞。

为当前信线程创建Looper的方法如下:

new Thead("Thread1"){

  @override
public void run()
{
Looper.prepare();
Handler handler=new Handler();
Looper.loop();
}
}

注意,只有调用了Looper的loop方法后,消息循环系统才真正起作用。

loop方法会调用MessageQueue的next方法来获取消息,当没有消息时,next方法会阻塞,导致loop方法也会阻塞。Looper获得消息后,调用Handler对象的dispatchMessage方法,这个dispatchMessage方法是在创建Handler时所使用的Looper中执行的,这样就就将代码逻辑切换到指定的线程中去了。

loop方法是一个死循环,唯一跳出循环的方式是MessageQueue的next方法返回null。当Looper的quit方法被调用时,它会调用MessageQueue的quit或者quitSafely方法来通知消息队列退出,当消息队列被标记为退出状态时,其next方法就会返回null。

(2)Looper也可以退出,主要的方法是quit和quitSafely。前者会直接退出Looper,而后者只是设定一个标记,然后把消息队列中的已有消息处理完毕后才安全退出。Looper退出以后,Handler就无法再顺利发送消息了,其send方法会返回false。

(3)在子线程中创建Looper后,当所有事情完成以后应该调用quit方法来终止消息循环,否则这个子线程会一直处于等待状态。

  6.Handler的工作原理

Handler的主要工作是发送和接收消息。消息的发送通过post的方法和send的方法实现,post的方法最终是通过send的方法实现的。

Handler发送消息仅仅是向消息队列中插入一条消息,然后MessageQueue的next方法将消息给Looper处理,最终Looper将消息交给Handler处理,即Handler的dispatchMessage方法被调用。

public void dispatchMessage(Message msg){
if(msg.callback!=null)
{
handleCallback(msg);
}
else
{
if(mCallback!=null)
{ if(mCallback.handleMessage(msg))
{
return;
               }
}
handleMessage(msg);
}
}

这里msg.callback对象其实就是一个Runnable对象,就是Handler的post方法所传递的Runnable参数。

mCallback对应的类实现的是Callback接口。当我们不想派生Handler的子类并重写其handleMessage方法时,可以通过Callback来实现。

Android学习笔记之消息机制的更多相关文章

  1. Android学习笔记(广播机制)

    1.Android的广播机制介绍 收听收音机也是一种广播,在收音机中有很多个广播电台,每个广播电台播放的内容都不相同.接受广播时广播(发送方)并不在意我们(接收方)接收到广播时如何处理.好比我们收听交 ...

  2. android学习笔记21——消息提示Toast

    消息提示可细分为两种:大量消息提示——当程序有大量图片.信息需要展示时,采用对话框消息提示: 小量消息提示——当程序只有少量信息需要呈现给用户时,采用轻量级的对话框——Toast; Toast ==& ...

  3. 【转】 Pro Android学习笔记(八八):了解Handler(2):什么是Handler

    文章转载只能用于非商业性质,且不能带有虚拟货币.积分.注册等附加条件.转载须注明出处:http://blog.csdn.net/flowingflying/ 之前我们有一篇很好的博文<Andro ...

  4. 【转】Pro Android学习笔记(二):开发环境:基础概念、连接真实设备、生命周期

    在Android学习笔记(二):安装环境中已经有相应的内容.看看何为新.这是在source网站上的Android架构图,和标准图没有区别,只是这张图颜色好看多了,录之.本笔记主要讲述Android开发 ...

  5. Android消息传递之Handler消息机制

    前言: 无论是现在所做的项目还是以前的项目中,都会遇见线程之间通信.组件之间通信,目前统一采用EventBus来做处理,在总结学习EventBus之前,觉得还是需要学习总结一下最初的实现方式,也算是不 ...

  6. Android学习笔记之JSON数据解析

    转载:Android学习笔记44:JSON数据解析 JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,采用完全独立于语言的文本格式,为Web应用开发提供了一种 ...

  7. 【转】Pro Android学习笔记(九八):BroadcastReceiver(2):接收器触发通知

    文章转载只能用于非商业性质,且不能带有虚拟货币.积分.注册等附加条件.转载须注明出处:http://blog.sina.com.cn/flowingflying或作者@恺风Wei-傻瓜与非傻瓜 广播接 ...

  8. 【转】 Pro Android学习笔记(六九):HTTP服务(3):HTTP POST MultiPart

    目录(?)[-] 建立测试环境 开发环境导入第三方JAR HTTP Post Multipart小例子 HTTP POST不仅可以通过键值对传递参数,还可以携带更为复杂的参数,例如文件.HTTP Po ...

  9. 【转】 Pro Android学习笔记(六七):HTTP服务(1):HTTP GET

    目录(?)[-] HTTP GET小例子 简单小例子 出现异常NetworkOnMainThreadException 通过StrictMode进行处理 URL带键值对 Andriod应用可利用ser ...

随机推荐

  1. Android的post()方法究竟运行在哪个线程中

    Android中我们常用的post()方法大致有两种情况: 1.如果post方法是handler的,则Runnable执行在handler依附线程中,可能是主线程,也可能是其他线程 2.如果post方 ...

  2. 网页链接qq

    <a href="mqqwpa://im/chat?chat_type=wpa&uin=12345678&version=1&src_type=web& ...

  3. .net学习笔记--文件读写的几种方式

    在.net中有很多有用的类库来读写硬盘上的文件 一般比较常用的有: File:1.什么时候使用:当读写件大小不大,同时可以一次性进行读写操作的时候使用         2.不同的方式可以读写文件类型不 ...

  4. 自定义指令directive

    1.自定义指令 在angular中,module下面的directive方法用于创建自定义指令,用法: m1.directive('myTab',function(){ return { restri ...

  5. jQuery 菜单栏 展开与收缩例子

    废话少说,上代码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://ww ...

  6. linux环境搭建

    gcc编译安装 解压下载的gcc包:tar -xxx gcc-xxxx.xxx.xx 下载安装gcc依赖库:./contrib/download_prerequisites configure一个Ma ...

  7. Android studio下载依赖包很慢

    build gradle文件 buildscript { repositories { //jcenter() maven { url 'http://maven.oschina.net/conten ...

  8. python安装使用talib

    安装主要在http://www.lfd.uci.edu/~gohlke/pythonlibs/这个网站找到 按照需要的python版本和平台位数下载,然后直接用pip install 进行安装 包含的 ...

  9. Docker-2:network containers

    docker run -d -P --name web training/webapp python app.py # -name means give the to-be-run container ...

  10. 使用 KGDB 调试 Kernel On Red Hat Linux

    1. KGDB 简介         KGDB  提供了一种使用 GDB 调试 Linux 内核的机制.使用 KGDB 可以象调试普通的应用程序那样,在内核中进行设置断点.检查变量值.单步跟踪程序运行 ...