activity 和 生命周期: 消息通信
实际上关于activity大概流程已经了解了,在深入的话方向应该是ams的处理操作和界面创建和view绘制。这些话题之后再谈,activity是一个gui程序,其中离不开的就是消息通讯,也就是在消息循环中不断的处理消息,比如用户交互消息,系统提醒消息等。
所以一定要把消息通信作为一个核心的组件,其中涉及到的类有Handler,Looper,Message,MessageQueue,HandlerThread。
首先介绍的就是1.Message了,表示一个消息,关键的几个属性为what:消息的类型。arg1,arg2这个可以传递两个简单的整数。data一个bundle可以传递多个数据。obj可以传递一个对象。when用来表示时间的和消息队列有关,callback是一个runnable也就是说消息除了可以用来传递数据之外还可以处理runnable。这个都是在Handler中做的处理。target就是处理本消息的handler。实际上Message的重点并不是在于这个几个对象的封装,更加关键的是消息队列的链表维护。一直以来都说消息队列是一个链表结构的,但是你看一下MessageQueue并没有使用list或者给是collection。那这个链表到底在哪里?在Jni层?当然jni层实现了一个底层的looper和queue。但是在java层数据结构和操作都是健全的。实际上这个链表结构就是MessageQueue的一个Message对象mMesssages。这真的只是一个消息对象吗?是一个链表。其中的关键就在于Message的一个字段next。
tip:假如熟悉用数据结构,都知道链表的下一个节点的指针维护在本节点中,也就是Message的next实际是维护的下一个Message对象。十分简单的一个实现,
Message有这样一个方法Obtain是获取一个缓冲池中的Message。这个缓冲池是怎么维护的?就是4个static final的对象,sPoolSync是一个锁对象,由于缓冲池可能多线程操作,所以要同步。spool就是一个缓冲池,也是一个Message对象。spoolSize是缓冲池的大小。还有一个代表缓冲池的最大值MAX_POOL_SIZE = 10。也就是当有10个缓冲对象,这些对象实际上就是之前用过的Message对象,为了避免大量的创建,增加复用,减少垃圾回收。缓冲池的原理就是利用一个Message的链表来进行对象缓冲。
tip:所以Message的关键其实在于维护链表,为了更加明白。先说一下缓冲池利用和释放的机制:
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}首先spool不为空,就说明有缓冲对象。然后m就是我们要使用的消息,先把spool引用给m。然后把m的next变为null。也就是把刚才的spool从链表中断开。spool = m.next在断开之前把缓冲池的操作根节点向后移了一位。其实都是这个原理,就是链表操作。这个是obtain的获取Message消息。再看一下如何缓冲已废弃的Message
clearForRecycle();
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}这个是Message的recycle函数,clearForRecycle就是把所有数据都置为空,然后当缓冲池没有10的时候,把要缓存的消息,实际就是笨消息实例,next节点变为spool,同时把spool根节点向前移一位,spool = this,容量++。
Message中并没有什么特殊的函数调用,注意sendToTarget这个方法内部是调用target里面的Handler的sendMessage发送给本身处理。假如是new的Message或者是复用的obtain里面的,这个方法不会起作用。
2.Handler:这个是Message的处理器兼职发送器,管理的就是Message的发送和处理,在使用的时候一般是复写Handler的handleMessage方法然后把Handler的对象传递出去发送消息。Handler中会有Looper,MessageQueue,Callback这几个主要的对象,这几个对象会在创建Handler的时候初始化。
消息发送,首先要获取消息,其中基本上都是调用的Message的Obtain的各种重载函数。所以有获取缓冲池中的Message,也可以自己创建。然后就要发送了基本所有的发送都是调用了sendMessqeAtTime,其中调用了MessageQueue的enqueueMessage把消息插入到消息队列中。其中不管Attime还是delayed,delayed就是当前时间+delayed的时间。所以都是调用attime的方法。还有一个sendEmptyMessage,实际上就是从Message缓冲池中去一个对象发送出去。
消息接收和处理:消息的接收实际是looper从MessageQueue中获取了消息之后,交给了Handler的dispatchMessage,在这个函数里首先查看Handler有没有Callback回调,假如有就执行,然后检测Message中有无Callback回调,有就执行。然后会交给HandleMessage,也就是我们复写的方法中执行。这也就是为什么Handler可以执行Message和runnable的原因。注意这些操作是在Looper线程中,假如是UI线程,必须注意执行的时间了。
3Looper:开始看looper的时候,会有人这么说线程一共分两种,一种就是带有Looper的,比如UI线程。一种没有looper假如你要在没有looper的线程中使用Handler就要自己构造looper或者是使用HandlerThread。这个实际上看了流程篇的介绍后就明白了。activity的ui线程实际上在创建的时候就创建了Looper,并且一直进行的消息循环。所有的UI线程的动作都是发消息到Looper中执行的。彻底明白了消息循环,就彻底的领悟了actvity运行的机制,包括生命周期都是在消息循环的规范下的,不能让我们自由的定义程序流程。
每一个线程都只能有一个looper,为什么?从原理角度讲,looper作用是从队列中取消息,队列中一旦出现消息就要取出来,这就使得Looper必须做死循环,这也就是消息循环。那么死循环就会阻塞线程执行。一旦有两个looper,一个运行起来,另一个就没办法执行。从代码角度来看,looper是私有的构造,创建只能靠prepare,而创建出来的会保存到ThreadLocal中,也就是说线程唯一性。
looper循环,从prepare中初始化了之后,就可以执行loop方法了,一旦执行了loop之后就会不断的调用Queue的next方法尝试取消息,一旦取到就会dispatchMessage给各个Handler。处理消息。
tip:looper每个线程只有一个,但是可以有多个Handler,我们的activity都是在同一个线程中运行的,但是可以创建多了Handler,那么不会乱吗?不会这就是为什么每一个Message都有一个target,Message只能由创建他的线程处理。也就是目标targetHandler处理。
这有一个有趣的东西,假如没有target怎么办?这时候就要looper退出了,而且Looper的quit中也确实就是这么定义的,注意的是UI线程的looper是不能退出的,在检测target是否为空的时候,还检测了一个Boolean额变量mQuitAllowed.UI线程中的这个变量为false,所以不可以退出
4.MessageQueue:这个就是消息队列了,这个是和jni交互的类,在jni层也有一队列和loop,我觉得是为了优化消息循环。我们要注意的就是next和enqueueMessager方法了。next是为了获取消息队列的下一个Message,enqueueMessage是为了向队列中添加一个Message。具体的过程就不分析了,实际上就是他维护的Message队列mMessages的增加和删除操作。
5HandlerThread,实际上更加简单就是为我们添加了Looper的线程。有一个回调函数onLooperPrepared这个就是还没有执行loop之前的调用,你可以创建一个Handler。也就是说其实自己也可以做一个有looper的线程,你可以调用Looper.preapre然后在做一切的准备操作,调用Loop方法就进入了阻塞的循环,等待消息到来了。
总体来说Handler机制是很简单的,了解清楚概念之后就很容易了。
activity 和 生命周期: 消息通信的更多相关文章
- Activity 和 生命周期: 创建
了解了整体的android创建流程之后,就分析一下到底这个过程中做了什么? activity创建中开始时由activityStack中的realstartActivityLocked函数中调用了act ...
- Android Activity的生命周期详解
应用程序中,一个Activity通常就是一个单独的屏幕,它上面可以显示一些控件也可以监听并处理用户的事件做出响应. Activity之间通过Intent进行通信.在Intent 的描述结构中,有两个最 ...
- Activity的生命周期和启动模式
Activity的生命周期分析 典型情况下的生命周期.是指在用户参与的情况下,Activity所经过的生命周期的改变. 异常情况下的生命周期.是指Activity被系统回收或者由于当前设备的Confi ...
- Android开发之漫漫长途 Ⅰ——Android系统的创世之初以及Activity的生命周期
该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>中的相关知识,再次表示该书 ...
- Android开发之Activity的生命周期以及加载模式
本篇博客就来好好的搞一下Activity的生命周期,如果搞过iOS的小伙伴的话,Activity的生命周期和iOS中ViewController的生命周期非常类似.生命周期,并不难理解.一个人的生命周 ...
- Activity的生命周期
Activity的生命周期 以往我们实现页面间的跳转都是实例化Intent类的对象,但是页面在我们眼前的出现与消失没有我们所看到的那么简单,它有一个复杂的生命周期,一个页面的出现,被覆盖,再次出现,被 ...
- 浅谈Android中Activity的生命周期
引言 我想对于Android开发人员来说,Activity是再熟悉不过了,今天我们就来探讨下Activity的生命周期.熟悉的掌握Activity对于开发健壮的Android应用程序来说至关重要.下面 ...
- 每天一点Android干货-Activity的生命周期
Activity Activity是这样一个程序组件,它为用户提供一个用于任务交互的画面. 一个应用程序通常由多个activity组成,它们彼此保持弱的绑定状态.典型的,当一个activity在一个应 ...
- Android中Activity的生命周期
简介: 这个基本是必问的问题了,说一下你对Activity生命周期的理解,呵呵… onCreate, onStart, onResume, onPause, onStop, onDestroy, on ...
随机推荐
- RCP: P2 Update两个烦人bug和解决办法
问题 Eclipse新的P2 Update机制,使用起来很方便,如果使用P2 plugin自带的UI,开发者完全不用写任何代码 即可实现application的在线更新. 但是P2 Update至少有 ...
- [问题2014A09] 复旦高等代数 I(14级)每周一题(第十一教学周)
[问题2014A09] 设 \(A,B\) 分别是 \(3\times 2\), \(2\times 3\) 矩阵且满足\[AB=\begin{bmatrix} 8 & 2 & -2 ...
- 第九天 内容提供者 ContentResolver
重点:理解ContentProvider 的作用和创建流程 1. 内容提供者,提供 其他数据库的访问. 特点 - 描述 : 它是android 四大组件之一,需要androidManife ...
- Python virtualenv安装库报错SSL: CERTIFICATE_VERIFY_FAILED
Python virtualenv安装库报错SSL: CERTIFICATE_VERIFY_FAILED 问题描述 使用pip按照virtualenv报错,如下: pip install virtua ...
- InfoSet
声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...
- filter 过滤缓存
package fifter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.Filter ...
- git@Osc初识
加油! 参考博客:http://www.cnblogs.com/lpshou/archive/2013/07/18/3199243.html 今天尝试了下git@osc的项目导入,基本算是入门了git ...
- Redis的安装与部署
为了解决公司产品数据增长过快,初始化太耗费时间的问题,决定使用redis作为缓存服务器. Windows下的安装与部署: 可以直接参考这个文章,我也是实验了一遍:http://www.runoob.c ...
- hdu 5542 The Battle of Chibi(2015CCPC - C题)
题目链接:hdu 5542 首届CCPC的C题,比赛时一起搞了好久,最后是队友A出的,当时有试过用树状数组来优化 dp,然后今天下午也用树状数组搞了一下午,结果还是踩了和当时一样的坑:我总是把用来记录 ...
- python核心编程第六章练习6-11
6-11.转换.(a)创建一个从整型到IP地址的转换,如下格式:www.xxx.yyy.zzz.(b)更新你的程序,使之可以逆转换.[答案](a)代码如下: Input_number = abs(in ...