Activity的生命周期
Activity整个的生命周期如下所示,这张图是从Android API上扒下来了,我觉得API上关于生命周期已经讲解的很详细了,我也就不啰嗦了,就简要的说下自己的一些总结:
做工作中,你可能感兴趣的三个关键环
-
① 完整生命周期
-
② 可见生命周期
-
③ 可交互生命周期
如图所示,图中的周期都是大的包括小的:
在实际工作中的使用
-
①onResume可见, 可交互.。把动态刷新的操作启动。
-
②onPause部分可见, 不可交互. 把动态刷新的一些操作, 给暂停了。
-
③onCreate 初始化一些大量的数据
-
④onDestroy 把数据给释放掉, 节省内存。
横竖屏切换问题
横竖屏切换时,默认情况下会把activity先销毁再创建,在类似手机游戏这一类的应用中,这个体验是非常差的。不让Activity在横竖屏切换时销毁,只需要在清单文件声明Activity时配置<activity>
节点的几个属性即可,其方式如下:
<code class="language-xml hljs has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;">android:configChanges="orientation|keyboardHidden|screenSize"</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>
解释
-
①configChange=”orientation”屏幕方向改变:不让屏幕在切换时重新创建activity
-
②sreensize 屏幕大小
-
③keyboardHidden是软键盘,如果切换屏幕,软键盘会去判断屏幕大小是否合适显示软键盘,在判断过程中会重启activity
设置横屏或者竖屏
-
android:screenOrientation=”portrait” 竖屏
-
android:screenOrientation=”landscape”横屏
任务栈的概念
任务栈是用来提升用户体验而设计的:
-
(1) 程序打开时就创建了一个任务栈, 用于存储当前程序的activity,所有的activity属于一个任务栈。
-
(2) 一个任务栈包含了一个activity的集合, 去有序的选择哪一个activity和用户进行交互:只有在任务栈栈顶的activity才可以跟用户进行交互。
-
(3) 任务栈可以移动到后台, 并且保留了每一个activity的状态. 并且有序的给用户列出它们的任务, 而且还不丢失它们状态信息。
-
(4) 退出应用程序时:当把所有的任务栈中所有的activity清除出栈时,任务栈会被销毁,程序退出。
任务栈的缺点:
-
(1) 每开启一次页面都会在任务栈中添加一个Activity,而只有任务栈中的Activity全部清除出栈时,任务栈被销毁,程序才会退出,这样就造成了用,户体验差, 需要点击多次返回才可以把程序退出了。
-
(2) 每开启一次页面都会在任务栈中添加一个Activity还会造成数据冗余, 重复数据太多, 会导致内存溢出的问题(OOM)。
为了解决任务栈产生的问题,Android为Activity设计了启动模式,那么下面的内容将介绍Android中Activity的启动模式,这也是最重要的内容之一。
Activity的4种启动模式
启动模式(launchMode)在多个Activity跳转的过程中扮演者重要的角色,它可以解决是否生成新的Activity实例,是否重用已经存在的Activity实例,是否和其他实例共用一个任务栈。任务栈是一个具有站结构的对象,一个任务栈可以管理多个Activity,每启动一个应用,也就创建一个与之对应的任务栈。
Activity一共有以下四种launchMode模式:
-
① standard
-
② singTop
-
③ singTask
-
④ singleInstance
我们可以在AndroidManifest.xml配置<activity>
的android:launchMode属性为以上四种之一即可。
为了详细讲解启动模式,我们创建一个工程,里面有两个Activity,分别是FirstActivity
和SecondActivity
,启动模式都是standard
。下面是界面截图和关键代码:
界面一
界面二
FirstActivity文件:
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onCreate</span>(Bundle savedInstanceState) {
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
System.out.println(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"当前任务栈ID:"</span> + getTaskId() + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"当前是第一个界面("</span> + <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.toString()+<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">")"</span>);
}
<span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onDestroy</span>() {
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.onDestroy();
System.out.println(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"第一个界面("</span>+<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.toString() + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">")退出任务栈"</span>);
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li></ul>
SecondActivity文件
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onCreate</span>(Bundle savedInstanceState) {
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
System.out.println(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"当前任务栈ID:"</span> + getTaskId() + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"当前是第二个界面("</span> + <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.toString()+<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">")"</span>);
}
<span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onDestroy</span>() {
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.onDestroy();
System.out.println(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"第二个界面("</span>+<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.toString() + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">")退出任务栈"</span>);
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li></ul>
standard 详细讲解
-
概述:在本模式中,不管有没有已存在的实例,都生成新的实例。即每次调用startActivity()启动时都会创建一个新的Activity放在栈顶,可重复创建。
-
操作:启动程序后,执行以下操作
①激活第二个界面 -> 激活第一个界面 -> 激活第二个界面 -> 激活第一个界面 ->激活第二个界面
②狂按返回键
-
打印的Log如下所示:
-
栈中的情况如下所示:
-
每次激活都会创建新的实例,每次返回都会销毁实例并出栈
singleTop详细讲解
-
概述:如果任务栈的栈顶存在这个要开启的activity,不会重新的创建activity,而是复用已经存在的activity。保证栈顶如果存在,不会重复创建。
-
为了讲解的更清楚,我在按钮中加入了如下Log
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">first</span>(View v) {
Intent intent = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Intent(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>, FirstActivity.class);
startActivity(intent);
System.out.println(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"想要激活第一个页面"</span>);
}
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">second</span>(View v) {
Intent intent = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Intent(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>, SecondActivity.class);
startActivity(intent);
System.out.println(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"想要激活第二个页面"</span>);
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li></ul>
-
操作:启动后执行一下操作
①激活第二个界面 -> 激活第一个界面 -> 激活第一个界面 -> 激活第一个界面
②按三次返回键
-
打印的Log如下所示:
-
栈中的情况如下所示:
-
注意当第一个页面处于栈顶,并且还想要激活第一个界面时就会因为singleTop
模式不再创建新的实例;但如果第一个界面不再栈顶,那么还是会创建新的实例的。
singleTask详细讲解
-
概述:在当前任务栈里面只能有一个实例存在;当开启activity的时候,就去检查在任务栈里面是否有实例已经存在,如果有实例存在就复用这个已经存在的activity,并且把这个activity上面的所有的别的activity都清空,复用这个已经存在的activity。保证整个任务栈里面只有一个实例存在;
-
操作:启动后执行一下操作
①激活第一个界面 -> 激活第二个界面 -> 激活第二个界面 -> 激活第一个界面
②按一次返回键
-
打印的Log如下所示:
-
这里的栈结构没法用图画出来了,我就描述一下Log吧。
-
应用开启,第一条Log打出,然后点击“激活第一个界面”,由于栈中已经有实例了,所以没有创建新的实例,直接复用。然后点击“激活第二个界面”,转到第二个界面,并创建出实例对象,然后在点击一次“激活第二个界面”,有创建了一个实例对象;此时,点击“激活第一个界面”,这时会去检查任务栈是否有第一个界面的实例,发现存在实例,直接复用,并把第一个界面栈上面的两个第二个界面的实例直接出栈。
-
最后点击返回键,由于此时栈中只剩下第一个界面的实例了,所以就只打印了一次Log。
singleInstance详细讲解
-
概述:这种启动模式比较特殊,因为它会启用一个新的栈结构,将Activity放置于这个新的栈结构中,并保证不再有其他Activity实例进入。
-
操作:启动后执行一下操作
①激活第二个界面 -> 激活第二个界面 -> 激活第一个界面
②按3次返回键
-
打印的Log如下所示:
-
栈中的情况如下所示:
-
正如概述中所说的,应用启动时,由于给第一个界面配置的singleInstance
模式,所以直接创建了一个新的任务栈给第一个Activity,并保证不会有其他Activity入栈,并且还保证了实例的复用。然后去激活第二个Activity,由于第二个界面是标准模式,所以直接创建了新实例、并入栈。