首页 > 解决方案 > Android Studio:如何使用片段正确登录 Firebase

问题描述

我搜索了几个 StackOverflow 问题,例如:

显示 Firebase 数据库条目时 Android 应用程序崩溃

是否有必要在 Android Firebase 的每个活动中添加一个 AuthStateListener?

应用程序因 java.lang.NullPointerException 而崩溃

FirebaseAuth.getCurrentUser() 返回空值

android-java.lang.NullPointerException 使用 getCurrentUser firebase auth

使用 FirebaseAuthentication 时应用程序在首次启动期间崩溃

什么是 NullPointerException,我该如何解决?

最后是关于我面临的问题的最接近的问题:

在片段上的 getCurrentUser().getUid() 上获取空对象引用

我还没有找到答案。在最后一个链接中,StackOverflow 成员解释说发生NullPointerException是因为应用程序忽略了从 MainActivity 到 LoginActivity 的重定向,getCurrentUser().getUid() == null并执行了发生NullPointerException的 ChatFragment 。

我的应用程序一开始在模拟器上遇到了这种问题,但不知何故它在模拟器上运行良好。

但是现在,我尝试在我的物理设备中安装该应用程序并且该应用程序崩溃了。我收到NullPointerException因为用户尚未登录。

我的应用类似于 WhatsApp ChatApp。它具有带有两个片段的滑动 TabLayout。我的应用程序启动 MainActivity,它应该在首次使用应用程序的情况下将 AppUser 重定向到 LoginActivity。

这是我的 MainActivity

public class MainPageActivity extends AppCompatActivity implements GroupListAdapter.DataChangeListener {

    public static final int PERMISSIONS_MULTIPLE_REQUEST = 123;

    private Toolbar mToolBar;
    private ViewPager mViewPager;
    private TabLayout mTabLayout;

    private TabsAccessAdapter mTabsAcessAdapter;

    private FirebaseAuth mAuth;
    private DatabaseReference RootRef;

    private String currentUserID;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_page);

        FirebaseApp.initializeApp(this);

        mAuth = FirebaseAuth.getInstance();
        RootRef = FirebaseDatabase.getInstance().getReference();

        mToolBar = (Toolbar) findViewById(R.id.main_page_toolbar);
        setSupportActionBar(mToolBar);
        getSupportActionBar().setTitle("MyApp");

        mViewPager = (ViewPager) findViewById(R.id.tabs_pager);
        mTabsAcessAdapter = new TabsAccessAdapter(getSupportFragmentManager());
        mViewPager.setAdapter(mTabsAcessAdapter);

        mTabLayout = (TabLayout) findViewById(R.id.tablayout);
        mTabLayout.setupWithViewPager(mViewPager);

        getPermissions();

    }


    private void getPermissions() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            requestPermissions(new String[]{Manifest.permission.WRITE_CONTACTS,Manifest.permission.READ_CONTACTS,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE},PERMISSIONS_MULTIPLE_REQUEST);
        }
    }

    @Override
    protected void onStart() {
        super.onStart();

        Log.i("debinf mainpage", "onStart");

        FirebaseAuth.getInstance().addAuthStateListener(new FirebaseAuth.AuthStateListener() {
            @Override
            public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {

                FirebaseUser currentUser = firebaseAuth.getCurrentUser();

                if (currentUser == null) {
                    //Here is the place to redirect the AppUser to LoginActivity
                    sendUserToLoginActivity();
                }else {

                    VerifyUserExistance();
                }

            }
        });
    }

    private void VerifyUserExistance() {
        String currentUserID = mAuth.getCurrentUser().getUid();

        RootRef.child("user").child(currentUserID).addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                Toast.makeText(MainPageActivity.this, "Bem vindo(a)!", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) {

            }
        });
    }


    // Dealing with MENU > starts here
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);

        getMenuInflater().inflate(R.menu.options_menu,menu);

        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        super.onOptionsItemSelected(item);

        if (item.getItemId() == R.id.main_logout_option) {


            mAuth.signOut();
            sendUserToLoginActivity();
        }
        if (item.getItemId() == R.id.main_settings_option) {
            //sendUserToSettingsActivity();
            Toast.makeText(this, "Settings from MainAct", Toast.LENGTH_SHORT).show();
        }

        return false;
    }

    private void sendUserToLoginActivity() {
        Intent loginIntent = new Intent(MainPageActivity.this,LoginActivity.class);
        loginIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        startActivity(loginIntent);

    }


    @Override
    public void onDataSetChanged(String groupKeyFromSender, boolean addToCall) {
        String tag = "android:switcher:" + R.id.tabs_pager + ":" + 1;
        CallFragment rf = (CallFragment) getSupportFragmentManager().findFragmentByTag(tag);
        rf.getDataFromGroupFragment(groupKeyFromSender, addToCall);
    }
}

这是我的 LoginActivity,它应该在第一次使用时由 MainActivity 调用,但事实并非如此。

public class LoginActivity extends AppCompatActivity {

    private EditText mPhoneNumber, mCode;
    private Button mSend;

    private PhoneAuthProvider.OnVerificationStateChangedCallbacks mCallbacks;

    String mVerificationId;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        FirebaseApp.initializeApp(this);

        userIsLoggedIn();

        mPhoneNumber = findViewById(R.id.phoneNumber);
        mCode = findViewById(R.id.code);

        mSend = findViewById(R.id.send);
        mSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mVerificationId != null) {
                    verifyPhoneNumberWithCode();
                } else {
                    startPhoneNumberVerification();
                }

            }
        });

        mCallbacks = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
            @Override
            public void onVerificationCompleted(PhoneAuthCredential phoneAuthCredential) {
                sighInWithPhoneAuthCredential(phoneAuthCredential);
            }

            @Override
            public void onVerificationFailed(FirebaseException e) {

            }

            @Override
            public void onCodeSent(String verificationId, PhoneAuthProvider.ForceResendingToken forceResendingToken) {
                super.onCodeSent(verificationId, forceResendingToken);

                mVerificationId = verificationId;
                mSend.setText("Enviar codigo");
            }
        };
    }

    private void verifyPhoneNumberWithCode(){
        PhoneAuthCredential credential = PhoneAuthProvider.getCredential(mVerificationId, mCode.getText().toString());
        sighInWithPhoneAuthCredential(credential);
    }

    private void sighInWithPhoneAuthCredential(PhoneAuthCredential phoneAuthCredential) {
        FirebaseAuth.getInstance().signInWithCredential(phoneAuthCredential).addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                if (task.isSuccessful()) {
                    //userIsLoggedIn();
                    final FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();

                    // I think add child named user and inside other child named userId = user.getUid()
                    if (user != null) {
                        // Do not forget to add database implementation in the gradle (module app)
                        final DatabaseReference mUserDB = FirebaseDatabase.getInstance().getReference().child("user").child(user.getUid());
                        mUserDB.addListenerForSingleValueEvent(new ValueEventListener() {
                            @Override
                            public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                                if (!dataSnapshot.exists()) {
                                    // if user has never loged in, add a new user
                                    Map<String, Object> userMap = new HashMap<>();
                                    userMap.put("phone", user.getPhoneNumber());
                                    //userMap.put("name", user.getPhoneNumber());
                                    userMap.put("name", "babaloo");
                                    mUserDB.updateChildren(userMap);
                                }
                                userIsLoggedIn();
                            }

                            @Override
                            public void onCancelled(@NonNull DatabaseError databaseError) {

                            }
                        });
                    }
                }
            }
        });
    }

    private void userIsLoggedIn() {

        FirebaseAuth.getInstance().addAuthStateListener(new FirebaseAuth.AuthStateListener() {
            @Override
            public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {

                FirebaseUser user = firebaseAuth.getCurrentUser();

                if (user != null) {
                    startActivity(new Intent(getApplicationContext(), MainPageActivity.class));
                    finish();
                    return;
                }
            }
        });


    }

    private void startPhoneNumberVerification() {
        PhoneAuthProvider.getInstance().verifyPhoneNumber(
                mPhoneNumber.getText().toString(),60,
                TimeUnit.SECONDS, this, mCallbacks);
    }
}

这里是发生NullPointerException的 GroupFragment 的 onCreateView 。

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    view = inflater.inflate(R.layout.group_fragment, container, false);

    groupTablePath = getContext().getFilesDir().getPath()+"/GroupDataBase/";
    rootPath = Environment.getExternalStorageDirectory() + "/";

    mAuth = FirebaseAuth.getInstance();
    currentUser = mAuth.getCurrentUser().getUid();

    GroupRef = FirebaseDatabase.getInstance().getReference().child("Group");
    UserRef = FirebaseDatabase.getInstance().getReference().child("user");

    StoreRef = FirebaseStorage.getInstance().getReference();

    FloatingActionButton findUser = (FloatingActionButton) view.findViewById(R.id.addContact);
    findUser.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {

            startActivity(new Intent(view.getContext(), FindUserActivity.class));

        }
    });

    //getPermissions();


    CreateRootFolder();
    initializeRecyclerView();
    loadGroupListDatabase();
    populateTeamTableInternet();
    OwnerGroupUploadClientListTable();
    updateClientListInternet();


    // Inflate the layout for this fragment
    return view;
}

这部分错误

at com.example.aliton.myapp.Fragments.GroupFragment.onCreateView(GroupFragment.java:107)

对应GroupFragment的onCreateView中的这行代码:

currentUser = mAuth.getCurrentUser().getUid();

这是致命错误:

02-09 20:24:10.950 24837-24837/com.example.aliton.myapp E/AndroidRuntime: FATAL EXCEPTION: main
    java.lang.NullPointerException
        at com.example.aliton.myapp.Fragments.GroupFragment.onCreateView(GroupFragment.java:107)
        at android.support.v4.app.Fragment.performCreateView(Fragment.java:2439)
        at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1460)
        at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1784)
        at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1852)
        at android.support.v4.app.BackStackRecord.executeOps(BackStackRecord.java:802)
        at android.support.v4.app.FragmentManagerImpl.executeOps(FragmentManager.java:2625)
        at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2411)
        at android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2366)
        at android.support.v4.app.FragmentManagerImpl.execSingleAction(FragmentManager.java:2243)
        at android.support.v4.app.BackStackRecord.commitNowAllowingStateLoss(BackStackRecord.java:654)
        at android.support.v4.app.FragmentPagerAdapter.finishUpdate(FragmentPagerAdapter.java:146)
        at android.support.v4.view.ViewPager.populate(ViewPager.java:1244)
        at android.support.v4.view.ViewPager.populate(ViewPager.java:1092)
        at android.support.v4.view.ViewPager.onMeasure(ViewPager.java:1622)
        at android.view.View.measure(View.java:15294)
        at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:665)
        at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:447)
        at android.view.View.measure(View.java:15294)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4818)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
        at android.support.v7.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:143)
        at android.view.View.measure(View.java:15294)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4818)
        at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1421)
        at android.widget.LinearLayout.measureVertical(LinearLayout.java:712)
        at android.widget.LinearLayout.onMeasure(LinearLayout.java:605)
        at android.view.View.measure(View.java:15294)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4818)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
        at android.view.View.measure(View.java:15294)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4818)
        at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1421)
        at android.widget.LinearLayout.measureVertical(LinearLayout.java:712)
        at android.widget.LinearLayout.onMeasure(LinearLayout.java:605)
        at android.view.View.measure(View.java:15294)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4818)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
        at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2432)
        at android.view.View.measure(View.java:15294)
        at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:1872)
        at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1115)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1296)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1013)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:4245)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:725)
        at android.view.Choreographer.doCallbacks(Choreographer.java:555)
        at android.view.Choreographer.doFrame(Choreographer.java:525)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:711)
        at android.os.Handler.handleCallback(Handler.java:615)
        at android.os.Handler.dispatchMessage(Handler.java:92)
        at android.os.Looper.loop(Looper.java:153)
        at android.app.ActivityThread.main(ActivityThread.java:5076)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:511)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:790)
        at com.

我很感激任何帮助。

标签: androidfirebaseandroid-fragments

解决方案


这里有3个问题

  1. 身份验证侦听器是异步的,因此可以使用片段适配器创建竞争条件
  2. 您在 onCreate 之后在 onStart 上附加了 auth 侦听器,此时片段已经附加
  3. 如果用户未登录,则您将主逻辑屏幕作为入口点,然后它将返回登录。它应该是向后的,你应该使登录允许用户前进到下一个活动。

要修复 1 和 2,您将不得不重构代码。

要修复数字 3,您应该在清单中将登录设置为默认活动。

要修复您现在拥有的内容,请将其添加到 main 的 onCreate 方法中

FirebaseApp.initializeApp(this);

        mAuth = FirebaseAuth.getInstance();
        RootRef = FirebaseDatabase.getInstance().getReference();
if (mAuth.getCurrentUser() == null) return;

返回该点将使代码跳转到 onStary ,在那里您有 auth 侦听器使您的逻辑正常工作


推荐阅读