前言:最近整理了下2017这一年刷的书(为了加深记忆打算去做总结)
安卓方向:《Android群英传》丶《Android开发艺术探索》丶《Android系统源代码情景分析》
数据结构和算法:《大话数据结构》丶《数据结构与算法分析》丶《编程之美》丶《剑指offer》
设计模式:《Andorid源码设计模式》
其它:《Java并发编程实战》丶《图解HTTP》丶LintCode刷到中级(为了查答案又成了九章学员(>_<))
Activity的生命周期
Activity生命周期分为两种:第一种是典型(正常)情况下的生命周期,第二种是异常情况下的生命周期。
~~~~第一种:典型情况下的生命周期:
针对图具体说明:
1:Avtivity第一次启动,回调:onCreate -> onStart ->OnResume。
2:当用户打开新的Activity或者切换到桌面时,回调:onPause ->onStop。特殊情况:如何新启动的Activity
采用了透明主题时,回调onPause,不会回调onStop。
3:当用户再次回到原Activity,回调onRestart -> onStart ->onResume。
4:当用户按back键回退时,回调onPause ->onStop ->onDestroy。
5:当Activity被系统回收后再次打开,生命周期方法回调过程和1一样,注意只是生命周期方法一样,不代表所
有过程都一样。
6:从整个生命周期方法来说,onCreate和onDestroy是配对的 ,对应Activity的创建与销毁;onStart 和 onStop 是配对的,对应Activity的可见与不可见;onResume和onPause是配对的,对应可交互与不可 交互。(注:执行onStop方法后是不可见的,执行onPause方法后是不可交互的)
解答两个问题:
1:onStart和onResume丶onPause和onStop从描述上来看差不多,对我们来说有什么实质的不同呢 ?
答:从实际使用过程来说,onStart和OnResume丶onPause和onStop看起来的确差不多,甚至我们可以只保
留其中的一对。但这两个配对分别表示不同的意义,onStart和onStop是从Activity是否可见这个角度来回调的,
onResume和onPause是从Activity是否位于前台(即:可交互)角度来回调的。在实际使用中没有其他明显的区别。
2:假设当前Activity为A,如果这时用户打开一个新Activity B,那么B的onResume和A的onPause哪个先执行?
答:Activity A 先执行onPause,然后新Activity B 的onCreate丶onStart丶onResume才被调用。(作者是从
Activity的启动过程中找的答案) >>>另:关于四大组件的启动过程《Android开发艺术探索》的第九章和《Android
系统源代码情景分析》的第七~九章都做了介绍,后面我也会去做下整理。
~~~~第二种异常情况下的生命周期:包含两种情况一种是:资源相关的系统配置发生改变;另一种是:系统内存
不足时
*******一种:资源相关的系统配置发生改变(比如横竖屏切换)并不做特殊处理时:
说明:Activity会被销毁,其onPause丶onStop丶onDestroy均会被调用,同时会调用OnSaveInstanceState来
解答三个问题:
1:OnSaveInstanceState和onRestoreInstanceState系统默认给我们做了哪些恢复工作 ?
答:当异常情况下需要重新创建时,系统会默认为我们保存当前Activity的视图结构,并且在Activity重启后为
我们恢复这些数据,比如文本框中用户输入的数据丶ListView滚动的位置等,这些View相关的状态系统都能够默认为我们恢复。如果我们想知道具体的某个View能为我们恢复哪些数据,我们可以查看View的源码。和Activity一样,每个View都实现了OnSaveInstanceState和OnSaveInstanceState这两个方法,看一下他们的具体实现就知道系统能够自动为每个View恢复哪些数据。
2:关于保存和恢复View层次结构,系统的工作流程是怎样的 ?
答:Activity被意外终止后,Activity会调用OnSaveInstanceState去保存数据,然后Activity会委托Window
去保存数据,接着Window在委托 它上面的顶级容器去保存数据。顶层容器是一个ViewGroup,一般来说是DecorView。然后顶层容器DecoreView再去一 一通知他的子元素来保存数据。这是一种典型的委托思想,上层委托下层丶父容器委托子元素去处理一件事。
3:当系统配置发生改变后Activity会被重新创建,那么有没有办法不重新创建呢 ?
答:有的。系统配置中有很多内容(比如:屏幕方向丶本地位置),如果当某项内容发生改变后,我们不想系统
重新创建Activity可以给Activity设置对应的configChanges属性。比如屏幕旋转的时候我们不想Acitivity重新创建就
可以给configChanges属性添加orientation这个值,多个值可以用"|"连接。
*******另一种:情况系统内存不足导致低优先级的Activity被杀死
先讲下Activity优先级的三种情况(从高到低):
1:前台Activity-----正在和用户交互的Activity,优先级最高。
2:可见但非前台的Activity-----比如Activity中弹出一个对话框,导致Activity可见但是位于后台无法和用户直 接交互。
3:后台Activity-----已经被暂停的Activity,比如执行了onStop,优先级最低。
当系统内存不足时,系统就会按照上述优先级去杀死目标Activity所在的进程,并后续通过
OnSaveInstanceState 和OnSaveInstanceState来存储和恢复数据。如果一个进程中没有四大组件在执行,那么这个
进程很容易被杀死,比较好 的方法就是将后台工作放入Service中从而保证此进程有一定的优先级,这样就不会被轻
易杀死。
Activity启动模式
Activity启动模式:为了满足项目的特殊需求Andorid设计者为我们提供了四种启动模式:standard丶
singleTop丶singleTask和singleInstance下面先一 一介绍其含义:
1:standard :标准启动模式,也是系统默认的模式。每次启动都会重新创建一个新的实例,不管这个实例是否
存 在。生命周期方法同与正常情况,onCreate->onStart->onResume都会被调用。这是一种典型的多实例实现,
一个任务栈中可以有多个相同实例,每个相同实例也可以属于不同的任务栈。
注意:在这种模式下谁启动了这个Activity,那么这个Activity就运行在启动它的那个Activity所在的栈中。比如
activity A 启动了activity B ,那么B 就会进入到A所在的栈中。假如我们用ApplicationContext或Service去启动
standard模式的Activity就会报错,因为非Activity类型的Context并没有所谓的任务栈。可以通过为待启动的
Activity指定FLAG_ACTIVITY_NEW_TASK标记位这样就会为它创建一个新的任务栈。
2:singleTop :栈顶复用模式。如果新Activity已经位于任务栈的栈顶,那么此Activity就不会被重新创建。同
时onNewIntent会被回调,新activity的onCreate丶onStart丶onResume不会被调用。如果新Activity已存在但不是
位于栈顶,那么新Activity仍然会被重新创建。比如:假设目前栈内情况为ABCD,其中ABCD是为四个Activity,A
位于栈底,D位于栈顶,再次启动D,如果D的启动模式为singleTop,那么栈内的情况仍然为ABCD。如果D的启动
模式为standard,那么D会被重新创建,导致栈内的情况变为ABCDD。
使用场景:适用于接收到消息后显示的界面,例如QQ接收到消息后弹出Activity,如果一次来10条消息,总不
能弹10个Activity。
3:singleTask:栈内复用模式。这是一种单实例模式,只要Activity在一个栈中存在,那么多次启动此Activity
都不会重新创建实例,并会回调onNewIntent 。具体一点,当一个具有singleTask模式的Activity请求启动后,比如
说Activity A,系统首先会寻找是否存在A 想要的任务栈,如果不存在,就重新创建一个任务栈,然后创建A的实例后
把A放到栈中。 如若存在A所需的任务栈,这时要看A是否在栈中有实例存在,如果有实例存在,那么系统就会把A调
到栈顶,如果实例不存在,就创建A的实例并把A压入栈中。
举几个例子:1:比如目前任务栈S1中的情况为ABC,这个时候Activity D 以singleTask模式请求启动,其所需
要的任务栈为S2,由于S2和D的实例均不存在,所以系统会先创建任务栈S2,然后再创建D的实例并将其入栈到S2。
2:假设D所需要的任务栈为S1,其它情况如上面例子1所示,那么由于S1已经存在,所以系统会直接常见D的实
例并将其入栈到S1。3:如果D所需的任务栈为S1,并且当前任务栈S1的情况为ABCD根据栈内复用的原则,此时D
不会重新创建,系统 会把D切换到栈顶同时singleTask默认具有clearTop的效果,会导致栈内所有在D上面的
Activity全部出栈,于是最终S1中的情况为AD。
使用场景:通常可以用来退出整个应用。比如讲主Activity设置为singleTask模式,然后在要退出的Activity中转
到主Activity,从而将主Activity之上Activity都清除,然后重写onNewIntent方法,在方法中加上一句finish(),将最
后一个Activity结束掉。
4:singleInstance:单实例模式。这是一种加强的singleTask模式,它除了具有singleTask模式的所有特性
外,还加强了一点,那就是具有此种模式的Activity只能单独地位于一个任务栈中,比如Acitivity A是singleInstance
模式,当A启动后,系统会为它创建一个新的任务栈,然后A独自在这个新的任务栈中,由于栈内复用的特性,后续
的请求均不会创建新的Acitivity,除非这个独特的任务栈被系统销毁了。
使用场景:常用于需要与程序分离的界面,如在SetupWizard中调用紧急呼叫。解释:如果应用A的任务栈中创
建了MainActivity实例,且启动模式为singleInstance,此时如果应用B也要激活MainActivity,则不需要创建,两
个应用共享该Activity实例。
1:解释singleTask启动模式中,多次提到某个Activity所需的任务栈,什么是Activity所需的任务栈?
答:这要从一个参数说起:TaskAffinity,可以翻译为任务相关性。这个参数标识了一个Acitivity所需要的任务
栈的名字,默认情况下,所有Activity所需的任务栈的名字为应用的包名。当然,我们可以为每个Activity都单独制定
TaskAffinity属性,这个属性值必须不能和包名相同,否则就相当于没有指定。TaskAffinity主要和singleTask启动模
式或者allowTaskReparenting属性配对使用,在其它情况下没有意义。当TaskAffinity和singleTask启动模式配对使
用的时候,他是具有该模式的Activity的目前任务栈的名字,待启动的Activity会运行在名字和TaskAffinity相同的任
务栈中。
2:如何给Activity指定启动模式呢?
答:第一种通过AndroidMenifest为Activity的launchMode属性设置相应的启动模式;第二种是通过
Intent.addFlags(标志位)为Activity指定启动模式。两种设置方式的区别:1:优先级上,第二种方式的优先级高于第
一种,当两者同时存在时以第二种。2:限定范围上有所不同,第一种方式无法直接为Activity设定
FLAG_ACTIVITY_CLEAR_TOP标识,而第二种方式无法为Activity指定singleInstance模式。
Activity的标志位
Activity的Flags有很多,这里主要分析一些比较常用的标记位。标记位的作用很多,有的标记位可以设置
Activity的启动模式,比如FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_SINGLE_TOP等;还有的标记位可以影响
Activity运行状态FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS等。在使用标记位的
时候,要注意有些标记位是系统内部使用的,应用程序不需要去手动设置这些标记位以防出现问题。下面介绍几种常
用的标记位:
1:FLAG_ACTIVITY_NEW_TASK
这个标记位的作用是为Activity指定“singleTask”启动模式,其效果和在XML中指定该启动模式相同
2:FLAG_ACTIVITY_SINGLE_TOP
这个标记位的作用是为Activity指定“singleTop”启动模式,其效果和在XML中指定该启动模式相同
3: FLAG_ACTIVITY_CLEAR_TOP
具有次标记位的Activity,当它启动时,在同一个任务栈中所有位于它上面的Activity都要出栈,这个标记位一
般和singleTask启动模式一起出现,在这种情况下,被启动activity如果已经存在,那么系统就会调用的它的
onNewIntent。如果被启动的Activity采用standar模式启动,那么它连同它之上的Activity都要出栈,系统会创建新
的Activity实例并放入栈顶。
4:FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
具有这个标记的Activity不会出现在历史Activity的列表中,当某些情况下我们不希望用户通过历史列表回到我
们的Activity的时候这个标记比较有用。它等同于在XML中指定Activity的属性android:excludeFromRecents =
"true"。
^_^感谢您的阅读^_^