一、Activity的生命周期
掌握Activity的生命周期对于各任何Android开发者来说都非常重要。当深入理解Activity的生命周期之后,就可以写出更加连贯流畅的程序。
1、返回栈
通过前面的学习,我们发现Android中的Activity是可以层叠的。每新启动一个Activity,就会覆盖原来的Activity,然后点击back键会销毁最上面的,下面的一个Activity就会重新显示出来。
Android是使用任务(Task)来管理Activity的,一个任务就是一组存放在栈里的Activity集合,这个栈也被称作返回栈(Back Stack)。栈是一种先进后出的数据结构,在默认情况下,每当我们启动一个新的Activity,它会在返回栈中入栈,并处于栈顶。在我们按下back键销毁一个Activity时,处于栈顶的Activity会出栈,前一个入栈的Activity重新回到栈顶。系统总是将栈顶的Activity展示给用户。
2、Activity状态
每个活动在其生命周期中最多可能会有4种状态。
1). 运行状态
当一个活动位于返回栈的栈顶时, 这时活动就处于运行状态。系统最不愿意回收的就是处于运行状态的活动, 因为这会带来非常差的用户体验。
2). 暂停状态
当一个活动不再处于栈顶位置, 但仍然可见时, 这时活动就进人了暂停状态。你可能会觉得既然活动已经不在栈顶了, 还怎么会可见呢? 这是因为并不是每一个活动都会占满整个屏幕的,比如对话框形式的活动只会占用屏幕中间的部分区域, 你很快就会在后面看到这种活动。 处于暂停状态的活动仍然是完全存活着的, 系统也不愿意去回收这种活动〈因为它还是可见的, 回收可见的东西都会在用户体验方面有不好的影响 ), 只有在内存极低的情况下, 系统才会去考虑回收这种活动。
3). 停止状态
当一个活动不再处于栈顶位置, 并且完全不可见的时候, 就进入了停止状态。 系统仍然会为这种活动保存相应的状态和成员变量, 但是这并不是完全可靠的, 当其他地方需要内存时, 处于停止状态的活动有可能会被系统回收。
4).销毁状态
当一个活动从返回栈中移除后就变成了销毁状态。 系统会最倾向于回收处于这种状态的活动, 从而保证手机的内存充足。
3、Activity的生存期
Activity 类中定义了 7个回调方法, 覆盖了活动生命周期的每一个环节, 下面就来一一介绍这7个方法。
口 onCreate() 这个方法你已经看到过很多次了, 每个活动中我们都重写了这个方法, 它会在活动第一次被创建的时候调用。 你应该在这个方法中完成活动的初始化操作, 比如说加载布局、 绑定事件等。
口 onStart() 这个方法在活动由不可见变为可见的时候调用。
口 onResume() 这个方法在活动准备好和用户进行交互的时候调用。此时的活动定位于返回栈的栈顶 并且处于运行状态。
口 onPause() 这个方法在系统准备去启动或者恢复另一个活动的时候调用。我们通常会在这个方法中将那些消耗 CPU 的资源释放掉, 以及保存一些关键数据, 但这个方法的执行速度一定要快, 不然会影响到新的栈顶活动的使用。
口 onStop() 这个方法在活动完全不可见的时候调用。 它和 onPause()方法的主耍区别在于, 如果启动的新活动是一个对话框式的活动, 那么 onPause()方法会得到执行, 而onStop()方法并不会执行。
口 onDestory() 这个方法在活动被销毁之前调用, 之后活动的状态将变为销毁状态。
口 onRestart() 这个方法在活动曰停止状态变为运行状态之前调用, 也就是活动被重新启动了。
以上7个方法中除了onCreate(),其他都是两两相对的。在Android的官网上,提供一张非常清晰的示意图。
为了体验Activity的生命周期,我们可以创建一个工程来,修改代码:
public class ExampleActivity extends Activity {
public static final String TAG = "ExampleActivity";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
// The activity is being created.
}
@Override
protected void onStart() {
super.onStart();
Log.d(TAG, "onStart");
// The activity is about to become visible.
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume");
// The activity has become visible (it is now "resumed").
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "onPause");
// Another activity is taking focus (this activity is about to be "paused").
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG, "onStop");
// The activity is no longer visible (it is now "stopped")
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
// The activity is about to be destroyed.
}
}
重新运行应用,这时候我们就可以观察logcat中的打印日志来了解Activity的生命周期。以下是当 Activity A 启动 Activity B 时一系列操作的发生顺序:
1、Activity A 的 onPause() 方法执行。
2、Activity B 的 onCreate()、onStart() 和 onResume() 方法依次执行。(Activity B 现在具有用户焦点。)
3、然后,如果 Activity A 在屏幕上不再可见,则其 onStop() 方法执行。
我们可以利用这种可预测的生命周期回调顺序管理从一个 Activity 到另一个 Activity 的信息转变。 例如,如果必须在第一个 Activity 停止时向数据库写入数据,以便下一个 Activity 能够读取该数据,则应在 onPause() 而不是 onStop() 执行期间向数据库写入数据。
二、Activity的启动模式
Activity的启动模式一共有四种,我们应该根据特定的需求为每个Activity指定恰当的启动模式。这4种模式分别为standard、singleTop、singleTask、singleInstance,可以在AndroidManifest中通过给
1、standard
2、singleTop
3、singleTask
4、singleInstance
前面3个非常容易理解,第四个有一些特殊,需要多花一点功夫来理解。singleInstance模式的Activity会启动一个新的返回栈来管理这个Activity。想象以下场景, 假设我们的程序中有一个活动是允许其他程序调用的, 如果我们想实现其他程序和我们的程序可以共享这个活动的实例, 应该如何实现呢?使用前面3种启动模式肯定是做不到的, 因为每个应用程序都会有自己的返回栈, 同一个活动在不同的返回栈中人栈时必然是创建了新的实例。而使用 singleInstance模式就可以解决这个问题,在这种模式下会有一个单独的返回栈来管理这个活动, 不管是哪个应用程序来访问这个活动, 都共用的同一个返回栈, 也就解决了共享活动实例的问题。
实际举个例子
Activity1和Activity3都是standard模式,Activity2是singleInstance模式。
我们从Activity1中启动Activity2,然后在Activity2中启动Activity3。请先想象一下目前为止返回栈中的样子,因为Activity2采用了singleInstance模式,它新启动一个返回栈来管理自己,如果我们添加了taskID的log,就能发现Activity2的taskID是不同于Activity1和Activity3的。接着我们实验一下,在Activity3中按下back键后,居然直接从Activity3返回到了Activity1,再次按下back键又会返回Activity2,再按back键才会退出。这里面原理很简单,由于Activity1和Activity3是放在同一个返回栈中的,所以当在Activity3按下back键,Activity3就会出栈,Activity1回到栈顶,因此也就出现了直接从Activity3返回到了Activity1的情况。在Activity1中按下back键,这时返回栈已经空了,于是就显示另一个返回栈的栈顶活动,即Activity2。
三、Activity的实践
1、记录当前的Activity
业务很复杂,代码又不是自己写的时候,我们想要修改一点界面的东西会很难找当前是哪一个Activity。这时候我们可以使用一些技巧来解决这个问题。
在之前的项目基础上进行修改,创建一个BaseActivity。这里的BaseActivity和普通的Activity不一样,因为我们不需要他在Manifest中注册。所以创建一个java类,然后继承AppCompatActivity就可以。
public class BaseActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("BaseAvtivity", getClass().getSimpleName());
}
}
我们在onCreate()方法中获取了当前实例的类名,并通过log打印出来。修改其他的Activity的继承结构,让他们不再继承AppCompatActivity,而是继承自BaseActivity。由于BaseActivity又是继承自AppCompatActivity,所以项目中其他的Activity的现有功能并不受影响,它们仍然全完继承了Activity中的所有特性。
重新运行应用,这是运行到哪个Activity,该Activity的类名就会被打印出来。
2、随时随地退出应用
以前面的项目为例,当我们在Activity3的时候,想要退出应用需要连续按3下back键才能退出应用,这样显得很繁琐,我们需要一个随地随地能退出应用的方案。
这里,我们可以用一个专门的集合类对所有的Activity进行管理就可以了。
新建一个ActivityCollector作为轰动管理器:
public class ActivityCollector {
public static List activities = new ArrayList<>();
public static void addActivity(Activity activity){
activities.add(activity);
}
public static void removeActivity(Activity activity){
activities.remove(activity);
}
public static void finishAll(){
for (Activity activity : activities){
if (!activity.isFinishing()){
activity.finish();
}
}
}
}
在Activity管理器中,我们通过一个List暂存Activities,然后提供一个addActivity
()方法用于想List中添加Activity;提供一个removeActivity()方法用于从List中移除Activity;最后提供一个finishAll()方法用于将List中的所有Activity全部销毁掉。
接下来我们修改BaseActivity中的代码:
public class BaseActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("BaseAvtivity", getClass().getSimpleName());
ActivityCollector.addActivity(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
ActivityCollector.removeActivity(this);
}
}
在BaseActivity的onCreate()方法中调用了ActivityCollector的addActivity()方法,表示将当前的Activity添加到活动管理器中。然后在onDestroy()中调用ActivityCollector的removeActivity()方法,表示将一个马上要销毁的活动从活动管理器中移除。
现在,不管想要从哪个地方退出,只要调用ActivityCollector的finishAll()方法就可以了。
3、启动Activity的另一种写法
之前我们启动Activity,都是通过构建Intent,然后调用startActivity()或者startActivityForResult()来启动Activity。如果有数据要传递,也可以借助Intent完成。但是当别人需要启动我们开发的Activity时,并不清楚启动咱们的Activity需要传递哪些数据,所以要么自己看代码,要么过来问,而显然这两种方式都不太现实。其实我们只要换一种写法就可以轻松解决上面的窘境。
public class NewsContentActivity extends BaseActivity {
public static void actionStart(Context context, String newsTitile, String newsContent) {
Intent intent = new Intent(context, NewsContentActivity.class);
intent.putExtra("news Title", newsTitile);
intent.putExtra("news Content", newsContent);
context.startActivity(intent);
}
我们在NewsContentActivity中添加了一个actionStart()方法,这个方法中完成了Intent的构建,另外所有NewsContentActivity中需要的数据都是通过actionStart()方法的参数传递过来的,然后把他们存储到Intent中,最后调用startActivity()方法启动NewsContentActivity。
这样写的好处就是一目了然,NewsContentActivity所需要的数据在方法actionStart()中全部体现了出来。这样即使不了解内部的逻辑,不需要看代码,别人也能清晰的知道启动NewsContentActivity需要哪些数据。另外还简化了启动Activity的代码,现在只需要一行代码就可以启动NewsContentActivity了。
btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
NewsContentActivity.actionStart(MainActivity.this, "news Title", "news Conten");
}
});
写代码需要养成良好的习惯。。。Activity本身的初探就先告一段落,不管是理论型还是实践型我们都已经涉及了。从Activity的基本用法,到启动Activity和传递数据的方式,再到Activity的生命周期,以及Activity的启动模式。最后我们还学习了一些实战的小技巧,感觉很满足。