Android Firebase登录模块空指针异常问题排查求助
问题概述
开发基于Firebase的Android登录功能时,在activity_singIn的onClick方法中调用singInPresenter.singInUser(v)触发空指针异常,报错信息如下:
2022-09-18 17:30:51.785 3811-3811/com.example.Natour21 E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.Natour21, PID: 3811
java.lang.NullPointerException: Attempt to invoke virtual method 'void com.example.NaTour21.Presenter.Activity.SingInPresenter.singInUser(android.view.View)' on a null object reference
at com.example.NaTour21.View.Activity.activity_singIn$2.onClick(activity_singIn.java:61)
核心问题分析
- Presenter未初始化:
activity_singIn中声明了SingInPresenter singInPresenter,但从未实例化,调用方法时触发空指针。 - Presenter设计错误:
SingInPresenter继承AppCompatActivity,不符合MVP架构中Presenter仅处理业务逻辑的职责定位。 - Firebase方法误用:登录应调用
signInWithEmailAndPassword,代码错误使用了注册方法createUserWithEmailAndPassword。 - 视图组件未关联:Presenter内的
EmailLgn、PasswordLgn等控件未绑定Activity视图,后续会触发新的空指针。
修复步骤
1. 重构Presenter为普通类
将SingInPresenter改为普通类,通过构造函数接收上下文和必要视图组件,专注处理登录业务逻辑:
package com.example.NaTour21.Presenter.Activity; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.os.Handler; import android.view.View; import android.widget.ProgressBar; import androidx.annotation.NonNull; import com.example.NaTour21.View.Activity.activity_Home; import com.google.android.gms.tasks.OnCompleteListener; import com.google.android.gms.tasks.Task; import com.google.android.material.snackbar.Snackbar; import com.google.firebase.auth.AuthResult; import com.google.firebase.auth.FirebaseAuth; import com.google.firebase.auth.FirebaseUser; public class SingInPresenter { private Context mContext; private ProgressBar mProgressBar; private FirebaseAuth mAuth; public SingInPresenter(Context context, ProgressBar progressBar) { mContext = context; mProgressBar = progressBar; mAuth = FirebaseAuth.getInstance(); } public void singInUser(String email, String password, View view) { mProgressBar.setVisibility(View.VISIBLE); mAuth.signInWithEmailAndPassword(email, password) .addOnCompleteListener(new OnCompleteListener<AuthResult>() { @Override public void onComplete(@NonNull Task<AuthResult> task) { if (task.isSuccessful()) { new Handler().postDelayed(() -> navigateToHome(), 3000); } else { mProgressBar.setVisibility(View.GONE); String errorMsg = "登录失败:" + task.getException().getMessage(); showErrorSnackbar(view, errorMsg); } } }); } private void navigateToHome() { Intent intent = new Intent(mContext, activity_Home.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); mContext.startActivity(intent); } public void checkCurrentUser() { FirebaseUser currentUser = mAuth.getCurrentUser(); if (currentUser != null) { navigateToHome(); } } private void showErrorSnackbar(View view, String message) { Snackbar snackbar = Snackbar.make(view, message, Snackbar.LENGTH_SHORT); snackbar.setBackgroundTint(Color.WHITE); snackbar.setTextColor(Color.BLACK); snackbar.show(); } }
2. 修正Activity逻辑并初始化Presenter
在activity_singIn中实例化Presenter,调整登录逻辑并修正错误判断:
package com.example.NaTour21.View.Activity; import android.content.Intent; import android.graphics.Color; import android.os.Bundle; import android.widget.Button; import android.widget.EditText; import android.widget.ProgressBar; import androidx.appcompat.app.AppCompatActivity; import com.example.NaTour21.Presenter.Activity.SingInPresenter; import com.example.NaTour21.R; import com.google.android.material.snackbar.Snackbar; import android.view.View; public class activity_singIn extends AppCompatActivity{ private EditText EmailLgn,PasswordLgn; private Button loginLgn; private Button registratiLgn; private SingInPresenter singInPresenter; private ProgressBar progressbar; String[] messaggio = {"请填写所有字段", "登录成功", "邮箱格式无效", "密码长度不能少于6位"}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_singin); getSupportActionBar().hide(); initializeComponent(); // 初始化Presenter singInPresenter = new SingInPresenter(this, progressbar); registratiLgn.setOnClickListener(v -> { Intent intent = new Intent(activity_singIn.this, activity_singUp.class); startActivity(intent); }); loginLgn.setOnClickListener(v -> { String email = EmailLgn.getText().toString().trim(); String password = PasswordLgn.getText().toString().trim(); if (email.isEmpty() || password.isEmpty()) { showSnackbar(v, messaggio[0]); } else if (!isValidEmail(email)) { showSnackbar(v, messaggio[2]); } else if (password.length() < 6) { // 修正原逻辑的判断错误 showSnackbar(v, messaggio[3]); } else { showSnackbar(v, messaggio[1]); // 传递邮箱密码给Presenter处理 singInPresenter.singInUser(email, password, v); } }); } @Override protected void onStart() { super.onStart(); // 检查已登录用户 singInPresenter.checkCurrentUser(); } private boolean isValidEmail(String email) { return android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches(); } private void initializeComponent() { registratiLgn = findViewById(R.id.btRegistratiLgn); EmailLgn = findViewById(R.id.tilEmailLgn); PasswordLgn = findViewById(R.id.tilPasswordLgn); loginLgn = findViewById(R.id.btLoginLgn); progressbar = findViewById(R.id.progressbarLgn); } private void showSnackbar(View view, String message) { Snackbar snackbar = Snackbar.make(view, message, Snackbar.LENGTH_SHORT); snackbar.setBackgroundTint(Color.WHITE); snackbar.setTextColor(Color.BLACK); snackbar.show(); } }
3. 修正布局中ProgressBar的属性
布局中ProgressBar误用ConstraintLayout属性,改为LinearLayout适配的属性:
<ProgressBar android:id="@+id/progressbarLgn" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:visibility="invisible" />
关键修复点总结
- 实例化
SingInPresenter,解决空指针问题。 - 重构Presenter为普通类,遵循MVP架构职责分离原则。
- 替换Firebase方法为正确的登录接口
signInWithEmailAndPassword。 - 修正Activity中密码长度的判断逻辑(原逻辑写反导致错误提示)。
- 将视图数据从Activity传递给Presenter,避免Presenter直接操作视图。
内容的提问来源于stack exchange,提问作者Fabiola Salomone

