0

I have a class called SessionManager that is supposed to handle logged in sessions using SharedPrefs. It works for the most part. I'm running into an issue where if I logged out (nothing in SharedPrefs) then logged back in--then the data is null. However, it isn't null on the Login activity--but the moment I leave the Login activity to go to the Main activity, that's when it is then null. Obviously, there is an issue with using the SharedPrefs between activities--but I'm not sure I understand why/how.

Another thing--if I close the app and then open it again--the SharedPref data is correct in the Main Activity (since we start here after we've logged in once). The issue I'm describing above only happens on the initial login.

For the sake of brevity, I've removed the getter methods as they aren't the issue.

public class SessionManager {

    Context context;
    SharedPreferences pref;
    //Editor for Shared preferences
    SharedPreferences.Editor editor;

    //SharedPref file name
    private static final String PREF_NAME = "current.user";

    //All Shared Preferences Keys
    private static final String IS_LOGIN = "IsLoggedIn";

    //Username
    public static final String KEY_USERNAME = "username";

    //Name
    public static final String KEY_NAME = "name";

    //Email
    public static final String KEY_EMAIL = "email";

    //Constructor for SessionManager
    public SessionManager(Context context) {
        this.context = context;
        pref = this.context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
        editor = pref.edit();
    }

    /**
     * Create login session
     * @param username to store in SharedPrefs
     * @param email to store in SharedPrefs
     */
    public void createLoginSession(String username, String name, String email) {
        /*Store each value into SharedPrefs*/
        editor.putBoolean(IS_LOGIN, true);
        editor.putString(KEY_USERNAME, username);
        editor.putString(KEY_NAME, name);
        editor.putString(KEY_EMAIL, email);

        //Commit changes to SharedPrefs
        editor.commit();
    }
}

I am calling the above in whatever Activity by doing this:

SessionManager mSession;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_welcome);
    ButterKnife.bind(this);

    mSession = new SessionManager(getApplicationContext());
}

I do the above for every activity.

What am I doing wrong?

Edit: Here is how I am accessing it and setting the values

Accessing it on LoginActivity:

// Create a session with values from Firebase--logging shows me these values are correct
mSession.createLoginSession(USERNAME, NAME, EMAIL);

HashMap<String, String> user = mSession.getUserDetails();
String name = user.get(SessionManager.KEY_NAME);
String username = user.get(SessionManager.KEY_USERNAME);
String email = user.get(SessionManager.KEY_EMAIL);
// Logging shows me that the name, username, and email are set properly

Then using that data to set the title in a navdrawer in MainActivity:

mSession = new SessionManager(getApplicationContext());

HashMap<String, String> user = mSession.getUserDetails();
String namey = user.get(SessionManager.KEY_NAME);
String emaily = user.get(SessionManager.KEY_EMAIL);
String usernamey = user.get(SessionManager.KEY_USERNAME);
Nxt3
  • 1,970
  • 4
  • 30
  • 52

2 Answers2

1

What am I doing wrong?

I guess you are using differnt context in other activities. You must use the same (getApplicagtionContext() is fine) everywhere, otherwise your SessionManager will be accessing different shared preference storage (XML files if you are curious :)

Marcin Orlowski
  • 72,056
  • 11
  • 123
  • 141
  • I am using the same context (`getApplicationContext()`) in every Activity. – Nxt3 Feb 18 '16 at 17:26
  • `ut the moment I leave the Login activity to go to the Main activity, that's when it is then null` - you sure you **read** the value again (i.e. in onResume()) or maybe you use what you have read before? – Marcin Orlowski Feb 18 '16 at 19:14
  • I've edited my question to show how I'm accessing the data. – Nxt3 Feb 18 '16 at 19:55
  • @Nxt3 what's `user` in LoginActivity? – Marcin Orlowski Feb 18 '16 at 19:58
  • Oops--I edited the question again. I'm getting values from Firebase and then setting them to the SharedPrefs; the values from Firebase are correct. – Nxt3 Feb 18 '16 at 20:01
  • @Nxt3 mate, what is `user` object you use in your `LoginActivity`! – Marcin Orlowski Feb 18 '16 at 20:06
  • Oh shoot--it's just a HashMap so I can check for the values being set in SharedPrefs--I have some logging that takes the values from the `user` HashMap and spits it out into the logger. I can confirm that at the time in LoginActivity--the values *ARE* stored in SharedPrefs as I would expect. – Nxt3 Feb 18 '16 at 20:09
1

I would strongly consider using a singleton pattern here. That way you are guaranteed to use the same instance across multiple classes.

Not sure why this seems to be the go-to implementation of session management.

Example usage

public class MainActivity extends AppCompatActivity {

    private SessionManager mSessionManager;

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

        this.mSessionManager = SessionManager.getInstance(getApplicationContext());

        mSessionManager.createLoginSession("tester", "Joe", "sample@example.com");
        boolean isLoggedIn = mSessionManager.getPref().getBoolean(SessionManager.IS_LOGIN, false);
        Log.d("MyTag", String.valueOf(isLoggedIn)); // true

        mSessionManager.logout();
        isLoggedIn = mSessionManager.getPref().getBoolean(SessionManager.IS_LOGIN, false);
        Log.d("MyTag", String.valueOf(isLoggedIn)); // false
    }

}

SessionManager class

public class SessionManager {

    private static SessionManager sInstance;

    private final SharedPreferences pref;
    private final SharedPreferences.Editor editor;

    public static final String PREF_NAME = "current.user";

    public static final String IS_LOGIN = "IsLoggedIn";
    public static final String KEY_USERNAME = "username";
    public static final String KEY_NAME = "name";
    public static final String KEY_EMAIL = "email";

    public static SessionManager getInstance(Context context) {
        if (sInstance == null) {
            sInstance = new SessionManager(context);
        }
        return sInstance;
    }

    private SessionManager(Context context) {
        pref = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
        editor = pref.edit();
    }

    public void createLoginSession(String username, String name, String email) {
        editor.putBoolean(IS_LOGIN, true);
        editor.putString(KEY_USERNAME, username);
        editor.putString(KEY_NAME, name);
        editor.putString(KEY_EMAIL, email);

        editor.commit();
    }

    public void logout() {
        editor.clear();
        editor.putBoolean(IS_LOGIN, false);
        editor.commit();
    }

    public String getLoggedInUsername() {
        String username = null;
        if (pref.contains(KEY_USERNAME) && pref.getBoolean(IS_LOGIN, false)) {
            username = pref.getString(KEY_USERNAME, null);
        }
        return username;
    }

    public SharedPreferences getPref() {
        return pref;
    }
}
OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
  • just small suggestion ...create default private constructor .so that one cannot create instance of it by any mean – Shakeeb Ayaz Feb 18 '16 at 20:24
  • @ShakeebAyaz - Not necessary. A defined constructor overrides the default. Just tried it myself to make sure. – OneCricketeer Feb 18 '16 at 20:28
  • This worked beautifully. Thank you very much--this is what happens when you don't pay attention in your Intro to Java CS course. :P Never thought of making it a singleton. – Nxt3 Feb 18 '16 at 21:04
  • Good to hear! I went through several semesters of Java courses before I knew what a singleton was or realized its usefulness when not abused. – OneCricketeer Feb 18 '16 at 21:09
  • @cricket_007 I spoke too soon. This still fails when logging in from a cold start. If there is no logged in user, and we login successfully, the data I get from querying SharedPrefs is still `null` (using your `getLoggedInUsername()`). If I close the app and come back--the data is there in the drawer. More interestingly, if I do the cold login, then go to another activity (where I also query the data), the data is correct. It must have to do with the speed of SharedPrefs, but I'm not certain. – Nxt3 Feb 18 '16 at 22:16
  • I might have done that method wrong. It was an example. You may try `editor.apply()` instead of `commit()`. Please [refer to this post](http://stackoverflow.com/questions/5960678/whats-the-difference-between-commit-and-apply-in-shared-preference) for the difference – OneCricketeer Feb 18 '16 at 22:22
  • @cricket_007 That didn't work either--and I tried changing that in multiple places too. [SessionManager](https://codeshare.io/P9HDF), [MainActivity](https://codeshare.io/sDQnm), [WelcomeActivity/Login](https://codeshare.io/IsLQJ). It must not be SharedPrefs related. :\ – Nxt3 Feb 18 '16 at 22:39
  • @cricket_007 Well get this: it works on the emulator--but not on my Nexus 6P. It works inconsistently on the emulator--but it works. I literally don't know how to make anything I've done faster... – Nxt3 Feb 18 '16 at 22:54
  • You might be getting lost somewhere in the Firebase callbacks. It works fine for me synchronously. Also, you have a duplicate method `checkLogin` and `isLoggedIn` – OneCricketeer Feb 18 '16 at 22:54
  • You're probably correct. I'll try doing it a different way. – Nxt3 Feb 18 '16 at 23:04