You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android中Testme Activity启动时触发NullPointerException问题求助

解决Testme Activity启动时的RuntimeException(NullPointerException)

兄弟,我一眼就瞅出问题根源了——你在类的成员变量声明阶段直接调用了findViewById()!这时候Activity还没完成初始化,连布局都还没加载,根本找不到对应的控件,直接就触发空指针了。

问题原因详解

Android的Activity生命周期里,findViewById()的调用必须在setContentView()之后,因为只有加载了布局文件,系统才能根据ID找到对应的控件实例。你现在把txtquestion、answer1这些控件变量放在类的成员位置直接初始化,这时候Activity的上下文还没准备好,Window对象都是null,也就出现了报错里的Attempt to invoke virtual method 'android.view.Window$Callback android.view.Window.getCallback()' on a null object reference。

修复步骤

  1. 先声明控件变量,不要直接初始化:把所有控件变量的声明留在类的成员位置,但去掉findViewById()的调用。
  2. 在setContentView()之后初始化控件:在onCreate()方法里,调用完setContentView(R.layout.activity_testme);之后,再逐个调用findViewById()初始化控件。

修改后的完整代码示例

public class Testme extends AppCompatActivity {
    // 只声明控件变量,不初始化
    private TextView txtquestion;
    private Button answer1;
    private Button answer2;
    private Button answer3;
    private Button answer4;
    
    int qnum = 0;
    private List<TopicQuestion> questionsList = new ArrayList<>();
    private boolean correct = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_testme);
        // 在这里初始化所有控件
        txtquestion = findViewById(R.id.txtquestion);
        answer1 = findViewById(R.id.btnans1);
        answer2 = findViewById(R.id.btnans2);
        answer3 = findViewById(R.id.btnans3);
        answer4 = findViewById(R.id.btnans4);
        readQuestions();
    }

    private void readQuestions() {
        InputStream is = getResources().openRawResource(R.raw.questions);
        BufferedReader reader = new BufferedReader(
                new InputStreamReader(is, Charset.forName("UTF-8"))
        );
        String line = "";
        try {
            while ( (line = reader.readLine()) != null) {
                //Split the data
                String[] tokens = line.split(",");
                //Read the data
                TopicQuestion question = new TopicQuestion();
                question.setTopic(tokens[0]);
                question.setQuestion(tokens[1]);
                question.setAns1(tokens[2]);
                question.setAns2(tokens[3]);
                question.setAns3(tokens[4]);
                question.setAns4(tokens[5]);
                questionsList.add(question);
            }
        } catch (IOException e) {
            Log.wtf("MyActivity","Error reading question file on line" + line, e);
            e.printStackTrace();
        }

        // 初始化第一题的内容
        updateQuestionUI();

        answer1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                correct = questionsList.get(qnum).getAns1().equals(answer1.getText().toString());
                // 点击后再判断是否切换题目
                checkAnswerAndUpdate();
            }
        });
        answer2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                correct = questionsList.get(qnum).getAns2().equals(answer2.getText().toString());
                checkAnswerAndUpdate();
            }
        });
        answer3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                correct = questionsList.get(qnum).getAns2().equals(answer3.getText().toString()); // 这里你之前写的是getAns1,应该是笔误吧?
                checkAnswerAndUpdate();
            }
        });
        answer4.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                correct = questionsList.get(qnum).getAns4().equals(answer4.getText().toString()); // 同样这里之前是getAns1,应该是笔误
                checkAnswerAndUpdate();
            }
        });
    }

    // 抽取出更新题目UI的方法,避免重复代码
    private void updateQuestionUI() {
        if (qnum >= questionsList.size()) {
            // 防止数组越界,比如题目做完了的处理
            txtquestion.setText("题目已完成!");
            return;
        }
        TopicQuestion currentQ = questionsList.get(qnum);
        txtquestion.setText(currentQ.getQuestion());
        answer1.setText(currentQ.getAns1());
        answer2.setText(currentQ.getAns3());
        answer3.setText(currentQ.getAns2());
        answer4.setText(currentQ.getAns4());
    }

    // 抽取出判断答案并更新题目的方法
    private void checkAnswerAndUpdate() {
        if (correct) {
            qnum +=1;
        }
        updateQuestionUI();
    }
}

额外的小建议

  • 我还注意到你原来的readQuestions()方法里,在设置完点击事件后直接判断correct的值,这时候correct初始是false,而且用户还没点击任何按钮,这个判断完全没有意义,我把这部分逻辑移到了点击事件里,每次用户点击后再判断是否切换题目。
  • 另外你在answer3和answer4的点击事件里,判断的是getAns1(),这明显是笔误,我已经改成了对应的getAns2()和getAns4(),你可以根据实际需求调整。
  • 新增了updateQuestionUI()方法,把重复的更新UI代码抽出来,既简洁又方便维护,还加了防止数组越界的判断。

内容的提问来源于stack exchange,提问作者Christian Webb

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:45:59