Profile.getCurrentProfile() returns null after logging in (FB API v4.0)

For some reason Profile.getCurrentProfile() shows up null at times right after logging into FaceBook, using FB API v4.0.

This is causing problems for me in my app because I can't display my next Activity with Profile being null.

The reason I say it's null sometimes is because if I close out of my app and re-open it, I'm able to get to my next Activity, but if I'm not already logged in, and then login, Profile is null. It seems it is for a short period.

Is there a work around or fix to this?


Solution 1:

Like Hardy said, you have to create an instance of ProfileTracker which will start tracking profile updates, (i.e ProfileTracker.onCurrentProfileChanged() will be called when the user's profile finishes being fetched).

Following is the complete code that you'd need to login to FB and get the user's profile:

LoginButton loginButton = (LoginButton) findViewById(R.id.btn_facebook);
loginButton.setReadPermissions("public_profile");
mCallbackManager = CallbackManager.Factory.create();
loginButton.registerCallback(mCallbackManager, new FacebookCallback<LoginResult>() {

    private ProfileTracker mProfileTracker;

    @Override
    public void onSuccess(LoginResult loginResult) {
        if(Profile.getCurrentProfile() == null) {
            mProfileTracker = new ProfileTracker() {
                @Override
                protected void onCurrentProfileChanged(Profile oldProfile, Profile currentProfile) {
                    Log.v("facebook - profile", currentProfile.getFirstName());
                    mProfileTracker.stopTracking();
                }
            };
            // no need to call startTracking() on mProfileTracker
            // because it is called by its constructor, internally.
        }
        else {
            Profile profile = Profile.getCurrentProfile();
            Log.v("facebook - profile", profile.getFirstName());
        }
    }

    @Override
    public void onCancel() {
        Log.v("facebook - onCancel", "cancelled");
    }

    @Override
    public void onError(FacebookException e) {
        Log.v("facebook - onError", e.getMessage());
    }
});

You must override your Activity's or Fragment's onActivityResult() as below:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    // if you don't add following block,
    // your registered `FacebookCallback` won't be called
    if (mCallbackManager.onActivityResult(requestCode, resultCode, data)) {
        return;
    }
}

Edit:

Code updated with Alex Zezekalo's suggestion to only call mProfileTracker.startTracking(); if Profile.getCurrentProfile() returns null.

Solution 2:

Following to Sufian's answer, you can also save the new profile to Profile class:

@Override
public void onSuccess(LoginResult loginResult) {
    ProfileTracker profileTracker = new ProfileTracker() {
        @Override
        protected void onCurrentProfileChanged(Profile oldProfile, Profile currentProfile) {
            this.stopTracking();
            Profile.setCurrentProfile(currentProfile);

        }
    };
    profileTracker.startTracking();
}

Solution 3:

Facebook loads the profile information asynchronously, so even after you get your result from the login callback, Profile.getCurrentProfile() will return null.

However, every time the user logs in through Facebook (the first time and every subsequent time) his profile will change and will fire the profiletracker. This is where you must call the profile.

Here is how you should structure your code. You must listen for the ProfileTracker to update in order to update your user properties - avoid calling getCurrentProfile outside the tracker during the login process:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        FacebookSdk.sdkInitialize(getApplicationContext());
        callbackManager = CallbackManager.Factory.create();
        profileTracker = new ProfileTracker() {
            @Override
            protected void onCurrentProfileChanged(Profile profile, Profile profile1) {


 //Listen for changes to the profile or for a new profile: update your
 //user data, and launch the main activity afterwards. If my user has just logged in,
 //I make sure to update his information before launching the main Activity.

                Log.i("FB Profile Changed", profile1.getId());
                updateUserAndLaunch(LoginActivity.this);


            }
        };
        profileTracker.startTracking();

        accessTokenTracker = new AccessTokenTracker() {
            @Override
            protected void onCurrentAccessTokenChanged(
                    AccessToken oldAccessToken,
                    AccessToken currentAccessToken) {




//Check that the new token is valid. This tracker is fired
//when the user logs in the first time and afterwards any time he interacts with 
//the Facebook API and there is a change in his permissions.

                if (!accessTokenIsValid(currentAccessToken)){
                    Log.i("FB Token Updated", String.valueOf(currentAccessToken.getPermissions()));
                    requestLogin();

                }
            }
        };
        // User already has a valid access token? Then take the user to the main activity
        if (accessTokenIsValid(AccessToken.getCurrentAccessToken())){
               launchApp();
        }else{
//Show the Facebook login page
            requestLogin();
        }

    }

The key here is that you should not call Profile.getcurrentprofile from the login callback (either LoginManager,registercallback or loginButton.registercallback) - the values are not reliable. Set up the tracker and rely on it firing at the opportune time in order to get updated profile info.