本章讲述 Activity 生命周期、状态以及状态切换时系统调用的方法。
activity状态图解
activity的状态( 某些场景下,暂停状态的activity可能会部分或完全可见)
「Activity 类会提供许多回调,这些回调会让 Activity 知晓某个状态已经更改。」
通常,通过覆盖 onCreate(Bundle) 方法,activity 可以预处理以下 UI 相关工作:
介绍的 android.util.Log 类打印日志,在上一章 MainActivity.kt 的上方加上日志 TAG 定义,然后,在 onCreate()、onStart()、onResume()、onPause()、onStop()、onDestroy() 生命周期回调方法中分别打印日志。
private const val TAG = "MainActivity"
class MainActivity : AppCompatActivity() {
...
override fun onStart() {
super.onStart()
Log.d(TAG, "onStart() called")
}
...
}
启动 app 日志:
启动
点击 Home 键日志:
home
重新进入 app 日志:
resume
旋转 app 日志:
旋转
退出 app 日志:
退出
旋转设备会改变设备配置(device configuration)。设备配置实际是指屏幕方向、屏幕像素密度、屏幕尺寸、键盘类型、语言等。
在运行时配置变更(runtime configuration change)发生时,可能会有更合适的资源来匹配新的设备配置。于是,Android销毁当前activity,为新配置寻找最佳资源,然后创建新实例使用这些资源。(在demo中,再创建了一个layout目录,并加了后缀-land,res/layout-land,于是结果是设备处于水平方向时,Android会找到并使用res/layout-land目录下的布局资源)
Android的配置修饰符列表及其代表的设备配置信息网址:http://developer.android.com/guide/topics/resources/providing-resources.html
Android 7.0 之前,通常使用 onResume() 和 onPause() 来启动或者停止任何与 UI 相关的正在进行的更新(动画和刷新数据)。Android 7.0 之后,有了多窗口模式,已经暂停的 activity 也是可见的状态,我们是希望已经暂停的 activitiy 也表现的像正常活动一样。比如说看视频的时候,不过我们可以在将恢复播放和暂停的播放移至 onStart() 和 onStop() 中,这样就能满足需求了。
protected void onSaveInstanceState(Bundle outState)【该方法通常在 onStop() 方法之前由系统调用,除非用户按后退键。(记住,按后退键就是告诉 Android,activity 用完了。随后,该 activity 就完全从内存中被抹掉,自然,也就没有必要为重建保存数据了。)】【 Bundle 是存储字符串键与限定类型值之间映射关系(键-值对)的一种结构】
所以,可通过覆盖 onSaveInstanceState(Bundle) 方法,将一些数据保存在 bundle 中,然后在 onCreate(Bundle) 方法中取回这些数据,解决旋转问题。
注意,在 Bundle 中存储和恢复的数据类型只能是基本类型(primitive type)以及可以实现 Serializable 或 Parcelable 接口的对象。在 Bundle 中保存定制类对象不是个好主意,因为你取回的对象可能已经没用了。比较好的做法是,通过其他方式保存定制类对象,而在Bundle中保存标识对象的基本类型数据。
完整activity生命周期
低内存状态下,Android直接从内存清除整个应用进程,连带应用的所有activity。目前,Android还做不到只销毁单个activity。
这里还介绍了使用Android手机中开发者设置,启用 Don’t keep activities
单击后退键后,系统总是会销毁当前的activity,相当于告诉系统“用户不再需要使用当前的activity”。
当然,打印日志也是有级别的,通常打错误日志才用 Log.e,默认是红色,打出来很显眼,可是平常一些信息什么的,最好不要打到这个级别了,很影响排除错误。
日志级别
关于日志打印:https://www.jianshu.com/p/de79bbf35a5b
private var mQuestionsAnswered: BooleanArray? = BooleanArray(questionBank.size)
private fun setBtnEnabled(enabled: Boolean) {
trueButton.isEnabled = enabled
falseButton.isEnabled = enabled
}
private fun checkAnswer(userAnswer: Boolean) {
...
setBtnEnabled(false)
mQuestionsAnswered?.set(currentIndex, true)
}
private fun updateQuestion() {
...
setBtnEnabled(!mQuestionsAnswered?.get(currentIndex)!!)
}
private const val KEY_QUESTION_ANSWERED = "answered"
override fun onSaveInstanceState(savedInstanceState: Bundle) {
...
savedInstanceState.putBooleanArray(KEY_QUESTION_ANSWERED, mQuestionsAnswered)
}
if (savedInstanceState != null) {
currentIndex = savedInstanceState.getInt(KEY_INDEX, 0)
mQuestionsAnswered = savedInstanceState.getBooleanArray(KEY_QUESTION_ANSWERED)
}
private var mTrueAnswerCount = 0
private fun checkAnswer(userAnswer: Boolean) {
val correctAnswer = questionBank[currentIndex].answer
val messageResId = if (userAnswer == correctAnswer) {
mTrueAnswerCount++
R.string.correct_toast
} else {
R.string.incorrect_toast
}
Toast.makeText(this, messageResId, Toast.LENGTH_SHORT).show()
setBtnEnabled(false)
mQuestionsAnswered?.set(currentIndex, true)
getScoreResult()
}
private fun getScoreResult() {
var isAllAnswered = true
for (i in questionBank.indices) {
if (!mQuestionsAnswered?.get(i)!!) {
isAllAnswered = false
return
}
}
if (isAllAnswered) {
Toast.makeText(
this,
"${mTrueAnswerCount * 100 / questionBank.size} %",
Toast.LENGTH_LONG
).show()
}
}
private const val KEY_TRUE_ANSWER_COUNT = "true_answer_count"
mTrueAnswerCount = savedInstanceState.getInt(KEY_TRUE_ANSWER_COUNT)
savedInstanceState.putInt(KEY_TRUE_ANSWER_COUNT, mTrueAnswerCount)
OK!完毕!ヾ(◍°∇°◍)ノ゙