在 setContentView() 之前调用 findViewById() 会导致 NullPointerException 但并非总是如此? [复制]

Posted

技术标签:

【中文标题】在 setContentView() 之前调用 findViewById() 会导致 NullPointerException 但并非总是如此? [复制]【英文标题】:Calling findViewById() before setContentView() causes NullPointerException but not always? [duplicate] 【发布时间】:2020-12-28 20:00:27 【问题描述】:

当我调用findViewById() 访问按钮时,我得到了NullPointerException。我想我明白为什么我会收到这个错误,但是我脑子里有一些问题没有解决。 好吧,我想我收到了这个错误,因为我将 findViewById() 调用从 onCreate() 方法内部移到了所有方法之外的类范围内。

所以现在我在onCreate() 方法之外初始化我的ButtonEditText。 好吧,如果我理解正确,这是发生的(Null 错误),因为在 findViewById() 方法之后调用了 setContentView() 方法,所以这就是它抛出异常的原因。

但是我不明白的是,我在第二个活动中做了同样的事情,并且运行良好,没有任何空异常。我正在onCreate() 方法之外初始化我的按钮等!

这确实让我有点困惑。任何帮助我清除这个问题将不胜感激。

第一个活动

public class FirstActivity extends AppCompatActivity 
 
    private Button signUpButton= findViewById(R.id.lo_signUpButton);
    private Button loginButton = findViewById(R.id.lo_loginButton);
    private EditText username= findViewById(R.id.lo_usernameText);
    private EditText password= findViewById(R.id.lo_passwordText);

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        //set view
        setContentView(R.layout.activity_login);
        Log.i(TAG,"Create "+formatter.format(new Date()));

        //listeners
        signUpButton.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View view) 
                startActivity(new Intent(LoginActivity.this, SignUpActivity.class));
                finish();
                
        );


第二次活动

public class SecondActivity extends AppCompatActivity 

    private EditText username = findViewById(R.id.su_username);
    private EditText password = findViewById(R.id.su_password);
    private TextView errorText= findViewById(R.id.su_error_msg);
    private Button signUpButton=findViewById(R.id.su_signupButton);

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);

        //set view
        setContentView(R.layout.activity_signup);
        Log.i(TAG,"Create");

        //listeners
        Button backButton = findViewById(R.id.su_backButton);
        backButton.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View view) 
                startActivity(new Intent(SignUpActivity.this, LoginActivity.class));
                Log.i(TAG,"Going Back ");
                finish();
            
        ); 

【问题讨论】:

在字段初始化程序中调用 findViewById() 总是会崩溃。您要么在查看错误的代码,要么在运行陈旧的构建,或者其他什么。所有给出的答案都是错误的。它与您稍后设置侦听器或以其他方式访问这些字段无关。第一个findViewById() 调用将立即抛出,早于onCreate() 运行。 【参考方案1】:

不能使用此初始化:

public class SecondActivity extends AppCompatActivity 

    private Button signUpButton= findViewById(R.id.lo_signUpButton);
    //....

用途:

public class SecondActivity extends AppCompatActivity 

    private Button signUpButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);

        //set view
        setContentView(R.layout.activity_signup)
        signUpButton= findViewById(R.id.lo_signUpButton);
        //...
    

在第二个活动中,您正在做一些不同的事情:

protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_signup);
    Button backButton = findViewById(R.id.su_backButton);
    //...

这是正确的,因为您在 setContentView 调用之后使用 findViewById 内部的 findViewById 方法声明和初始化了 backButton

在第一个活动中,您使用signUpButton.setOnClickListener 设置侦听器,但signUpButton 未初始化(因为它在onCreate 方法之外)

同样在第二个活动中,其他被错误初始化的按钮在OnCreate方法中没有使用。

【讨论】:

好吧,我也是这么想的。但是为什么我的第二个 Activity 没有导致任何异常? 两者都做同样的初始化... @Panagiss 因为在第二个活动中你使用了不同的东西:Button backButton = findViewById(R.id.su_backButton); 而不是signUpButton.setOnClickListener(new View.OnClickListener() 这里有什么区别?我不明白。请原谅我,但我看到我再次打电话给findViewById()。但我只是想访问另一个View @Panagiss 我已经更新了答案。在第一个活动中,您使用signUpButton.setOnClickListener 设置侦听器,但signUpButton 未初始化。在第二个活动中,您在设置侦听器之前正确使用了findViewByIdOnCreate 方法中没有使用其他按钮。【参考方案2】:

在第二个activity中,你没有使用初始化错误的按钮,一旦你使用它们也会导致异常。

您使用了正确初始化的backButton。

【讨论】:

【参考方案3】:

答案很简单。全局变量在调用 setContentView() 之前被初始化,这意味着 findViewById() 将返回 null。 findViewById() 如果找到则返回视图或为空。因为你还没有调用 setContentView,所以它找不到你想要的视图。

FirstActivity 和 SecondActivity 中的全局变量都为空。 FirstActivity 和 SecondActivity 的区别在于,您不能访问 SecondActivity 中的任何全局变量。

您在SecondActivity中使用的唯一视图是backButton,是您调用setContentView后获取的,所以当然不会抛出NullPointerException。

始终将变量声明为全局变量并在 onCreate() 中对其进行初始化。更好的是,尽量避免使用全局变量并通过方法参数传递它们。

【讨论】:

以上是关于在 setContentView() 之前调用 findViewById() 会导致 NullPointerException 但并非总是如此? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

(BEGINNER) 调用 setContentView 时出错?

Android 何时在 Activity.onCreate() 中的 setContentView() 之后首次调用 View.onMeasure()?

Android setContentView()源码解析

在调用 f:ajax 侦听器之前和之后执行 JavaScript

setContentView流程

setContentView流程