0

I have searched several StackOverflow questions such as:

Android app crashing when displaying firebase database entries

Is it necessary to add an AuthStateListener in every activity of Android Firebase?

App crashes with java.lang.NullPointerException

FirebaseAuth.getCurrentUser() is returning null value

android- java.lang.NullPointerException using getCurrentUser firebase auth

application crashes during the first launch while using FirebaseAuthentication

What is a NullPointerException, and how do I fix it?

And finally the closest question regarding to the problem I am facing:

Getting Null Object Reference on getCurrentUser().getUid() on Fragment

I have not found an answer yet. In the last link, a StackOverflow member explained that the NullPointerException happens because somehow the app ignores the redirection from the MainActivity to the LoginActivity in the case of getCurrentUser().getUid() == null and executes the ChatFragment, where the NullPointerException occurs.

My app had this kind of problem at the beginning on the Emulator, but somehow it works fine on the Emulator.

But now, I tried to install the app in my physical device and the app is crashing. I get the NullPointerException because the user is not yet signed in.

My app is similar to WhatsApp ChatApp. It has sliding TabLayout with two fragments. My app launchs the MainActivity which should redirect the AppUser to LoginActivity in case of first use of the app.

Here is my 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);
    }
}

Here is my LoginActivity which should be called by MainActivity in the first usage, but it isn't.

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);
    }
}

And here is the onCreateView of the GroupFragment where the NullPointerException happens.

@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;
}

This part of the error

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

corresponds to this line of the code in the onCreateView of the GroupFragment:

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

And here is FATAL error:

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.

I appreciate any help.

Aliton Oliveira
  • 1,224
  • 1
  • 15
  • 26
  • You can also check **[this](https://stackoverflow.com/questions/50885891/one-time-login-in-app-firebaseauth)** out. – Alex Mamo Feb 10 '19 at 09:32
  • Thank you! But I think the very key to this problem is the **Fragments**. In the **Fragments** I use the `currentUser` everywhere. What I do not understand is why `MainActivity` is not redirecting to `LoginActivity`, but instead the code keeps running and calling `currentUser` in all my **Fragments**. So, do I have to implement `AuthStateListener` in all my **Fragments**? – Aliton Oliveira Feb 10 '19 at 17:28

2 Answers2

1

There are 3 problems here

  1. Auth listener is async, so that create race conditions with the fragments adapter
  2. You are attaching the auth listener on onStart after onCreate by that moment the fragment is already attached
  3. You are making the main logic screen to be the entry point if the user is not logged then it returns to login. It should be backward, you should make the login allows the user to move forward to the next activity.

For fixing 1 and 2 you will have to refactor your code.

For fixing number 3 you should set login as default activity in the manifest.

For fixing what you have now, add this in the onCreate method of the main

FirebaseApp.initializeApp(this);

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

Returning in that point will make the code jump to onStary where you have the auth listener making your logic work

Mohamed Sahbi
  • 1,065
  • 10
  • 24
cutiko
  • 9,887
  • 3
  • 45
  • 59
  • Good points! Regarding to number 3, I made my app start from `LoginActivity`, but aesthetically it was not a good experience because **Login layout** was visible for a brief moment before launching `MainActivity`. Regarding to number 2, is it more appropriated to attatch the auth listener in the **onCreate** instead of **onStart**? – Aliton Oliveira May 15 '19 at 02:18
  • Your login layout problem can be solved by improving the ui, hide and show elements as needed. Again, the auth listener is async. In this case you simple need .getCurrentUser if is null is not logged if is not null then is logged. – cutiko May 15 '19 at 04:16
  • As soon as I put your suggestions in practice, I will let you know. Thank you! – Aliton Oliveira May 15 '19 at 16:17
  • I've added your solution `if (mAuth.getCurrentUser() == null) return;` as you suggested and it solved the problem. It worked perfectly. Thank you! – Aliton Oliveira May 15 '19 at 21:11
0

If mAuth.getCurrentUser() returns null, that means the user isn't signed in yet, or more accurately: that the Firebase client hasn't verified that the user has signed in yet. The verification happens asynchronously and it could be that your mAuth.getCurrentUser() runs before it has completed.

The solution for this problem is always the same: use an AuthStateListener like you do in the activity.

So something like this:

@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();
    FirebaseAuth.getInstance().addAuthStateListener(new FirebaseAuth.AuthStateListener() {
        @Override
        public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {


            if (firebaseAuth.getCurrentUser() != null) {
                currentUser = mAuth.getCurrentUser().getUid();

                // TODO: any code that needs the UID should be in here

            }
        }

The onAuthStateChanged gets executed whenever the authentication state of the user changes. So in this case also once the client has verified the authentication state. And from that moment on, you can safely get the user's UID.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • I implemented `addAuthStateListener` as you suggested in `GroupFragment`, but I call `currentUser` everywhere in all my **Fragments**. I am disabling and commenting all my code and my app is not doing what it is supposed to do anymore. I do not understand why the code keeps running even when it finds a redirection to `LoginActivity` in the `MainActivity` and also in `GroupFragment`. – Aliton Oliveira Feb 10 '19 at 18:11