文章首发在个人公众号:追风栈Binary。希望共同交流和相互探讨。
当我们在评价一个
App
的时候,经常会从流畅度、稳定性以及人性化这些角度去多维度的比较。一些操作流畅、页面精美的App
即使没有什么大的实用意义,但我们都会选择多看几眼,相反的是,如果一个App
启动慢,页面滑动卡顿,经常需要重新加载页面信息,那么毫无疑问,卸载是对它最后的仁慈。Android开发是通过Activity(活动)
来完成与用户的交互,除了Activity
的页面内容要丰富以外,也需要关注一个Activity
是如何启动的,它的生命历程到底是怎么样的,我们才可以更好的优化应用程序。
什么是Activity?
简单的说,Activity
就是应用程序的一个个页面,例如下面的两个应用程序的页面,制作的非常精美。
Activity
是Android应用程序重要的组成部分,它直接面向用户,接受用户的输入,并按照用户的意图来响应相应的操作。也正是因为它与用户直接交互,也就注定了Activity
在应用程序中的重要性,就拿主页面来讲,如果连打开程序的主页面进行操作都会觉得卡顿不适的话,那用户很大可能就因此失去了了解这款App
的兴趣,他可能就会选择不再打开或者直接卸载。所有的App
都强调用户体验第一,就是这个道理。
如何形象的理解应用程序中的Activity
使用任何App
的时候,我们可以感受得到页面仿佛是可以堆叠的,例如在主页面点击了一个登录
按钮后就会跳转至登录详情页,登陆成功后就又会返回到我们的主页面。那么显示登录页面的时候,主页面去哪里了?为什么后面它又会原封不动的回来?
实际情况与我们对于这种切换的认知是一致的,Activity
可以堆叠,我们切换页面,使得不同的页面显示在当前的屏幕中,而之前的页面被系统管理了起来。以箱子和同等大小的木板来做类比,过程如下:
箱子是一个管理
Activity
的机制,木板就是一个个的Activity
我们永远只能看到箱子最上面的那块木板,意味着我们只能看到显示在屏幕当前的
Activity
我们点击进入新的页面,就好比往箱子里添加了一块新的木板,此时新木板处于最顶端,也正是我们看到的页面
当我们返回上一个页面时,就好比把最上面的木板从箱子里抽取出来,我们回到了原来的界面
当程序退出时,相当于木板都被抽取出来,箱子此时是空的。
Android使用Task
来管理活动,一个Task
就是一组Activity
的集合,而上面的箱子,在Android中被称为返回栈
,栈的特性就是和箱子一样,先进来的反而后出去。这也和我们使用App
的感受一致,如果从主页面点开另一个页面,然后从另一个页面点开再另一个页面...,到后面要返回到主页面时,就不得不一直按返回键
,直到最后达到主页面。其实这些操作,就是Activity
不断入栈和出栈的过程。
Activity生命周期中有哪些方法?
在新建一个Android Studio
工程的时候,选择Empty Activity
后,会自动的创建一个MainActivity
,并且会自动的在AndroidManifest
文件中声明该Activity
为启动页面:
也就是说,点击应用图标后,首先映入眼帘的就是这个界面(当然这个指的是主页面,而并不是指启动页面)。可以指定任何程序中的Activity
作为程序的主页面,但是需要在AndroidManifest
文件中进行
的相关配置。
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指南上的流程图太经典了,绘制的很详细,所以直接把那幅图搬到这里来。
还可以看出,除了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
查看这些输出信息。
可以看到Logcat
打印出了三条信息,并且调用方法的顺序依次是onCreate()
、onStart()
和onResume()
。这说明当系统第一次启动该应用的时候,会通过onCreate
方法来创建一个MainActivity
的实例,完成控件和事件的绑定,例如上面程序中的findViewById()
和onClick()
这些方法。并随后调用onStart()
和onResume()
的方法,这些方法的执行很快,从时间上看,两个方法几近是同一时刻调用的。完成这三个方法后,主页面显示在用户面前,并一直处于onResume
状态。
2.从MainActivity切换到正常的Activity,会发生什么?
点击主页面的跳转至正常的Activity
按钮,程序会跳转到另外一个界面上:
查看Logcat
的输出信息
可以看出,从MainActivity
跳转至另外一个Activity
时,MainActivity
会依次调用onPause()
和onStop()
的方法,正如指南中所说,当Activity
完全不可见时,调用onStop()
的方法,这一点在示例中得到了验证。现在MainActivity
不再处于栈顶,处于栈顶的是这个正常的Activity
3.从正常的Activity返回到主页面会调用什么?
现在我们要返回到MainActivity
,通过按返回键就可以实现:
查看Logcat
的输出信息
注意,从完全不可见恢复到完全可见的状态,是通过调用onRestart()
方法来实现的,然后再依次调用onStart()
和onResume()
方法,重新获得焦点,现在MainActivity
又回到了栈顶了。
4.从MainActivity到对话框Activity,又会发生什么?
我们现在从MainActivity
出发,点击跳转至对话框Activity
按钮,跳至对话框页面。但要注意,对话框是一种局部覆盖MainActivity
的页面,它不会让MainActivity
完全消失。
查看Logcat
,看看这个过程与上一个过程到底有何不同。
这个调用过程并不像是上面过程一样,这里的MainActivity
只调用了onPause()
方法,而没有继续调用onStop()
方法,这说明MainActivity
并没有完全消失,从直观的角度来看,我们仍然可以看得到对话框下的MainActivity
,只不过是被对话框页面蒙上了一层灰色。
5.从对话框页面返回MainActivity,会发生什么?
现在从对话框页面点击返回键回到主页面
查看Logcat
的输出信息
从对话框跳回至MainActivity
,MainActivity
直接调用了onResume()
方法,这一步也没有调用onRestart()
方法。
6.在MainActivity上按返回键退出,会发生什么?
测试在主页面上直接按返回键,返回到手机桌面上,这一步过程中MainActivity
会调用哪些方法。依旧查看Logcat
,查看输出信息:
退出应用时,程序相当于被关闭了。MainActivity
依次调用onPause()
、onStop()
之后,会调用onDestroy()
方法来销毁自身。那如果不是按返回键,而是按Home
键回到桌面上,那会有区别吗?
7. 在MainActivity上按HOME键回到桌面,会发生什么?
在MainActivity
上直接按Home
键,也同样会回到桌面上,那么此时MainActivity
会把自身销毁掉吗?查看Logcat
的输出结果,就会知道答案。
Logcat
的输出结果表明,点按Home
键回到桌面上时,MainActivity
并不会调用onDestroy()
的方法,反而是和MainActivity
被另一个页面覆盖的结果相同。但是这种方式回到桌面,那么程序很有可能会被后台管理程序回收,这就是为什么有时我们打开微信后又回到桌面上,过一段时间打开微信需要重新进入的原因。
至此,Activity
生命周期的讨论就可以先告一段落了。
如何快速的记住这些方法?
都说把它们全部理解了,就顺便记下来了。但我觉得,可能先记下来,再后面遇到了慢慢理解也不迟。七个方法按一个Activity
创建到销毁的顺序来,会好记很多。
我个人喜欢用通俗的方式来记忆,比如说这七个方法,除去特殊的onStart()
外,按照每个方法的大写字母来顺序排列,那就是C-S-R-P-S-D
,其中C-S
就记忆成反恐精英
,R-P
记忆成人品
,S-D
记忆成SD卡
,最后只需要单独记忆一个onStart()
就可以了。