009 Android programming 3rd Activity生命周期以及debug处理

本文详细探讨了Android应用中Activity的生命周期,包括onCreate、onStart、onResume、onPause、onStop和onDestroy等关键回调函数的使用场景。在屏幕旋转时,Activity会重新创建,可能导致数据丢失。为解决此问题,文章建议重写onSaveInstanceState方法保存状态。此外,文章还介绍了如何处理按Home键后Activity的状态,以及从最近任务列表重新打开应用时的回调。在调试方面,提到了查看Logcat、使用Layout Inspector和Allocation Tracker等技巧来定位和解决问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第三章 Activity生命周期

在旋转屏幕的时候,每次都会跳到第一个问题中。

生命周期

其中onCreate具体应该做哪些工作

Typically, an activity overrides onCreate(Bundle) to prepare the
specifics of its UI:

1. inflating widgets and putting them on screen (in the call to (setContentView(int))

2. getting references to inflated widgets

3. setting listeners on widgets to handle user interaction

4. connecting to external model data


package com.audio.android.geoquiz;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class QuizActivity extends AppCompatActivity {

    private static final String TAG = "QuizActivity";

    private Button mTrueButton;
    private Button mFalseButton;
    private Button mPrevButton;
    private Button mNextButton;
    private TextView mQuestionTextView;

    private Question[] mQuestionBank = new Question[] {
            new Question(R.string.question_australia, true),
            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),
    };

    private int mCurrentIndex = 0;

    //Toast correctToast = Toast.makeText(QuizActivity.this, R.string.correct_toast, Toast.LENGTH_SHORT);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate (Bundle) called");
        setContentView(R.layout.activity_quiz);

        mQuestionTextView = (TextView) findViewById(R.id.question_text_view);
        updateQuestion();

        mQuestionTextView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mCurrentIndex = (mCurrentIndex + 1) % mQuestionBank.length;
                updateQuestion();
            }
        });

        mTrueButton = (Button) findViewById(R.id.true_button);
        mTrueButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                checkAnswer(true);
            }
        });

        mFalseButton = (Button) findViewById(R.id.false_button);
        mFalseButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                checkAnswer(false);
            }
        });

        mPrevButton = (Button) findViewById(R.id.prev_button);
        mPrevButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mCurrentIndex = mCurrentIndex - 1 < 0 ? (mCurrentIndex - 1 + mQuestionBank.length) : mCurrentIndex - 1;
                mCurrentIndex = mCurrentIndex % mQuestionBank.length;
                updateQuestion();
            }
        });

        mNextButton = (Button) findViewById(R.id.next_button);
        mNextButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mCurrentIndex = (mCurrentIndex + 1) % mQuestionBank.length;
                updateQuestion();
            }
        });
    }
    private void updateQuestion() {
        int question = mQuestionBank[mCurrentIndex].getTextResId();
        mQuestionTextView.setText(question);
    }

    private void checkAnswer(boolean userPressedTrue) {
        boolean answerIsTrue = mQuestionBank[mCurrentIndex].isAnswerTrue();

        int messageResId = 0;
        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 onStart() {
        super.onStart();
        Log.d(TAG, "onStart() called");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume() called");
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "onPause() called");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG, "onStop() called");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy() called");
    }
}

点击开启,和返回键时的调用顺序。

06-25 15:05:25.638 30412-30412/com.audio.android.geoquiz D/QuizActivity: onCreate (Bundle) called
06-25 15:05:25.758 30412-30412/com.audio.android.geoquiz D/QuizActivity: onStart() called
06-25 15:05:25.761 30412-30412/com.audio.android.geoquiz D/QuizActivity: onResume() called

06-25 15:05:35.420 30412-30412/com.audio.android.geoquiz D/QuizActivity: onPause() called
06-25 15:05:35.831 30412-30412/com.audio.android.geoquiz D/QuizActivity: onStop() called
06-25 15:05:35.831 30412-30412/com.audio.android.geoquiz D/QuizActivity: onDestroy() called

可以很清楚地看到函数的调用情况。

开始回调之前先要调用父类的函数

These superclass calls are required. Calling the superclass implementation should be the first line of each callback method override implementation.

按home键有的效果

This means, after pressing Home, your instance of QuizActivity hangs out in the stopped state (in memory, not visible, and not active in the foreground).

最近使用的菜单中打开程序

A quick look at Logcat shows that your activity got calls to onStart() and onResume().

设备配置

Rotating the device changes the device configuration. The device configuration is a set of characteristics that describe the current state of an individual device. The characteristics that make up the configuration include screen orientation screen density, screen size, keyboard type, dock mode, language, and more.


创建一个横幅的画面

FrameLayout介绍

FrameLayout is the simplest ViewGroup and does not arrange its children in any particular manner. In this layout, child views will be arranged according to their

android:layout_gravity attributes.

横竖屏时数据会丢失,需要重写onSaveInstanceState(Bundle outState)方法。

Note that the types that you can save to and restore from a Bundle are primitive types and classes that implement the Serializable or Parcelable interface.

两个挑战,一个是enable/disable相应的按钮,完成。

打印得分,编写完成

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id = "@+id/question_text_view"
        android:layout_width = "wrap_content"
        android:layout_height = "wrap_content"
        android:layout_gravity = "center_horizontal"
        android:padding = "24dp" />

    <LinearLayout
        android:layout_width = "wrap_content"
        android:layout_height = "wrap_content"
        android:layout_gravity = "center_vertical|center_horizontal"
        android:padding = "20dp"
        android:orientation = "horizontal" >

        <Button
            android:id = "@+id/true_button"
            android:layout_width = "wrap_content"
            android:layout_height = "wrap_content"
            android:text = "@string/true_button" />

        <Button
            android:id = "@+id/false_button"
            android:layout_width = "wrap_content"
            android:layout_height = "wrap_content"
            android:text = "@string/false_button"/>

    </LinearLayout>

    <LinearLayout
        android:layout_width = "wrap_content"
        android:layout_height = "wrap_content"
        android:layout_gravity = "bottom|right"
        android:orientation = "horizontal" >
        <Button
            android:id="@+id/prev_button"
            android:layout_width = "wrap_content"
            android:layout_height = "wrap_content"
            android:text = "@string/prev_button"
            android:drawableLeft = "@drawable/arrow_left"
            android:drawablePadding = "4dp"/>

        <Button
            android:id="@+id/next_button"
            android:layout_width = "wrap_content"
            android:layout_height = "wrap_content"
            android:text = "@string/next_button"
            android:drawableRight = "@drawable/arrow_right"
            android:drawablePadding = "4dp"/>

        </LinearLayout>

    <LinearLayout
        android:layout_width = "wrap_content"
        android:layout_height = "wrap_content"
        android:orientation = "horizontal" >

        <ImageButton
            android:id="@+id/prev_img_button"
            android:layout_width = "wrap_content"
            android:layout_height = "wrap_content"
            android:src = "@drawable/arrow_left"
            android:contentDescription="@string/previous_question"/>

        <ImageButton
            android:id="@+id/next_img_button"
            android:layout_width = "wrap_content"
            android:layout_height = "wrap_content"
            android:src = "@drawable/arrow_right"
            android:contentDescription="@string/next_question"/>

    </LinearLayout>

</FrameLayout>

package com.audio.android.geoquiz;

import android.os.PersistableBundle;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class QuizActivity extends AppCompatActivity {

    private static final String TAG = "QuizActivity";
    private static final String KEY_INDEX = "index";

    private Button mTrueButton;
    private Button mFalseButton;
    private Button mPrevButton;
    private Button mNextButton;
    private TextView mQuestionTextView;

    private Question[] mQuestionBank = new Question[] {
            new Question(R.string.question_australia, true),
            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),
    };

    private int mCurrentIndex = 0;

    private int isRight = 0;
    private int isWrong = 0;

    //Toast correctToast = Toast.makeText(QuizActivity.this, R.string.correct_toast, Toast.LENGTH_SHORT);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate (Bundle) called");
        setContentView(R.layout.activity_quiz);

        if (savedInstanceState != null) {
            mCurrentIndex = savedInstanceState.getInt(KEY_INDEX, 0);
        }

        mQuestionTextView = (TextView) findViewById(R.id.question_text_view);
        int question = mQuestionBank[mCurrentIndex].getTextResId();
        mQuestionTextView.setText(question);

        mQuestionTextView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mCurrentIndex = (mCurrentIndex + 1) % mQuestionBank.length;
                updateQuestion();
            }
        });

        mTrueButton = (Button) findViewById(R.id.true_button);
        mTrueButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                checkAnswer(true);
            }
        });

        mFalseButton = (Button) findViewById(R.id.false_button);
        mFalseButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                checkAnswer(false);
            }
        });

        mPrevButton = (Button) findViewById(R.id.prev_button);
        mPrevButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mCurrentIndex = mCurrentIndex - 1 < 0 ? (mCurrentIndex - 1 + mQuestionBank.length) : mCurrentIndex - 1;
                mCurrentIndex = mCurrentIndex % mQuestionBank.length;
                updateQuestion();
            }
        });

        mNextButton = (Button) findViewById(R.id.next_button);
        mNextButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mCurrentIndex = (mCurrentIndex + 1) % mQuestionBank.length;
                updateQuestion();
            }
        });
    }
    private void updateQuestion() {
        mTrueButton.setClickable(true);
        mFalseButton.setClickable(true);

        int question = mQuestionBank[mCurrentIndex].getTextResId();
        mQuestionTextView.setText(question);
    }

    private void checkAnswer(boolean userPressedTrue) {
        mTrueButton.setClickable(false);
        mFalseButton.setClickable(false);

        boolean answerIsTrue = mQuestionBank[mCurrentIndex].isAnswerTrue();

        int messageResId = 0;
        if (userPressedTrue == answerIsTrue) {
            messageResId = R.string.correct_toast;
            isRight++;
        } else {
            messageResId = R.string.incorrect_toast;
            isWrong++;
        }
        Toast.makeText(this, messageResId, Toast.LENGTH_SHORT).show();
        if ((isRight + isWrong) % mQuestionBank.length == 0)
            Toast.makeText(this, "percentage score " + ((float)isRight)/(isRight + isWrong), Toast.LENGTH_SHORT).show();
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.d(TAG, "onStart() called");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume() called");
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "onPause() called");
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Log.i(TAG, "onSaveInstanceState");
        outState.putInt(KEY_INDEX, mCurrentIndex);
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG, "onStop() called");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy() called");
    }
}

截图


第四章  Debugging Android Apps

FATAL EXCEPTION

FATAL EXCEPTION: main
                                                                         Process: com.audio.android.geoquiz, PID: 2996
                                                                         java.lang.RuntimeException: Unable to start activity ComponentInfo{com.audio.android.geoquiz/com.audio.android.geoquiz.QuizActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(int)' on a null object reference
                                                                             at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2665)
                                                                             at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726)
                                                                             at android.app.ActivityThread.-wrap12(ActivityThread.java)

技巧

When you encounter runtime exceptions, remember to look for the last exception in Logcat and the first line in its stack trace that refers to code that you have written.

错误的行为,程序的逻辑错误怎么解决。

Log.d(TAG, "Updating question text", new Exception());

在updateQuestion函数里加上上面的打印之后

01-01 09:04:13.796 7562-7562/com.audio.android.geoquiz D/QuizActivity: Updating question text
                                                                       java.lang.Exception
                                                                           at com.audio.android.geoquiz.QuizActivity.updateQuestion(QuizActivity.java:103)
                                                                           at com.audio.android.geoquiz.QuizActivity.access$200(QuizActivity.java:13)
                                                                           at com.audio.android.geoquiz.QuizActivity$5.onClick(QuizActivity.java:93)
                                                                           at android.view.View.performClick(View.java:5618)
                                                                           at android.view.View$PerformClick.run(View.java:22286)
                                                                           at android.os.Handler.handleCallback(Handler.java:751)
                                                                           at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                           at android.os.Looper.loop(Looper.java:154)
                                                                           at android.app.ActivityThread.main(ActivityThread.java:6174)
                                                                           at java.lang.reflect.Method.invoke(Native Method)
                                                                           at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
                                                                           at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)


The Layout Inspector button

explore the properties of your layout.


Allocation Tracker

recording memory allocations as you interact with your app.



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值