Android 学习之《Android编程权威指南》第二版 代码+笔记整理(三)
(代码)解决GeoQuiz应用旋转恢复第一题的BUG
GeoQuiz应用初步开发
GeoQuiz应用升级开发
一、产生BUG的原因
1. 设备旋转时,系统会销毁当前的QuizActivity实例,然后创建一个新的实例,这时数组索引(mCurrentIndex)会初始化为0,因此用户看到的还是第一道题目。
2. 旋转设备会改变设备配置。设备配置是用来描述设备当前状态的一系列特征:屏幕方向、密度、尺寸、键盘类型、底座模式以及语言等。在应用运行中,只要设备配置发生改变,Android就会销毁当前activity,然后创建新的activity。
二、解决方法
采用某种方式保存以前的数据:(实现方式之一)
1. 重写protected void onSaveInstanceState(Bundle outState)
2. 该方法通常在onPause()、onStop()以及onDestroy()方法之前由系统调用。
3. Bundle是存储字符串键与限定类型值直接映射关系(键值对)的一种结构。
4. 在Bundle中存储和恢复的数据类型只能是基本数据类型以及可以实现Serializable或Parcelable接口的对象。
5. 在Bundle中保存定制类对象不是一个好主意,因为你所取回的对象可能已经过时了,比较好的做法是:通过其它方式保存定制类对象,而在Bundle中保存对象对应的基本数据类型的标示符。
三、修改QuizActivity.java
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
public class QuizActivity extends AppCompatActivity {
private Button mTrueButton; //true选项按钮
private Button mFalseButton; //false选项按钮
private Button mNextButton; //next选项按钮
private TextView mQuestionTextView; //textView文本显示
private Question[] mQuestionBank = new Question[]{ //Question对象数组
new Question(R.string.question_oceans,true),
new Question(R.string.question_mideast,false),
new Question(R.string.question_africa,false),
new Question(R.string.question_americas,true),
new Question(R.string.question_asia,true),
};
//KEY_INDEX常量作为将要存储在Bundle中的数组索引变量的键值对的键
private static final String KEY_INDEX = "index";
private int mCurrentIndex = 0; //数组索引变量
private void updateQuestion(){ //更新问题文本内容函数
int question = mQuestionBank[mCurrentIndex].getTextResId(); //获取资源ID
mQuestionTextView.setText(question); //设置文本内容
}
private void checkAnswer(boolean userPressedTrue){ //检查问题答案函数
boolean answerIsTrue = mQuestionBank[mCurrentIndex].isAnswerTrue(); //获取对应问题的答案
int messageResId = 0;
//根据答案正确与否分配资源ID
if(userPressedTrue == answerIsTrue){
messageResId = R.string.correct_toast;
}else{
messageResId = R.string.incorrect_toast;
}
Toast.makeText(this,messageResId,Toast.LENGTH_SHORT).show();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_quiz); //加载布局
mQuestionTextView = (TextView) findViewById(R.id.question_text_view); //获取TextView对象
mTrueButton = (Button) findViewById(R.id.true_button); //获取trueButton按钮对象
mTrueButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
checkAnswer(true);
}
});
mFalseButton = (Button) findViewById(R.id.false_button); //获取falseButton按钮对象
mFalseButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
checkAnswer(false);
}
});
mNextButton = (Button) findViewById(R.id.next_button); //获取NextButton按钮对象
mNextButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mCurrentIndex = (mCurrentIndex+1) % mQuestionBank.length; //索引值增加1
updateQuestion();
}
});
//检查是否有存储的键值对,有则拿出来赋值。
if(savedInstanceState != null){
mCurrentIndex = savedInstanceState.getInt(KEY_INDEX,0);
}
updateQuestion(); //更新问题
}
//重写onSaveInstanceState()函数,将要数组索引值存入Bundle当中
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(KEY_INDEX,mCurrentIndex);
}
}
(代码)探究Activity生命周期
不展示编译器自动完成的代码,仅提供手动修改或者编写的代码。
QuizActivity .java(主活动)
通过LogCat查看日志消息
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
public class QuizActivity extends AppCompatActivity {
private static final String TAG = "QuizActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG,"onCreate(Bundle) called");
setContentView(R.layout.activity_quiz);
}
@Override
protected void onStart() {
super.onStart();
Log.d(TAG,"onStart() called");
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG,"onPause() called");
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG,"onResume() called");
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG,"onStop() called");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG,"onDestroy() called");
}
}
(笔记)第3章 Activity的生命周期
1. 每个Activity实例都有生命周期,在它的生命周期内,activity在 运行、暂停和停止 三种可能的状态间进行转换,当转换发生时,都会有对应的方法将状态改变的消息通知给activity。
2. 千万不要自己去调用Activity的任何生命周期方法。
3. Log类有好几个日志记录方法,其中最为常用的是 public static int d(String tag,String msg) 其中d代表debug的意思,是日志信息的级别,第一个参数为日志来源,第二个参数为日志具体内容。第一个参数通常以类名为值的 TAG常量 传入。
日志记录的级别与方法
Log Level | Method | 说明 |
---|---|---|
ERROR | Log.e(…) | 错误 |
WARNING | Log.w(…) | 警告 |
INFO | Log.i(…) | 信息型消息 |
DEBUG | Log.d(…) | 调试输出:可能被过滤掉 |
VERBOSE | Log.v(…) | 只用于开发 |
4. 暂停了的activity能够存在多久,无法确定。当系统需要回收内存时,它将首先销毁那些停止的activity。
来自一名刚刚开始学习Android的小菜鸟~