Android 开发艺术与探究 第一章 Activity 的生命周期和启动模式
来源:互联网 发布:网站数据库下载代码 编辑:程序博客网 时间:2024/06/04 20:02
第一章Activity的生命周期和启动模式
正常情况下,Activity会经历如下生命周期。
(1)onCreate, Activity正在被创建,在这个方法我么可以做一下初始化工作。setContentView去加载布局,初始化Activity所需数据等。
(2)OnRestart,Activity正在重新启动,一般情况下,当前Actvity由不可见,变成可见,onRestart就会被调用。这种情形一般是用户导致的。按Home键切换到桌面或者用户打开一个新的Activity,这是当前的Activity就会暂停,执行onPause和onStop被执行了。接着用户又回到这个Activity就会出现这种情况
(3)onStart,Activity已结显示还不能和用户交互时调用。
(4) onResume 表示已经可见了,并且出现在前台,与onstart相比,onStart的时候Activity还在后台,onResume的时候Activity才显示到前台
(5)onPause Activity还在停止,正常情况下,紧接着onStop就会被调用,在特殊情况下,这个时候快速的在回到当前Activity,那么onResume会被调用,那么OnResume会被调用,这种情况属于极端情况,此时可做一些存储数据停止动画等工作,但是注意不能太耗时,因为这会影响到新Activity的显示,onPause必须先执行,新Activity的onResume才会执行 (也就是说只有执行了旧Activity的onpause,新Activity才会执行onCreate 后面还会做详细介绍)
(6)onStop:表示Activity即将停止,可以稍微重量级的回收工作,同样不耗时,(特殊情况:在使用透明主题的情况下,onStop不会被执行)
(7)onDestroy:表示Activity即将被销毁,这是Activity生命周期中的最后一个回调,在这里我们可以做一些回收工作和最终的资源释放。
生命周期是配对的,onCreate和onDestroy是配对的,分别标识者Activity的创建和销毁,并且只可能一次调用,从Activity是否可见来说,onStart和onStop是配对的,随着用户的操作或者屏幕的点亮,这两个Activity会被多次调用,从是否在前台来说,onResume和onPause是配对的,随着用户操作或者设备屏幕的点亮和熄灭者两个可能被调用多次,
面试小提问:‘
1. onStart和onResume,onPause和onStop从描述上来看差不多,对我们有什么实质的不同呢?
从使用过程来说,onStart和onResume,onPause和onStop看起来的确差不多,甚至我们可只保留其中一对,比如只保留onStat和onStop既然如此为什么要提供看起来重复的接口,根据上面的分析,这两个配对的回调分别表示不同的意义,onStart和onStop是从Activity是否可见这个角度调用的,二OnResume和OnPause是从Activity是否位于前台这个角度调用的出了这种区别,在实际使用中没有其他明显的区别。
2.假设当前Activity为A,如果用户打开一个新的ActivityB,那么B的onResume和A的onPause哪个先执行呢?
第二个问题,从Activity源码的角度来分析,在新的Activity启动之前,栈顶的Activity需要先onpause后,新Activity才能启动。根据实际实际效果打出的log
A--onpause B ---onCreate B--onStart B--onResume A--Onstop
从另一个角度,Android官方文档对onPause的解释有这么一句,不能再onpause中做重量级的操作,因为必须onPause执行完成以后新Activity才能Resume。通过分析这个问题,我们知道onStop中操作,儿时的新Activity尽快显示出来切换并且切换到前台,不能再onPause和onStop做耗时操作,尤其是onPause,这意味着我们应当尽量在OnStop中做操作,而使得新Activity尽快显示出来切换到前台。
上面介绍了正常情况下Activity的生命周期,下面介绍Activity异常生命周期的分析
异常情况下的生命周期的分析
1.资源相关的系统配置发生改变导致Activity被杀死并重新创建。
我们首先要对系统资源加载机制有一定的了解,这里不详细分系统的资源加载机制,只是做一个简单说明,那最简单的图片来说,当我们把一张图片仿如drawable目录后,就可以通过Resources去获取这张图片,同事为了兼容不同的设备,我们可能还需要在奇台一些目录放置不同的图片,比如drawable-mdpi等,这样当应用程序启动时,系统就会根据当前设备的情况去加载合适的Resources资源,比如说横屏手机和竖屏手机会拿到两张不同的图片,比如说:当前Activity处于竖屏状态,如果突然旋转屏幕,由于系统配置发生改变(不止这一种情况),在默认情况下,Activity就会被从新创建,当然我们也可以组织系统重新创建我们Activity。
在默认情况下,当我们Activity不做特殊处理,当系统配置发生改变后,Activity就会被从新创建,其嵊州周期如下所示:
其中onPause,onStop ,onDestroy均会被调用,同时由于Activity是在一次情况下终止的,系统会调用onSaveInstanceState来保存当前Activity的状态。这个方法在调用时机是在OnStop之前,它和onPause没有既定的时序关系,它可能调用在onPause之前,亦可能在之后,需要强调的一点,当系统认为Activity要被异常终止时候调用这个方法,当Activity重新创建时,我们可以通过OnRestoreInstanceState和OnCreate方法来判断Activity是否被重建。从时序上来说,OnRestoreInstanceState的调用时机在OnStart之后,
同时,我们要知道,在OnSaveInstanceState和 OnRestoreInstanceState方法中,系统自动。 为我们做一定的回复工作,当Activity在异常情况下重新创建时,系统会默认保存当前Activity的视图结构,并且在Activity重启后为我们回复这些数据,比如文本框中的输入数据,listView的滚动位置等,这些view相关状态系统都能够默认为我们恢复,具体针对某一个特定的View系统能为我们恢复哪一些数据,我们可以查看View的源码,和Activity一样,每个View都有OnsaveInstanceState和OnRestoreinstanceState方法,看一下具体实现,就能够知道系统能够自动为我们恢复哪一些数据。
关于保存和恢复View层次结构,系统的工作流程,Activity会调用OnStaveInstanceState去保存数据,然后Activity会委托Window保存数据,接着Window在委托他什么的顶级容器去保存数据。顶层容器是一个ViewGroup。最后顶层容器会在一一去通知它的子元素保存数据,这样整个保存数据的过程也就完成了。上层委托下层的模式,类似View的绘制过程和View的时间处理机制。
当调用onSaveInstanceState中存储一个字符串,然后当Activity销毁并重新创建后,接收的位置可以OnRestoreInstanceState,也可以是OnCreate,两者的区别。onRestoreInstanceState一但被调用,其中参数一定有值,我们不用额外的去判断是否为空,但是OnCreate不行,onCreate如果正常启动,参数Bundle可能为空,这两个方法我们选择任意一个都可以进行数据恢复,单官方文档建议是采用onRestoreInstanceState去恢复数据。
(onSaveInstanceState方法还有一点需要说明:那就是系统只会在Activity即将销毁并且有机会重新显示的才会调用它。考虑一种情况,当Activity正常销毁的时候系统不会调用OnSaveInstanceState方法,因为被销毁的Activity不可能在被显示。) 屏幕旋转 Activity在即将销毁之后马上就会重新创建,所以会调用onSaveInstanceState方法。
资源不足的情况下导致低优先级的Activity被杀死。这种情况不容易模拟,当系统内存不足时候,系统就会按照优先级杀死Activity,
1.前台Activity 2.非可见Activity,导致Activity不可见,但是位于后台无法和用户直接交互 3.后台Activity
系统配置发生改变Activity会被重新创建,可以配置Android:configchanges="oreientation "等属性来组织Activity重启。
Activity启动模式
前言:一个任务栈可以有多个实例,每个实例也可以属于不同的任务栈。
standard:标准的启动模式,系统默认的模式。每次启动都会创建一个新的实例。在这种模式下,谁启动了这个Activity,那么这个Activity就运行在启动它的那个Activity所在的栈中。
注意:当我们用Application去启动Standard的时候回报错,因为standard模式的Activity默认进入启动它的Activity的任务栈中,但是由于非Activity类型的Context并没有所谓的任务栈,所以这就有问题了。解决 这个问题的方法是为待启动的Activity指定FLAG_ACTIVITY_NEW_TASK标记位,这样启动的时候会为它创建一个新的任务栈。
singletop:栈顶复用模式,如果新的Activity已经位于任务栈的栈顶,那么此Activity不会被重复创建,同时它的ONNewIntent方法会被调用。
singleTask:栈内服用模式,者是一种单实例模式,只用Activiy在一个栈中存在,那么多次启动此Activiy都不会重新创建实例,和singTop一样,系统也会调用其onNewIntent。
具体一点:当一个具有singleTask模式的Activity请求启动后,系统首先寻找是否存在想要的Activity的任务栈,如果不存在就重新创建一个任务栈,然后创建Activity的实例后把Activity放入栈中,如果 存在所需的任务栈,并且A的实例存在,系统就会把Activity调到栈顶,并调用器onNewIntent方法,并且将Activity上面的Activity全部出栈,如果实例不存在将创建Activity并将其押入栈中。
singleInstane;单列模式,是一个种加强singleTask,它具有所有SingleTask的所有特性外,那就是这种模式的Activity只能单独的位于一个任务栈中。
下面将singleTask做重点介绍:在singleTask中启动模式中,多次提到某个Activity所需的任务栈,什么是Activity所需的任务栈?这要从一个参数说起,
TaskAffinty:这个参数标示了一个Activity所需要任务栈的名字。默认情况下,所有Activity所需的任务栈的名字为应用的包名,当然我们可以为每个Activity单独指定TaskAffinity属性。这个属性必须不能和包名相同,否则就相当于没有指定。TaskAffinty属性主要和singleTask启动模式或者AlllowTaskReparenting属性配合使用。在其他情况下没有意义。
任务栈分为前台任务栈和后台任务栈,后台任务栈中的Activity处于暂停状态,用于可以通过切换将后台在任务栈调回前台
当TaskAffinity和allowTaskReparenting结合的时候这种情况比较复杂,会产生特殊的效果,allowTaskReparenting设置为true,表示可以运行在别的任务栈中。
Activity指定启动模式,有两个方法一种通过Android:luanchMOde="singleTask"启动,另一种通过Intent中设置标志位来启动。
两种方法区别优先级代码用Intent的启动高于配置文件中的。配置文件中无法为Activity设置FLAG_ACTIVITY_CLEAR_TOP标识。第二种方式无法为Activity指定singleInstance模式。
Activity标记位Flags
Activity标记位很多,
FLAG_ACTIVITY_NEW_TASK 这个标记位作用是为Activity指定singleTask启动模式。
FLAG_ACTIVITY_SINGLE_TOP,这个标记位作用是为Activity指定singleTop启动模式。
FLAG_ACTIVITY_CLEAR_TOP.这个标记为,当它启动时,在同一个任务栈所有位于它上面的Activity出栈,这个一般和LAG_ACTIVITY_NEW_TASK配合使用,在这种情况下,被启动的Activity如果已经存在,那么系统就会调用它的onNewIntent方法。如果启动模式为standard模式。那么连同它之上的Activity都要出栈。系统会创建新的Activity实例并放入栈顶。
FLAG_ACTIVITY_EXCLUDE_FORM_RECENTS:具有这个标记位的Activity不会出现在历史Activity的列表中。等同于在XML中指定Activity属性android:excludeFormRecents="true"
Activity的匹配规则:
我们知道Activity启动方式分为两种,显示调用和隐示调用。显示调用不多做介绍。隐示调用需要Intent能够匹配目标组件的IntentFilter中所设置的过滤信息,如果不匹配将无法启动目标Activity,IntentFilter的过滤信息有Action,category,data。
一个activity有多个Intent-filter,一个Intent匹配任何一组inten-filter即将可成功启动对应的Activity.
Action的匹配规则:
一个过滤当中有多个action,那么只要intent中的action能够和过滤规则中的任何一个action相同匹配成功即可。 需要注意的是:如果Intent中没有指定Action那么匹配失败。 总结:action的匹配要求Intent中的action存在且和过滤规则总的其中一个action相同,(也就是说还可以有Action不需要跟过滤规则中的相同) action区分大小写
category的匹配规则:
category的匹配规则和action不同,它要求如果Intent中含有category,那么所有的category都必须和过滤规则中的一个category相同。换句话说,只有Intent中存在category不管有几个,对于每个category来说,它必须是过滤规则中定义的category。由于系统在调用startActivity或者startActivityforResult的时候默认会加上Android.intent.category.DEFAULT,所有我们在写Inten-filter的时候尽量带上android.inten.category.DEFAULT.
data的匹配规则:
data语法:
<data android:scheme="sting" android:host="string“ android:port="String" anroid:path="string" anroid:pathPattern="stirng" android:pathPrefile=”string" android:mimeType=“string"/>
data由两部分组成:
mimeType和URI。mimeType指媒体类型。
URI的结构:
<scheme>://<host>:<port>/[<path>|<pahtPrefix|<pathPattern>
scheme:URI的模式,比如http,file,content,如果URI中没有指定scheme,那么整个URI的其他参数无效。
HOST:URI的主机名,比如www.baidu.com.如果Host未指定,那么整个URI的其他参数无效
Port:URI 的端口号,比如80,仅当URI指定了scheme和host参数port参数才能有意义。
path.pathPattern和pathPrefix,这三个参数表示路径信息, path表示完整路径,pahtPattern表示也是完整路径,但是可以包含通配符"*","*"表示0个或者多个字符串。pathPrefix表示路径的前缀信息。
data的匹配 规则:
<data android:mimeType="image/*">
这种情况下。那么Inten中的mimeType属性必须有Image/*才能够匹配。虽然没有指定uri但是却有默认值。uri的默认值为content或者file.也就是说。虽然没有指定URI,但是Intent中URI部分的scheme必须为content或者file才能匹配
注意:
intent.SetDataAndType(URi.parse("file://abc"),"image/png");
另外如果要完整的data必须调用setDataAndType方法。setData在和setTye会清除对方的的值
最后
通过隐示启动的Intent的时候,可以做一些判断,看是否有Activity能偶匹配我们Intent,否则就会报Activity找不到错误。判断有方法有两种,采用PackageManager,resolveActivity方法或Intent的resolveActivity方法,如果找不到匹配的Activity就会返回null 。
也可以通过PagemanagerrueryIntentActvityes方法,这个方法和resolveActivity不同的是,它不是返回最佳的匹配Activity信息,而是所有符合匹配规则的Activity的信息的集合。 如下:
public abstract List<ResolveInfo> queryIntentActivities(Intent intent,inf flags);
public abstract ResolveInfo ResolveActivity(Intent intent,int Flags);
第一个参数比较好理解,第二个参数需要注意,我们使用MATCH_DEFAULT_ONLY这个标记位。这个标记位的含义是仅仅匹配那一些在intent-filter中声明了<category andr oid:name="android.intent.category.DEFAULT"/> 这个category的Activity。
使用这个标记意义在于,只要上述方法不反回null,那么startActivity一定可以匹配成功。(因为startActivity方法自带<category andr oid:name="android.intent.category.DEFAULT"/> ,根据上面的category匹配规则。) 如果不含这个标记可能导致startActivity失败。
有一类action和category比较重要,他们是
<action adroid:name=“android.intent.action.main>
<category android:name="android.intent.categor.LAUNCHER>
这二者公同的作用表面Activity的入口。并且会出现在系统的应用列表中,少了任何一个都没有实际意义。也无法出现在系统的应用列表当中。也就是这二者缺一不可
另外。针对Service和BroadcastReceiver.Packagemanager同样提供了类似的方法去获取成功匹配的组件信息
- Android 开发艺术与探究 第一章 Activity 的生命周期和启动模式
- Android开发艺术-第一章Activity的生命周期和启动模式
- Android开发艺术探究(一):Activity的生命周期和启动模式
- 【Android开发艺术探索读书笔记】 第一章 Activity的生命周期与启动模式
- 第一章Activity的生命周期和启动模式(Android开发艺术探索)
- Activity的生命周期和启动模式-Android开发艺术探索读书笔记第一章
- Android开发艺术探索笔记——第一章:Activity的生命周期和启动模式
- Android开发艺术探索读书笔记 第一章 Activity的生命周期和启动模式
- Android 艺术开发探索 第一章 Activity 的生命周期和启动模式观后感
- 《Android开发艺术探索》读书笔记-第一章 Activity的生命周期和启动模式
- Activity生命周期和启动模式 - Android开发艺术探索读书笔记(第一章)
- 《Android 开发艺术探索》随手笔记——第一章Activity生命周期和启动模式
- 《Android开发艺术探索》第一章Activity的生命周期和启动方式小节
- 《Android开发艺术探索》读书笔记----第一章:Activity的启动模式
- Android开发艺术探索--Activity生命周期和启动模式
- <<Android 开发艺术探索>> 第一章 Actiivty的生命周期和启动模式
- Android开发艺术探索--第一章Acitivity的生命周期和启动模式
- 《Android开发艺术探索》读书笔记之Activity的生命周期与启动模式
- 记毕设中遇到的菜鸡问题----3
- 215. Kth Largest Element in an Array
- myBatis配置(一)
- 动态数组
- 剑指offer1:赋值运算符函数
- Android 开发艺术与探究 第一章 Activity 的生命周期和启动模式
- myBatis(二)方法、控制、接口使用
- 让自己的软件出现在选择打开列表的软件中
- MVC和三层架构
- leetcode---Word Pattern
- DOS下操作数据库基本语法(简明扼要)
- Hacker-1
- n阶蛇形矩阵
- CodeForces 652A Gabriel and Caterpillar(模拟)