活动的生命周期
理解活动的生命周期可以帮助写出更加连贯流畅的程序,并更好管理资源。最终获得更好的用户体验。
返回栈
Android使用任务(Task)来管理活动的,一个任务就是一组存放在返回栈(Back Stack)里的活动的集合。每个应用程序都有自己的返回栈,大多数情况下,一个应用程序就是一个任务。(参考活动的启动模式一节。)
活动状态
- 运行状态:活动处于返回栈栈顶。
- 暂停状态:不再处于栈顶,但仍然可见。仍然是完全存活的,系统只有在内存极低的情况下才会考虑回收这种活动。
- 停止状态:不再处于栈顶,且完全不可见。系统会保存这种活动相应的状态和成员变量,但不可靠,必要时可能被回收。
- 销毁状态:返回栈中移除。系统会倾向于回收处于这种状态的活动。
活动的生存周期
Activity定义了7个回调方法,覆盖活动生命周期的每一个环节。
onCreate()
:活动第一次被创建时调用。要在此方法中完成活动的初始化操作,比如加载布局、绑定事件。onStart()
:在活动由不可见变为可见时调用。onResume()
:在活动准备好和用户进行交互时候调用。此时活动一定处于栈顶并处于运行状态。《《《《Resume是重返重新开始的意思》》》》onPause()
:系统准备启动或者恢复另一个活动的时候调用。会将一些消耗CPU的资源释放掉,并保存一些关键数据。此方法执行速度一定要快,不然会影响新的栈顶活动的使用。onStop()
:在活动完全不可见时候调用。
以上两种方法主要区别在于如果启动的新活动是一个对话框式的活动,则onPause()
方法会得到执行,而onStop()
方法并不会执行。onDestroy()
:活动被销毁前调用。之后活动变为销毁状态。onRestart()
:活动由停止状态变为运行状态之前调用,也就是活动被重启了。
除了Restart,都是两两相对,可以将活动分为三种生存期:
- 完整生存期:活动在
onCreate()
(完成初始化)和onDestroy()
(释放内存)方法之间所经历的就是完整生存期。 - 可见生存期:活动在
onStart()
和onStop()
方法之间。此时活动对于用户总是可见的,即便有可能无法和用户进行交互。
可以通过这两个方法,合理地管理那些对用户可见的资源。比如在onStart()
方法中对资源进行加载,在onStop()
方法中对资源进行释放,从而保证处于停止状态的活动不会占用过多内存。 - 前台生存期:活动在
onResume()
和onPause()
方法之间。
前台生存期内,活动总是处于运行状态的,此时的活动是可以和用户进行交互的。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="This is a normal activity"
/>
</LinearLayout>
编辑dialog_layout.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="This is a dialog activity"
/>
</LinearLayout>
编辑AndroidManifest.xml文件,修改<activity>
标签的配置:
<activity android:name=".DialogActivity"
android:theme="@android:style/Theme.Dialog"></activity>
修改activity_main.xml,重新定制主活动布局。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/start_normal_activity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Start NormalActivity"/>
<Button
android:id="@+id/start_dialog_activity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Start DialogActivity"/>
</LinearLayout>
修改MainActivity中的代码:
public class MainActivity extends AppCompatActivity {
public static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button startNormalActivity = (Button) findViewById(R.id.start_normal_activity);
Button startDialogActivity = (Button) findViewById(R.id.start_dialog_activity);
startNormalActivity.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, NormalActivity.class);
startActivity(intent);
}
});
startDialogActivity.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, DialogActivity.class);
startActivity(intent);
}
});
}
@Override
protected void onStart() {
super.onStart();
Log.d(TAG, "onStart");
}
@Override
protected void onPostResume() {
super.onPostResume();
Log.d(TAG,"onResume");
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG,"onPause");
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG,"onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG,"onDestroy");
}
@Override
protected void onRestart() {
super.onRestart();
Log.d(TAG,"onRestart");
}
}
《《《《《哭了呀为啥出bug了明明都一样》》》》》
活动被回收了怎么办
当一个活动进入停止状态,有可能会被回收。一旦回收,当Back键返回时,会执行onCreate()
方法而不会执行onRestart()
方法。也就是原有的数据都不会保存。
解决:onSaveInstanceState()
方法会携带一个Bundle类型的参数,Bundle提供方法保存数据。
putString()
保存字符串;putInt()
保存整型数据;以此类推。
每个保存方法传入两个参数:一是键,用于后面从Bundle取值;二是要保存的内容。
数据的恢复:onCreate()
方法含有Bundle理性参数,一般情况下是null。
if (savedInstanceState != null) {
String tempData = savedInstanceState.getString("data_key");
Log.d(TAG, tempData);
}
这样加上if判断可以保证参数包含Bundle时能够将数据读出。
另,Intent可以结合Bundle一起用于传递数据——首先将数据保存在Bundle对象中,再将Bundle对象存放在Intent里;到目标活动后先从Intent去除Bundle,再从Bundle中一一取出数据。
活动的启动模式
实际项目中应当根据特定的需求为每个活动指定恰当的启动模式。
可以在AndroidManifest.xml中通过给<activity>
标签指定Android:launchMode
属性来选择启动模式。
四种启动模式
启动模式分为standard、singleTop、singleTask、singleInstance四种。
standard
活动默认的启动模式,没有显示指定时都会自动使用这种启动模式。
standard启动模式下,每当启动一个新的活动,不管活动是否已经存在于返回栈中,都会创建活动的一个新的实例,将其在返回栈中入栈,并使其处于栈顶位置。
singleTop
启动活动时,如发现返回栈栈顶已经是该活动,则不会再创建新的活动实例。
singleTask
保证活动在整个应用程序的上下文中只存在一个实例。
当活动启动模式指定为singleTask时,每次启动该活动,系统首先会在返回栈中检查是否存在该活动实例,如果发现已经存在则直接使用该实例,并把在这个活动之上的所有活动统统出栈,如果没有发现就会创建一个新的活动。
singleInstance(复杂)
指定为singleInstance模式的活动会启用一个新的返回栈来管理这个活动。
应用场景:程序中有一个活动允许其他程序调用的。(每个应用程序都会有自己的返回栈,同一个活动在不同的返回栈中入栈时必然是创建了新的实例。)在singleInstance模式下会有一个单独的返回栈来管理这个活动,不管哪一个应用程序来访问这个活动,都共用同一个返回栈,也就解决了共享活动实例的问题。
实践技巧
知晓当前是哪一个活动
getClass().getSimpleName()
获取当前实例的类名。
例子:
在ActivityTest项目的基础上修改,新建一个BaseActivity类。重写类如下:
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("BaseActivity", getClass().getSimpleName());
}
}
而后让Activity继承BaseActivity类而不再继承AppCompatActivity类。此时进入任何活动都会在日志中打印当前活动名称。
随时随地退出程序
当连续进入三个活动时,往往需要按三次Back键才能退出程序。需要一个注销或者退出的功能。
解决:用一个专门的集合类对所有的活动进行管理。
尝试一下:
新建一个ActivityCollector类作为活动管理器:
public class ActivityCollector {
public static List<Activity> 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();
}
}
}
}
在BaseActivity中加以运用:
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("BaseActivity", getClass().getSimpleName());
ActivityCollector.addActivity(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
ActivityCollector.removeActivity(this);
}
}
所以在任何地方退出程序只需要调用ActivityCollector.finishAll()
方法就可以了。
也可以通过android.os.Process.killProcess(android.os.Process.myPid())
方法杀掉当前进程。
killProcess()
方法用于杀掉一个进程,接收一个进程id参数。只能杀掉当前程序的进程。myPid()
方法可以获得当前程序的进程id。
启动活动的最佳写法
以上所学到的,使用Intent构建意图然后调用atartActivity()
或者startActivityForResult()
方法将活动启动起来,但在开发项目中此种方式会导致很多的对接工作。最佳写法:将所要传递的内容以及启动活动方法的调用都包装在一个函数中,将要传递的数据以及上下文作为参数传入其中。
例:
public static void actionStart(Context context,String data1, String data2){
Intent intent = new Intent(context, SecondActivity.class);
intent.putExtra("param1",data1);
intent.putExtra("param2",data2);
context.startActivity(intent);
}
到时只需要一行代码就可以启动SecondActivity:
SecondActivity.actionStart(FirstActivity.this,"data1", "data2");