Shared preferences isn't gettingBoolean properly - android

I'm trying to load boolean value from Shared Preferences. On first launch it should be TRUE as I have never saved it to Shared Preferences. However, I'm somehow getting false.
See code below.
settings = context.getSharedPreferences(SAVED_PREFERENCES, 0);
isFirstLaunch = settings.getBoolean(FIRST_LAUNCH, true);
Log.d(TAG, "loadIsFirstLaunch: " + isFirstLaunch);
p.s. I haven't saved FIRST_LAUNCH value in shared preferences before 100%.
UPD1 I've double checked with empty project - code is working properly. Shared Preferences use Boolean class, not boolean primitive type. So it isn't the case that boolean by default is false. Also according to debug my shared preferences somehow includes FIRST_LAUNCH value by the time it is launched. So looking at how it got there.
UPD2 I finally stuck. Removed all mentionings of FirstLaunch, but it still somehow appears in SharedPreferences when another call to read other values is made. So question is - how can I get rid of saved value in Shared preference.
http://take.ms/Rr0Xf
UPD3 I've changed name to my saved preferences file and it worked. So problem was that SOMEHOW device was keeping info for saved preferences even after application clean install. PFM.

I can't commnet to your question because of less points So, if you're 100 sure that you didn't set any value to FIRST_LAUNCH, then 1. try to clean the app's data 2. Can you please provide the full source code
And #miller what if there are more than one sharedpreferences

Its because the boolean value by default sets its value to false .. it only takes the true value when there is empty value returned from the shared preference , but boolean value must have a value either true or false...it doesn't support null value.
so what you need to do is set the boolean value of the session to true programatically in the class before loading and then get the value from the session

So finally I found the problem and solution.
In android M and above versions Google keeps application backups in google driver, that's why even after reinstall shared preferences were restoring. I've disabled backups.
project manifest file under Application section set android:allowBackup="true" to false.

Related

How can I add a watch for getSharedPreferences (Where is it stored?)

I've been testing out a few sample weather app code projects looking for a 3 to 5 day forecast one that easily compiles in android studio. I can't find a simple one that is at my level of understanding yet but working with one that might help. (If anyone knows of a simple up to date weather forecast app then note it below please)
First line in question.
if (getSharedPreferences(KEY_PREF, 0).getBoolean(KEY_PREF, true)
&& servicesConnected())
From:
private static final String KEY_PREF = "firstrun";
if (getSharedPreferences(KEY_PREF, 0).getBoolean(KEY_PREF, true)
&& servicesConnected()) {
if (getSharedPreferences(KEY_PREF, 0).getBoolean(KEY_PREF, true)
&& servicesConnected()) {
// get current city lat lon
buildGoogleApiClient();
mClient.connect();
CommonUtils.showToast("Retrieving your current location...");
// this will be done one time only
getSharedPreferences(KEY_PREF, 0).edit()
.putBoolean(KEY_PREF, false).apply();
} else if (savedInstanceState == null) {
if (DBHelper.getInstance().getCityCount() == 0) {
// called only if the cities array is empty too add a city
getSupportFragmentManager()
.beginTransaction()
.add(R.id.container, new AddCityFragment(),
AddCityFragment.class.getSimpleName()).commit();
} else {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new WeatherFragment()).commit();
}
}
}
According to developer.android.com
boolean getBoolean (String key, boolean defValue)
Parameters: (1) String key: The name of the preference to retrieve. (2) defValue boolean: Value to return if this preference does not exist.
My original questions asked:
What exactly is getBoolean doing?
The documentation doesn't say what mode 0 means in the 2nd parameter of getSharedPreferences (String name, int mode). Any idea what?
How can I add a watch for getSharedPreferences to Android Studio's debugging
The Solution that I deduced with the help of you guys:
getSharedPreferences(KEY_PREF, 0) is looking for a KEY_PREF preference file on the android device. It returns an object to it which allows us to use getBoolean to determine if there is a key inside it with the same name. In this case it was looking for a "firstrun" file which doesn't exist during first run of the app on the actual android devices storage (saved as cache). Since the key didn't exist on the first run it sets the boolean value as true and continues running inside the if statement. If the key "firstrun" existed and it was set to false then it would return false and skip executing inside that part of the if statement.
Mode 0 is MODE_PRIVATE = the default mode, where the created file can only be accessed by the calling application (or all applications sharing the same user ID).
When I was trying to debug the issue I noticed that I can add a debugger watch for getSharedPreferences(KEY_PREF, 0) and drill inside the resulting preference file from withing the debugger to see that the mMap parameter to the SharedPreferences object creates a "firstrun" key with the value of "false". It was a first for me to see how it adds this value.
I also found a cool plug-in for android studio for mac and windows which is faster than going into the android device or virtual device and clearing the apps cache. It's called ADB idea and used with the CTRL+ALT+SHIFT+A hotkey combo.
Thanks everyone!
SharedPreference is just a way to store some small-size data.
1) Meaning why would it return true if it doesn't exist?
You can have it return false if you want. That's just the default value for that key. You should set default to true/false depending on the data logic. For their particular case, setting the default true for that key goes with their app logic.
For example, AFAIK android sdk doesn't provide us with any apis that would say that app is running for the first time. So, to know that if the app is running for the first time or not, you could get the boolean value for a key like "is_first_run" and have the default value be true. If the app is run for the first time, then this value would not exist in shared preference because you've not set it yet. So, the default value true makes sense. And if you find it to be true then set it to false, so that the next time the app is run, it'll return false. If you clear the cache, the value will be true once again because the cache is destroyed and the variable no longer exists.
2) question what is operating mode 0 for SharedPreferences getSharedPreferences (String name, int mode)
Operating mode 0 is just the PRIVATE_MODE. The developer should have used the variable instead of just setting it to zero. Their are multiple modes for the sharedpreference. You can look into the docs for more. Private mode means that this data can be accessed by this application only.
3) How can I add a watch for getSharedPreferences (Where is it stored?)
Not sure what you mean by adding a watch. Are you talking about an event listener on the sharedPreference? AFAIK, you can't do that. Also, shared preference is stored in app's cache directory which can be /data/app/appname/cache or something else (idk for sure)
Shared Preferences store data till you delete and reinstall the app
getBoolean( "this is the key" , true);
if getBoolean returns null then this whole statement evaluates to true
If getBoolean( "this is the key" , false);
This means that if it returns null then evaluate it to false
Meaning why would it return true if it doesn't exist?
This was your question right ?
It is the coder's choice to make it true or false like
At first when user has logged in when you did not even touch sharedPreferences
then it would probably return the second argument
like
if(SharedPreferences.getBoolean('isUserOld' , true)){
Toast.make( Context , "Welcome to Our App" , Long).show()
}
Here it is checking whether user is old or not
If user is not old then if blocks gets executed because the second param returns true
And this indicates that a new user has logged in

Default SharedPreference value being set weirdly

Say in version 1, I do not have this SharedPreference value.
Now, I manually updated the APK to version 2. After that I re-boot my phone. However, when I adb pull the .xml file that was created, it is set as false.
SharedPreference as below:
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean optimus = prefs.getBoolean("prime", true);
I thought getBoolean sets the value as the default value it it does not exist (which doesn't. It's the first time it's being initialized).
This xml file is completely new and it did not exist before.
Why does this happen? Is there some native Android behavior I'm missing?
If you update the application (without first uninstalling), it will keep the preference that has been set in the previous version. So if "prime" was set to false previously in the old version then it will still be false in the new version, unless the old version is uninstalled first, before installing the new one. Uninstalling would delete the old preferences xml file.
Also, rebooting your phone will not clear preferences.
getBoolean(key, defaultValue) returns the defaultValue programmatically when this Java statement is executed if there is no value in the XML file for this key, so you won't find the defaultValue in the XML file you're talking about unless you call prefs.edit().putBoolean(key, myValue).apply()
Edit: In other words, prefs.getBoolean("prime", true) will always return true UNLESS you once called prefs.edit().putBoolean("prime", false).apply() at any point in time (from the time you installed the first version of the application and assuming you never uninstalled it or cleared data).

Why not use PreferenceManager.setDefaultValues(readAgain = true) in all apps?

Android Settings guide suggests to call PreferenceManager.setDefaultValues() with readAgain = false. In this case the defaults from preferences.xml are only loaded once when the app is launched for the 1st time (or after "clear data").
If a new preference is added, its android:defaultValue is not loaded, I checked.
So why nobody uses readAgain = true? Google results:
10 "PreferenceManager.setDefaultValues(this, R.xml.preferences, true)"
60k "PreferenceManager.setDefaultValues(this, R.xml.preferences, false)"
What are the drawbacks?
This parameter is typically set to false because this one ensure that default values are only set for shared preferences that are not initialized. Keeping this to false will ensure that you do not override the setting modified by the user. This field is useful if you allow your user to re-set all app settings to the initial app settings.

SharedPreferences not being updated

I am having an odd issue in which the SharedPreferences are not being updated upon returning to an app. Here's the scenario:
I have two projects that use the same shared preferences. Project1 and Project2. They are separate but related apps. They are signed with the same key and use sharedUserId to share information.
Project1 opens Project2.
Project2 retrieves the SharedPreferences file and writes to it via this method:
Context prefsContext = c.createPackageContext(packageNameOfProject1, Context.CONTEXT_IGNORE_SECURITY);
SharedPreferences prefs = prefsContext.getSharedPreferences(fileName, Context.MODE_PRIVATE);
SharedPreferences.editor editor = prefs.edit();
editor.putBool("bool1", value1);
editor.putBool("bool2", value2);
...
editor.putBool("boolN", valueN);
editor.apply();
Once that is done, I return to Project1 by calling finish().
Project1 then reads the data like so:
SharedPreferences prefs = getSharedPreferences(getPreferencesFileName(), Context.MODE_PRIVATE);
Boolean value1 = prefs.getBoolean(fileName, false);
Boolean value2 = prefs.getBoolean(fileName, false);
...
Boolean valueN = prefs.getBoolean(fileName, false);
Map<String, ?> mappings = prefs.getAll();
Set<String> keys = mappings.keySet();
for(String key : keys) {
log.d(TAG, "_____");
log.d(TAG, "Key = " + key);
log.d(TAG, "Value = " + mappings.get(key));
}
The problem is the values are not updated in Project1. I can tell based off the logs at the end that the file isn't even generating mappings. However, I can verify that the xml is being updated. If I force stop the app then restart it, all the mappings are there in Project1. All the values are correct. However, I need them updated when the user leaves Project2. I feel like there's something I'm missing here but can not spot it.
The only things I have been able to find on the subject is:
SharedPreferences.Editor not being updated after initial commit
SharedPreferences value is not updated
These don't help as I'm already doing that.
I have WRITE_EXTERNAL_STORAGE set in both manifests. The fileName is the same (else I wouldn't be able to read the file when I reenter the app).
EDIT:
I should note that I did try to do editor.commit() instead of editor.apply() as I thought I was facing a race condition. The problem still persisted. I'm thinking that for some reason, the old reference to the SharedPreference in Project1 is being used instead of a new one even though I'm lazy-loading it each time.
EDIT2:
Ok, to further test to see what id going on. I decided to try the opposite direction.
In Project1 I do:
Float testFloat (float) Math.random();
Log.d("TEST_FLOAT", "Project1: TEST_FLOAT = " + testFloat);
prefs.edit().putFloat("TEST_FLOAT", testFloat).commit();
In Project2 I do:
Log.d("TEST_FLOAT", "Project2: TEST_FLOAT = " + prefs.getFloat("TEST_FLOAT", 0.0f));
I then go back and forth between the two like so: Project1->Project2->Project1->Project2->Project1->Project2 and here is the logcat result:
Project1: TEST_FLOAT = 0.30341884
Project2: TEST_FLOAT = 0.30341884
Project1: TEST_FLOAT = 0.89398974
Project2: TEST_FLOAT = 0.30341884
Project1: TEST_FLOAT = 0.81929415
Project2: TEST_FLOAT = 0.30341884
In other words, it's reading and writing to the same file. However, it's keeping the mapping that it had when it was first opened it in the project. Even though I close the project, the mapping remains until the application is force stopped.
EDIT: I'm still getting upvotes on this answer even though it recommends a method that has since been deprecated. If you need consistent data through multi-process, then you need to use something other than SharedPreferences like a ContentProvider backed by a file system or database.
https://developer.android.com/reference/android/content/Context#MODE_MULTI_PROCESS
Final answer:
Replace
getSharedPreferences(fileName, Context.MODE_PRIVATE);
with
getSharedPreferences(fileName, Context.MODE_MULTI_PROCESS);
As per document:
Context.MODE_MULTI_PROCESS
SharedPreferences loading flag: when set, the file on disk will be
checked for modification even if the shared preferences instance is
already loaded in this process. This behavior is sometimes desired in
cases where the application has multiple processes, all writing to the
same SharedPreferences file. Generally there are better forms of
communication between processes, though.
This was the legacy (but undocumented) behavior in and before
Gingerbread (Android 2.3) and this flag is implied when targeting
such releases. For applications targeting SDK versions greater than
Android 2.3(Gingerbread), this flag must be explicitly set if desired.
I knew there was a simple oversight in this.
Try to call editor.commit(); instead of editor.apply();. Normally they should do the same, but I noticed there some weird behaviour sometimes.
From the SharedPreferences documentation the method "apply()" writes asynchronously (delayed) to the file and the method "commit()" writes the information synchronously (immediatly) to the file.
Also from the documentation, they say that you don't need to wary about the activity life cycle while using any of the above methods, as they ensure the "apply()" writes are completed before status changes, if they are running in the same system process.
However, as you are using two different projects, they run in two different processes and you can't be sure that "apply()" on project 2 will be concluded before the "onResume()" starts on project 1.
I suggest that you try "commit()" instead of "apply()" to force synchronous write. If this don't solve the issue, you can add a delay of a couple of seconds before reading the preferences in project 1, just to check if the issue is related to this delayed write.
--EDITED--
To debug the issue let's do the following:
1-In Eclipse Select/Add the view "File Explorer" and navigate to the directory:
/data/data/[your package name]/shared_prefs
your package name should be something like "com.myproject.shared"
2-Select the file with your saved preferences and press the button "download to PC".
3-Check if file contents match your expectations.
good luck.

Initializing Preferences

Android Guide recommends defining preferences in XML files, And from there, these can be loaded in PreferenceActivity/PreferenceFragment etc for viewing and editing by user. But in real scenario, User Interacts with other activities first, then (maybe) with Preferences UI.
What if the starter activity needs some of these preferences ? They'll be not loaded yet, because preferences resources has not been inflated yet. Is there a way to pre-access preferences in XML files ?
Yes. When you first request the preference you can provide it with a default value. E.g. if you are loading a preference of type Int, then you can do so in the following manner from an activity:
SharedPreferences defaultSettings = PreferenceManager.getDefaultSharedPreferences(this);
int preferenceValue = defaultSettings.getInt("PreferenceName", 7);
This would load your preferenceValue to be 7 (without this preference ever being initialized yet). This is assuming that in your XML preference file, you have a preference of key "PreferenceName". If you plan on editing this preference in the activity before the Preference activity has been ran, be sure you commit your changes with a SharedPreferenceEditor:
// ... change to preferenceValue occurs prior to this code
SharedPreferences.Editor defaultEditor = defaultSettings.edit();
defaultEditor.putInt("PreferenceName", preferenceValue);
defaultEditor.commit();
We probably want to avoid "PreferenceName" in a hardcoded matter though, and instead use it as a string in the strings.xml file. This way it can be grabbed both from the initial code when the preference has not been saved yet and from the Preference XML file as well. This means that our above code would substitute the string "PreferenceName" with something like the following:
getResources().getString(R.string.pref_name)
And in your Preference XML file you may would reference the key in the following way:
android:key="#string/pref_name"
android:defaultValue="7"
This should cover "pre-loading" the preference as well as trying to keep most of the application settings within one place. There may indeed be overlap in terms of whether or not the XML preference was created/loaded before the initial Activity occurred, but I haven't tested that out yet.
EDIT: It turns out instead of using the above code, you can directly load the XML file (with its default preference) by the following method:
PreferenceManager.setDefaultValues(this, R.xml.preference, false);
More information about this method can be found in the documentation for the PreferenceManager: http://developer.android.com/reference/android/preference/PreferenceManager.html
If you look at SharedPreference API, you will see this
getString(String key, String defValue)
So, you can actually in fact define a default value if it's not already existed.
Source: http://developer.android.com/reference/android/content/SharedPreferences.html
You can also predefine default value in XML using
android:defaultValue="SOMETHING"

Categories

Resources