文章首发在个人公众号:追风栈Binary。希望共同交流和相互探讨。
当我们在评价一个
App
的时候,经常会从流畅度、稳定性以及人性化这些角度去多维度的比较。一些操作流畅、页面精美的App
即使没有什么大的实用意义,但我们都会选择多看几眼,相反的是,如果一个App
启动慢,页面滑动卡顿,经常需要重新加载页面信息,那么毫无疑问,卸载是对它最后的仁慈。Android开发是通过Activity(活动)
来完成与用户的交互,除了Activity
的页面内容要丰富以外,也需要关注一个Activity
是如何启动的,它的生命历程到底是怎么样的,我们才可以更好的优化应用程序。
什么是Activity?
简单的说,Activity
就是应用程序的一个个页面,例如下面的两个应用程序的页面,制作的非常精美。
Activity
是Android应用程序重要的组成部分,它直接面向用户,接受用户的输入,并按照用户的意图来响应相应的操作。也正是因为它与用户直接交互,也就注定了Activity
在应用程序中的重要性,就拿主页面来讲,如果连打开程序的主页面进行操作都会觉得卡顿不适的话,那用户很大可能就因此失去了了解这款App
的兴趣,他可能就会选择不再打开或者直接卸载。所有的App
都强调用户体验第一,就是这个道理。
如何形象的理解应用程序中的Activity
使用任何App
的时候,我们可以感受得到页面仿佛是可以堆叠的,例如在主页面点击了一个登录
按钮后就会跳转至登录详情页,登陆成功后就又会返回到我们的主页面。那么显示登录页面的时候,主页面去哪里了?为什么后面它又会原封不动的回来?
![](https://img.haomeiwen.com/i2811416/a68447d95782bf13.jpg)
实际情况与我们对于这种切换的认知是一致的,Activity
可以堆叠,我们切换页面,使得不同的页面显示在当前的屏幕中,而之前的页面被系统管理了起来。以箱子和同等大小的木板来做类比,过程如下:
-
箱子是一个管理
Activity
的机制,木板就是一个个的Activity
-
我们永远只能看到箱子最上面的那块木板,意味着我们只能看到显示在屏幕当前的
Activity
-
我们点击进入新的页面,就好比往箱子里添加了一块新的木板,此时新木板处于最顶端,也正是我们看到的页面
-
当我们返回上一个页面时,就好比把最上面的木板从箱子里抽取出来,我们回到了原来的界面
-
当程序退出时,相当于木板都被抽取出来,箱子此时是空的。
Android使用Task
来管理活动,一个Task
就是一组Activity
的集合,而上面的箱子,在Android中被称为返回栈
,栈的特性就是和箱子一样,先进来的反而后出去。这也和我们使用App
的感受一致,如果从主页面点开另一个页面,然后从另一个页面点开再另一个页面...,到后面要返回到主页面时,就不得不一直按返回键
,直到最后达到主页面。其实这些操作,就是Activity
不断入栈和出栈的过程。
![](https://img.haomeiwen.com/i2811416/3607cded2901a7c1.jpg)
Activity生命周期中有哪些方法?
在新建一个Android Studio
工程的时候,选择Empty Activity
后,会自动的创建一个MainActivity
,并且会自动的在AndroidManifest
文件中声明该Activity
为启动页面:
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity></pre>
也就是说,点击应用图标后,首先映入眼帘的就是这个界面(当然这个指的是主页面,而并不是指启动页面)。可以指定任何程序中的Activity
作为程序的主页面,但是需要在AndroidManifest
文件中进行<intent-filter>
的相关配置。
Android对Activity
生命周期定义了七种方法,分别如下:
-
onCreate()
:指的是Android系统初次创建这个Activity
时调用的方法,这是一个必须实现的方法,我们需要在里面设置setContentView()
方法,并且还要进行一系列控件的绑定,onCreate()
接收一个savedInstanceState
的参数,这个参数的意思是保存Activity
先前状态的信息,但是如果是第一次创建,那么saveInstanceState
中就没有任何的信息。 -
onStart()
:指的是系统将这个Activity
变为用户可见,这个方法执行的十分快速,紧接着就是onResume()
-
onResume()
: 指的是Activity
准备好与用户进行交互了,此时它处于返回栈的栈顶,是用户直接接触的对象 -
onPause()
: 指的是当Activity
不再是处于栈顶,但它可能还是会对用户可见的时候,一般指的是当前页面突然弹出一个对话框,此时Activity
就是处于onPause()
状态 -
onStop()
: 指的是当Activity
完全不可见的时候,例如主页面跳转到登录页面,那么此时主页面就是处于onStop
状态 -
onDestroy()
: 指的是Activity
将要被销毁了,例如在主页面按返回键回到桌面时,会调用此方法 -
onRestart()
: 当Activity
从onStop()
状态到onStart()
状态下切换时调用,例如从登录页面返回到主页面
本来打算自己画一个流程图,但是Android指南上的流程图太经典了,绘制的很详细,所以直接把那幅图搬到这里来。
![](https://img.haomeiwen.com/i2811416/191ec4e45b796fb6.png)
还可以看出,除了onRestart()
,其余的六种方法都是配对出现的,onCreate()
对应onDestroy()
、onStart()
对应onStop()
、onResume()
对应onPause()
Activity的生命周期示例
文章的重点是讨论Activity
的生命周期,也就是要探讨程序启动、页面跳转、弹出对话框、程序退出等场景时Activity
的行为,具体分析如下两个方面
-
Activity
在这些场景下,它做了哪些事情 -
Activity
在这些场景下,它分别处于什么状态
为了更直观的展示,建立了一个ActivityLifeCycleDemo
的示例,很多时候Android Studio默认生成了必须重写的onCreate()方法,而其他方法则默认继承父类方法,导致我们无法查看生命周期状态,这个例子重写了继承的方法,加入了Log以便打印信息。
跳转逻辑:MainActivity(启动主页面)、点击"跳转至正常的Activity"跳转到"这是正常的Activity"、点击"跳转至对话框Activity"跳转到"这是对话框的Activity"
// MainActivity代码,重写了继承父类的一些方法
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "1: 我在onCreate()方法");
setContentView(R.layout.activity_main);
initView();
}
private void initView(){
Button turnToNormal = findViewById(R.id.start_normal);
Button turnToDialog = findViewById(R.id.start_dialog);
turnToNormal.setOnClickListener(this);
turnToDialog.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.start_normal:
Intent normalIntent = new Intent(this, NormalActivity.class);
startActivity(normalIntent);
break;
case R.id.start_dialog:
Intent dialogIntent = new Intent(this, DialogActivity.class);
startActivity(dialogIntent);
break;
default:
break;
}
}
@Override
protected void onStart() {
super.onStart();
Log.d(TAG, "2: 我在onStart()方法");
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "3: 我在onResume()方法");
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "4: 我在onPause()方法");
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG, "5: 我在onStop()方法");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "6: 我在onDestroy()方法");
}
@Override
protected void onRestart() {
super.onRestart();
Log.d(TAG, "7: 我在onRestart()方法");
}
1.当点开程序图标,MainActivity中发生了什么?
点击程序图标,进入MainActivity,显示主页面。由于打印了Log
日志,所以可以使用Logcat
查看这些输出信息。
![](https://img.haomeiwen.com/i2811416/99efa17e65c4423f.jpg)
可以看到Logcat
打印出了三条信息,并且调用方法的顺序依次是onCreate()
、onStart()
和onResume()
。这说明当系统第一次启动该应用的时候,会通过onCreate
方法来创建一个MainActivity
的实例,完成控件和事件的绑定,例如上面程序中的findViewById()
和onClick()
这些方法。并随后调用onStart()
和onResume()
的方法,这些方法的执行很快,从时间上看,两个方法几近是同一时刻调用的。完成这三个方法后,主页面显示在用户面前,并一直处于onResume
状态。
2.从MainActivity切换到正常的Activity,会发生什么?
点击主页面的跳转至正常的Activity
按钮,程序会跳转到另外一个界面上:
查看Logcat
的输出信息
![](https://img.haomeiwen.com/i2811416/c61a4924c346b86c.jpg)
可以看出,从MainActivity
跳转至另外一个Activity
时,MainActivity
会依次调用onPause()
和onStop()
的方法,正如指南中所说,当Activity
完全不可见时,调用onStop()
的方法,这一点在示例中得到了验证。现在MainActivity
不再处于栈顶,处于栈顶的是这个正常的Activity
3.从正常的Activity返回到主页面会调用什么?
现在我们要返回到MainActivity
,通过按返回键就可以实现:
查看Logcat
的输出信息
![](https://img.haomeiwen.com/i2811416/f886931c6abe3498.jpg)
注意,从完全不可见恢复到完全可见的状态,是通过调用onRestart()
方法来实现的,然后再依次调用onStart()
和onResume()
方法,重新获得焦点,现在MainActivity
又回到了栈顶了。
4.从MainActivity到对话框Activity,又会发生什么?
我们现在从MainActivity
出发,点击跳转至对话框Activity
按钮,跳至对话框页面。但要注意,对话框是一种局部覆盖MainActivity
的页面,它不会让MainActivity
完全消失。
查看Logcat
,看看这个过程与上一个过程到底有何不同。
![](https://img.haomeiwen.com/i2811416/f8a569ff4ca102ed.jpg)
这个调用过程并不像是上面过程一样,这里的MainActivity
只调用了onPause()
方法,而没有继续调用onStop()
方法,这说明MainActivity
并没有完全消失,从直观的角度来看,我们仍然可以看得到对话框下的MainActivity
,只不过是被对话框页面蒙上了一层灰色。
5.从对话框页面返回MainActivity,会发生什么?
现在从对话框页面点击返回键回到主页面
查看Logcat
的输出信息
![](https://img.haomeiwen.com/i2811416/e2d3332bd36f5272.jpg)
从对话框跳回至MainActivity
,MainActivity
直接调用了onResume()
方法,这一步也没有调用onRestart()
方法。
6.在MainActivity上按返回键退出,会发生什么?
测试在主页面上直接按返回键,返回到手机桌面上,这一步过程中MainActivity
会调用哪些方法。依旧查看Logcat
,查看输出信息:
![](https://img.haomeiwen.com/i2811416/13da450a08838131.jpg)
退出应用时,程序相当于被关闭了。MainActivity
依次调用onPause()
、onStop()
之后,会调用onDestroy()
方法来销毁自身。那如果不是按返回键,而是按Home
键回到桌面上,那会有区别吗?
7. 在MainActivity上按HOME键回到桌面,会发生什么?
在MainActivity
上直接按Home
键,也同样会回到桌面上,那么此时MainActivity
会把自身销毁掉吗?查看Logcat
的输出结果,就会知道答案。
![](https://img.haomeiwen.com/i2811416/725cae25614c3750.jpg)
Logcat
的输出结果表明,点按Home
键回到桌面上时,MainActivity
并不会调用onDestroy()
的方法,反而是和MainActivity
被另一个页面覆盖的结果相同。但是这种方式回到桌面,那么程序很有可能会被后台管理程序回收,这就是为什么有时我们打开微信后又回到桌面上,过一段时间打开微信需要重新进入的原因。
至此,Activity
生命周期的讨论就可以先告一段落了。
如何快速的记住这些方法?
都说把它们全部理解了,就顺便记下来了。但我觉得,可能先记下来,再后面遇到了慢慢理解也不迟。七个方法按一个Activity
创建到销毁的顺序来,会好记很多。
我个人喜欢用通俗的方式来记忆,比如说这七个方法,除去特殊的onStart()
外,按照每个方法的大写字母来顺序排列,那就是C-S-R-P-S-D
,其中C-S
就记忆成反恐精英
,R-P
记忆成人品
,S-D
记忆成SD卡
,最后只需要单独记忆一个onStart()
就可以了。
![](https://img.haomeiwen.com/i2811416/4761cf7acb06ccd4.jpg)
网友评论