一、简介

①、我们不能在子线程中去访问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的消息机制的更多相关文章

  1. 《Android开发艺术探索》读书笔记 (10) 第10章 Android的消息机制

    第10章 Android的消息机制 10.1 Android消息机制概述 (1)Android的消息机制主要是指Handler的运行机制,其底层需要MessageQueue和Looper的支撑.Mes ...

  2. 【原创】源码角度分析Android的消息机制系列(一)——Android消息机制概述

    ι 版权声明:本文为博主原创文章,未经博主允许不得转载. 1.为什么需要Android的消息机制 因为Android系统不允许在子线程中去访问UI,即Android系统不允许在子线程中更新UI. 为什 ...

  3. 【原创】源码角度分析Android的消息机制系列(五)——Looper的工作原理

    ι 版权声明:本文为博主原创文章,未经博主允许不得转载. Looper在Android的消息机制中就是用来进行消息循环的.它会不停地循环,去MessageQueue中查看是否有新消息,如果有消息就立刻 ...

  4. 【原创】源码角度分析Android的消息机制系列(二)——ThreadLocal的工作过程

    ι 版权声明:本文为博主原创文章,未经博主允许不得转载. 在上一篇文章中,我们已经提到了ThreadLocal,它并非线程,而是在线程中存储数据用的.数据存储以后,只能在指定的线程中获取到数据,对于其 ...

  5. Android 基础 十一 Android的消息机制

    Handler是Android消息机制的上层接口,这使得在开发应用过程中我们只需要和Handler交互即可.Handler的使用过程很简单,通过它可以轻松地将一个任务切换到Handler所在的线程中去 ...

  6. Android的消息机制简单总结

    参考文章: http://gityuan.com/2015/12/26/handler-message-framework/#next 参考资料: Android Framework的源码: Mess ...

  7. 聊一聊Android的消息机制

    聊一聊Android的消息机制 侯 亮 1概述 在Android平台上,主要用到两种通信机制,即Binder机制和消息机制,前者用于跨进程通信,后者用于进程内部通信. 从技术实现上来说,消息机制还是比 ...

  8. Android开发——Android的消息机制详解

    )子线程默认是没有Looper的,Handler创建前,必须手动创建,否则会报错.通过Looper.prepare()即可为当前线程创建一个Looper,并通过Looper.loop()来开启消息循环 ...

  9. Android之消息机制Handler,Looper,Message解析

    PS:由于感冒原因,本篇写的有点没有主干,大家凑合看吧.. 学习内容: 1.MessageQueue,Looper,MessageQueue的作用. 2.子线程向主线程中发送消息 3.主线程向子线程中 ...

随机推荐

  1. Js 导出Excel IE ActiveX控件

    function ExportExcel() { var oXL = new ActiveXObject("Excel.Application"); //创建excel应用程序对象 ...

  2. spring3+hibernate3+(dbcp+oracle+拦截器事务配置)整合(一)

    1.applicationContext-base.xml文件 <?xml version="1.0" encoding="UTF-8"?>< ...

  3. 论docker中 CMD 与 ENTRYPOINT 的区别

    Dockerfile里有 CMD 与 ENTRYPOINT 两个功能咋看起来很相似的指令,开始的时候觉得两个互用没什么所谓,但其实并非如此: CMD指令: The main purpose of a ...

  4. Eclipse工程乱码解决

    eclipse之所以会出现乱码问题是因为eclipse编辑器选择的编码规则是可变的.一般默认都是UTF-8或者GBK,当从外部导入的一个工程时,如果该工程的编码方式与eclipse中设置的编码方式不同 ...

  5. [WPF]ListView点击列头排序功能实现

    [转]   [WPF]ListView点击列头排序功能实现 这是一个非常常见的功能,要求也很简单,在Column Header上显示一个小三角表示表示现在是在哪个Header上的正序还是倒序就可以了. ...

  6. USB系列之九:基于ASPI的U盘驱动程序

    USB系列之七和之八介绍了ASPI,并通过一些实例说明了基于ASPI的编程方法,本文使用前两篇文章介绍的知识以及以前介绍的有关DOS驱动程序下驱动程序的内容实际完成一个简单的基于ASPI的U盘驱动程序 ...

  7. arcgis api for silverlight

    原文 http://blog.sina.com.cn/s/blog_4638cf7b0100wntt.html arcgis api for silverlight(1) (2011-09-21 09 ...

  8. Unix/Linux环境C编程入门教程(31) 数学函数带你战胜企业面试

    1.函数介绍: abs()acos()asin()atan()atan2()ceil()cos()cosh()exp()frexp()ldexp()log()log10()pow()sin()sinh ...

  9. Merge Two Sorted Lists 解答

    Question Merge two sorted linked lists and return it as a new list. The new list should be made by s ...

  10. Canvas之动态波浪效果_陈在真Sunny_chen_新浪博客

    Canvas之动态波浪效果_陈在真Sunny_chen_新浪博客 Canvas之动态波浪效果 (2012-04-26 09:04:51) 转载▼