Android-Activity横竖屏切换时的生命周期
相信大家对活动的生命周期十分了解了,它的7个生命周期方法onCreate(),onStart(),onResume(),等在什么时候调用都很熟悉。那么,当一个活动运行时锁屏,返回主界面,旋转屏幕时方法会怎么调用,数据是如何保存的,我分享一下我所学的。
新的方法
onSaveInstanceState():当activity变得容易被系统回收时调用
onRestoreInstanceState():被系统回收的activity重新创建时调用
onConfigurationChanged():当系统的配置信息发生改变时,系统会调用此方法
- onSaveInstanceState
onSaveInstanceState()在onStop()方法后执行
(注:根据官方文档,在Android API26以下,onSaveInstanceState()优先于onStop()方法执行,API26以后会先执行onStop()再执行onSaveInstanceState())
首先给大家带来三个新方法的学习,onSaveInstanState()我们已经有过接触,在《第一行代码》P63,2.4.5 活动被回收了怎么办 这一节中提到,为了避免活动被回收时数据无法及时保存的问题,我们可以使用onSaveInstanceState(Bundle outState)方法的outState参数保存临时数据,并在onCreate()方法中取出
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
String tempData = "Something you just typed";
outState.putString("data_key", tempData);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState != null){
//注意要做非空判断
String tempData = savedInstanceState.getString("data_ley);
Log.d(TAG, tempData);
}
}
另外,onSaveInstanceState()方法要注意的是,它只有在activity变得容易被系统销毁前会调用,而用户主动销毁,如按返回键退出程序是不会调用的。
什么是“容易”,有一下几种情况:
- 按HOME键返回主屏幕
- 按菜单键切换程序
- 锁屏时
- 启动新活动时
- 屏幕方向切换
总而言之,onSaveInstanceState()的调用遵循一个原则,当系统存在未经你许可销毁活动的可能时,则onSaveInstanceState()会被系统调用。
- onRestoreInstanceState()
onRestoreInstanceState()在onStart()方法后执行
onRestoreInstanceState()onSaveInstanceState()相辅相成,与从名字来看,这个方法就是用来回复数据的。这个方法会在activity确实被系统回收,并且要重新创建activity时调用。
与在onCreate()方法我们手动回复数据不同的是,onRestoreInstanceState()会在重新创建已经被系统回收的activity时自动调用,并且我们也不需要在进行非空判断,因为调用了此方法就一定会有Bundl对象。
- onConfigurationChanged()
因为活动的创建十分耗费资源,而横竖屏切换系统又必定销毁再重新创建,有什么方法可以跳过这些步骤呢?我们可以使用onConfigurationChanged()方法。
首先我们需要再Manifest文件中为activity加上configChanges属性
<activity android:name=".ActivityTwo"
android:configChanges="orientation|keyboardHidden|screenSize">
</activity>
configChanges属性所声明的内容就意味着activity可以处理的配置改变,即当这些内容发生变化时,activity不会重新启动,而是调用onConfigurationChanged()方法
调用顺序
代码
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
String s = "Activity1";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "onCreate: " + s);
Button startActivity = findViewById(R.id.start_activity);
startActivity.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, ActivityTwo.class);
startActivity(intent);
}
});
Button alert1 = findViewById(R.id.alert1);
alert1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, ActivityThree.class);
startActivity(intent);
}
});
Button startService = findViewById(R.id.start_service);
startService.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, MyService.class);
startService(intent);
}
});
Button stopService = findViewById(R.id.stop_service);
stopService.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, MyService.class);
stopService(intent);
}
});
}
@Override
protected void onStart() {
super.onStart();
Log.d(TAG, "onStart: " + s);
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume: " + s);
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "onPause: " + s);
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG, "onStop: " + s);
}
@Override
protected void onRestart() {
super.onRestart();
Log.d(TAG, "onRestart: " + s);
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: " + s);
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
Log.d(TAG, "onSaveInstanceState: " + s);
}
@Override
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Log.d(TAG, "onRestoreInstanceState: " + s);
}
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
Log.d(TAG, "onConfigurationChanged: " + s);
}
}
- 锁屏
D/MainActivity: onPause: Activity1
D/MainActivity: onStop: Activity1
D/MainActivity: onSaveInstanceState: Activity1
- 按下HOME键
D/MainActivity: onPause: Activity1
D/MainActivity: onStop: Activity1
D/MainActivity: onSaveInstanceState: Activity1
- 按下菜单键
D/MainActivity: onPause: Activity1
D/MainActivity: onStop: Activity1
D/MainActivity: onSaveInstanceState: Activity1
这三种情况可以看到,活动进行到onStop()再调用onSaveInstanceState()保存数据就结束了
-屏幕旋转时
D/MainActivity: onPause: Activity1
D/MainActivity: onStop: Activity1
D/MainActivity: onSaveInstanceState: Activity1
D/MainActivity:onDestroy: Activity1
D/MainActivity: onCreate: Activity1
D/MainActivity: onStart: Activity1
D/MainActivity: onRestoreInstanceState: Activity1
D/MainActivity: onResume: Activity1
这里我们终于见到了onRestoreInstanceState()方法,因为此时旧活动确实被系统回收了,系统又帮我们重新创建了活动
- 带configChanges属性的活动的横竖屏切换
D/ActivityTwo: onConfigurationChanged: Activity2
可以看到,当我们设置好了configChanges属性后,切换屏幕只会执行onConfigurationChanged()方法
- 应用分屏
没有configChanges属性
D/MainActivity: onPause: Activity1
D/MainActivity: onStop: Activity1
D/MainActivity: onSaveInstanceState: Activity1
D/MainActivity: onDestroy: Activity1
D/MainActivity: onCreate: Activity1
D/MainActivity: onStart: Activity1
D/MainActivity: onRestoreInstanceState: Activity1
D/MainActivity: onResume: Activity1
D/MainActivity: onPause: Activity1
有configChanges属性
D/ActivityTwo: onConfigurationChanged: Activity2
D/ActivityTwo: onPause: Activity2
总结:应用分屏总是会执行onPause()方法,不同的是,没有configChanges属性会先销毁再新建,有configChanges属性先执行onConfigurationChanged()方法再执行onPause()方法。
- 启动新活动及退出
启动新活动
D/MainActivity: onPause: Activity1
D/ActivityTwo: onCreate: Activity2
D/ActivityTwo: onStart: Activity2
D/ActivityTwo: onResume: Activity2
D/MainActivity: onStop: Activity1
D/MainActivity: onSaveInstanceState: Activity1
退出活动
D/ActivityTwo: onPause: Activity2
D/MainActivity: onRestart: Activity1
D/MainActivity: onStart: Activity1
D/MainActivity: onResume: Activity1
D/ActivityTwo: onStop: Activity2
D/ActivityTwo: onDestroy: Activity2
这个有了很大的变化,在启动新活动时,先执行活动1的onPause()方法,然后开始创建活动2,当活动2准备好后,也就是onResume()状态才会接着执行活动1的onStop(),onSaveInstanceState()方法。退出时也一样,先执行活动2的onPause()方法再回复活动1,当活动1完全恢复时才调用活动2的onStop(),onDestroy()方法。
数据保存
一般来说,我们可以在生命周期的onPause(),或者在onSaveInstanceState()方法中保存数据,他们有什么区别,我要怎么使用呢?
- 为什么要在onPause()中保存数据,而不是在onStop()中?
以上面启动新活动为例,我们可以看到,在启动新活动时,我们先执行了活动1的onPause()方法才创建活动2,因此我们能保证onPause()一定被执行,onStop()方法并不保证一定能执行。同时,因为onPause()方法结束才会创建下一个活动,我们不能在onPause()有耗时操作,否则会影响下一个活动入栈。
- 他们分别可以保存什么数据
onSaveInstanceState()用来保存一些临时数据,同时,他的默认实现会自动保存活动的某些数据,比如EditText会自动保存和恢复输入的数据。
onPause()方法可以进行一些数据持久化操作,将数据保存在数据库中。但是,因为数据库操作是耗时操作,数据量过多会ANR异常,我们只能保存少量数据,对于大量数据,我们就需要开启新线程来保存了