Android开发艺术探索学习笔记(一)
android,开发艺术,学习笔记2016-08-02
学习章节:第一章 Activity的生命周期和启动模式
学习时间:2016.8.2
学习内容:
1.1正常情况下Activity的生命周期分析
测试代码:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.e("tag", "生命周期:onCreate");
}
@Override
protected void onStart() {
super.onStart();
Log.e("tag", "生命周期:onStart");
}
@Override
protected void onResume() {
super.onResume();
Log.e("tag", "生命周期:onResume");
}
@Override
protected void onRestart() {
super.onRestart();
Log.e("tag", "生命周期:onRestart");
}
@Override
protected void onPause() {
super.onPause();
Log.e("tag", "生命周期:onPause");
}
@Override
protected void onStop() {
super.onStop();
Log.e("tag", "生命周期:onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.e("tag", "生命周期:onDestroy");
}
}
测试结果:
第一次启动Activity,生命周期回调如下:
这时候将应用切换到后台或打开新的Activity,生命周期回调如下:
然后再次打开这个应用,生命周期回调如下:
最后按返回键退出这个应用,生命周期回调如下:
假如应用已经被切换到后台,这时直接结束所有进程,生命周期回调如下:
从整个生命周期来说,onCreate和onDestroy是配对的,分别标识着Activity的创建和销毁,并且只可能有一次被调用。从Activity是否可见来说,onStart和onStop是配对的,随着用户的操作或者设备屏幕的点亮和熄灭,这两个方法可能被调用多次;从Activity是否在前台来说,onResume和onPause是配对的,随着用户的操作或者设备屏幕的点亮和熄灭,这两个方法可能被调用多次。
假设当前Activity为A,如果这时用户打开一个新ActivityB,那么B的onResume和A的onPause哪个先执行???
测试代码:
private void initView() {
btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivity(new Intent(MainActivity.this, SecondActivity.class));
}
});
}
测试结果:
可以看到,新启动一个Activity的时候,旧Activity的onPause会先执行,然后才会启动新的Activity。通过分析这个问题,我们知道不能在onPause中做重量级的操作,因为必须onPause执行完成以后新Activity才能Resume。
1.2异常情况下Activity的生命周期分析
1.2.1资源相关的系统配置发生改变导致Activity的生命周期的改变
最常见的一种情况就是屏幕旋转导致的Activity的生命周期的改变,网上关于Android横竖屏切换生命周期的资料一大把,照着这些资料高高兴兴的敲着代码,尼玛却发现坑爹呀!!!
代码与结果才是最有说服力,开始举栗子:
Activity的配置文件不设置任何参数,从竖屏切换到横屏:
调用了一次完整的生命周期
Activity的配置文件不设置任何参数,从横屏切换到竖屏:
调用了一次完整的生命周期,并没有调用两次!!!
Activity的配置文件设置:android:configChanges=”orientation”,从竖屏切换到横屏或者从横屏切换到竖屏:
调用了一次完整的生命周期,也就是说,设置这个参数没有任何作用!!!
Activity的配置文件设置:android:configChanges=”orientation|keyboardHidden,从竖屏切换到横屏或者从横屏切换到竖屏:
调用了一次完整的生命周期,也就是说,额外设置这个参数没有任何作用,这不坑爹嘛!说好的不调用生命周期呢???
后来才知道,当android:targetSdkVersion<=12,不会调用完整生命周期;当android:targetSdkVersion>12,会调用完整生命周期。我的 targetSdkVersion是23,嗦嘎寺内。
那么我该怎样设置才能在targetSdkVersion大于12的时候,横竖屏切换不调用生命周期呢:
android:configChanges="orientation|keyboardHidden|screenSize"
这样就够了,看看打印结果,没有打印与生命周期相关的log,只是调用了系统的onConfigurationChanged方法,这时候我们可以自己做一些处理,完美。
当然,你要是这样设置
android:screenOrientation="portrait"
这就另当别论,只允许竖屏,管你怎么切换都没用,就不谈生命周期的调用了。
OK,我们已经知道这种系统配置发生变化的情况会调用Activity的完整生命周期。这种Activity被异常中止的情况下,系统会调用onSaveInstanceState来保存当前Activity的状态,正常情况下系统不会回调这个方法。举个栗子:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.e("tag", "A生命周期:onCreate");
initView();
if (savedInstanceState != null) {
Log.e("tag", "savedInstanceState保存的值为:" + savedInstanceState.get("data"));
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Log.e("tag", "onSaveInstanceState");
outState.putString("data", "我是需要保存的值");
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Log.e("tag", "savedInstanceState保存的值为:" + savedInstanceState.get("data"));
}
还是这段代码,当我第一次正常启动时,没有什么不同:
当我切换到横屏时,高潮来了:
果然和预期的一样,这种异常情况调用了onSaveInstanceState方法,比如我们需要在这种异常情况下保存一些必要的参数,像播放视频时横竖屏切换,我一定得保存当前的播放进度等信息。那么我们可以把这些参数以键值对的形式保存在onSaveInstanceState方法中。怎样恢复这些数据呢,恢复数据的位置有两种:onRestoreInstanceState和onCreate,区别如下:
onRestoreInstanceState一旦被调用,其参数Bundle savedInstanceState一定是有值的,我们不用额外判断是否为空
onCreate如果正常启动的话,其参数Bundle savedInstanceState为空,我们需要额外判断是否为空
1.2.2资源内存不足导致优先级的Activity被杀死
Activity按照优先级从高到低,可以分为以下三种:
(1)前台Activity:正在和用户交互的Activity,优先级最高;
(2)可见但非前台Activity:比如Activity中弹出了一个对话框,导致Activity可见但是位于后台无法和用户直接交互;
(3)后台Activity:已经被暂停的Activity,比如执行了onStop,优先级最低
系统会按照上述优先级去杀死目标Activity所在的进程,并通过onSaveInstanceState与onRestoreInstanceState方法来存储和恢复数据。一些后台工作不适合脱离四大组件而独自运行在后台中,这样很容易被杀死。比较好的做法是将后台工作放入Service中从而保证进程有一定的优先级,这样不会轻易地被系统杀死。
关于Fragment的生命周期:
刚好之前写过一篇封装BaseActivity与BaseFragment的文章:
从BaseActivity与BaseFragment的封装谈起
我这次加入了生命周期的监听,看看是什么情况:
测试情况,初始化第一个Fragment:
然后将当前Fragment进行remove操作,并加入回退栈:
返回到第一个Fragment:
退出当前Fragment: