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).
Related
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
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.
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.
I have an app that depends on SQLite for data which is populated by xmls shipped with the app in the assets folder.
When you run the app the first time it sets a shared preference config_run = false.
then i check if config_run = false then parse the xml and dump the data into db
set config_run = true
Now i realize that when i have to push an update on Google Play and add more content into the XML. Even though i change the database version from 1 to 2. The import script wont run because shared preference config_run value will be set to true.
Any pointers on how to handle this situation ?
Scenarios
First Instal - Ver = 1, DB V = 1 (Data is parsed and dumped into the database)
Bugs Fixed and push and update but no data has changed - ver = 1.1, DB V = 1 (It should just replace the code and not upgrade or re-create the database)
Upgraded the DATA and pushed a new update - ver 1.2, DB = 2 ( No new code but data has to be re-created)
The Flow of My App
The App Starts Splash Activity. If Shared Pref - config_run is equal to false then it starts a Progress Dialog and parses and dumps the data into the database.
Upon Parsing and Creating DB and dumping data it goes to MainActivity.
Second Case
SplashActivity Runs and config_run = true so directly goes to MAin Activity.
As Suggested by few people below if i try to dumb the data into the database in onUpgrade of the SQLiteHelper it will happen only in MAinActivity as i dont open a Db connection in the SplashActivity and the Dialog Progress wont be displayed also.
Add a shared pref of the version number you last ran the script for.
On app start, check the current apk version, and if newer, run the script again and update the pref
Why dont you want use built in sqlite versioning system. DB version is independed from app version. And it does exactly what you want. SQLiteOpenHelper? Every time tou change your db version an onUpgrate callback will be called and you can refill your db. There are a lot of examples.
Instead of setting your shared pref (config_run) to false and making it true, just set the database version into it. When you update your app, check whether you have the same version number in your shared pref. You can do this as shown below:
configRun = settings.getInt("database_version", 0);
if ((DBAdapter.DATABASE_VERSION) == configRun)
{
//skip xml parsing
}
else
{
//first time configRun will be "0" and DBAdapter.DATABASE_VERSION will be 1
// so you need to parse your xml here and set configRun =1
//on update, change your DB version to 2. now again your configRun and DBAdapter.DATABASE_VERSION will mismatch and you can parse your xml.
}
Have your xmlfiles end with the date of the update, and store the last updated date in sharedpref.
On launch you can check search for updates ( in an optimized way ) and if you find a new file with a new date when compared to the last time you know you need to dump the file.
Total hack job :D
Two things you can do:
The right way: Override database provider's onUpdate() to import the file. (as suggested above)
The one line changer: Instead of check for key="config_run", you check and set for key=("config_run"+DB_VERSION) to see if import is needed, and of course, if the key does not exist, you should return false.
This way every time you update the DB number, import job will run again.
This is agnostic to your app version.
EDIT: The problem described below was due to a very peculiar device issue not caused by any coding-related problem.
I have a preferenceActivity in which I have many checkBoxPreferences.
The checkBoxPreference is suppose to save the the default shared preferences file, and then be called again when I open the app in order to update the UI.
This does not happen like it's suppose to.
If I close the app and open it back up, my values remain like they are suppose to, but if I use task manager to end the app or if I power cycle the phone (when the app is not running) then the defaultValues are called again.
So, I created a SharedPreference in my onResume() to test it.
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
I then check to see if there is a key in that sharedpreference.
pref.contains("myCheckBoxPreference");
When I close out and open it back up, it returns true. if I close with the task manager or power cycle the phone off and on, then that returns false.
So, I tried manually setting the SharedPreference
SharedPreferences.Editor editor = pref.edit();
editor.putBoolean("myCheckBoxPreference", myCheckBoxPreference.isChecked());
editor.commit();
and then I called that when the checkboxpreference value changed. I also tried calling it in onStop and onPause. Still, if I close the app and open it back up, pref.contains returns true, but if I power cycle the phone off and back on, it returns false.
So I then tried using a SharedPreferences file.
In the class declaration:
public static final String PREFS = "prefs";
And in the onResume():
SharedPreferences pref = this.getSharedPreferences(PREFS, 0);
Same behavior, pref.contains still returns true if I just close the app and open it back up, but it returns false if I power the phone off and back on.
I then tried changing the key value of myCheckBoxPreference to something that did NOT match the xml key for the CheckBoxPreference, and it still had the same effect.
And I uninstalled the application from the phone, then powered the phone off and back on, and then re-installed, and it still has the same effect.
I just solved it, I'm pretty sure. There's no code error on my part, and there is no issue with my app whatsoever (I don't believe, anyway.)
I created a new project called "testproj", then I copied ALL the code from my settings PreferenceActivity, pasted it into the TestprojActivity, and I copied the code from the xml it relied on, then pasted that into the main.xml of TestProj.
I then installed TestProj on the Samsung Captivate, changed the settings around, cleared the ram through RAM management (a function of the custom ROM I have), and the settings stuck. I then power cycled the phone and the settings were still there like I'd configured them.
They stayed both when I manually set them using:
PreferenceManager.getDefaultSharedPreferences();
and without manually saving them to the SharedPreferences.
Since it is not my phone, I haven't tried it yet, but I assume a Factory Data reset would fix it completely
EDIT: I was able to test on both a new Samsung Captivate and a Samsung infuse, and it worked.
I wasted a lot of my time trying to figure this out, and I hope it helps someone else. :)
I encountered a possibly similar problem on a Samsung Galaxy S, where the permissions for the preferences XML file had somehow changed/corrupted.
The log revealed some host process was failing to read the file, causing all the settings to reset to their defaults. I don't recall the exact error message, but it was along the lines of "permission denied for /path/to/preferences/file.xml".
The resolution for me was to delete the application data through Settings, Applications, Manage Applications, MyApp, Delete data. This deletes the preference file associated with the app and the problem instantly disappeared.
I assumed it was an isolated event, as I've not run into it again on a variety of Android devices (including the Galaxy S II).
On the client's main test device I came across the very same issue. The Device used is a Samsung Galaxy S with SDK level 8 (2.2.1).
The strange behavior is that either SharedPreferences are not saved, or, as after a factory reset, they're too persistent, that is to say they are not deleted after having reinstalled the application.
Due to the current distribution of 2.2.x, and the number of Samsung Galaxy S devices sold being several millions, the probability of an occurrence of this issue is significant.
So it can be considered as crucial to implement a workaround for saving preferences.
For collecting detailed characteristics to isolate this workaround in a sharp-edged way, could everyone who is also facing that issue please provide the corresponding kernel version (System.getProperty("os.version") here?
I was thinking of something like this:
// !! I know that 2.6.32.9 is not yet correct. This would be a false positive !!
if ((System.getProperty("os.version").startsWith("2.6.32.9"))
&& (android.os.Build.DEVICE.contains("GT-I9000")))
useInternalStorage();
else
useSharedPreferences();
I can post the real code here also once it's ready and someone is interested.
EDIT: some additional information:
Devices facing that issue:
Property | Values
---------------------------------+------------------------------------
Build.DEVICE | "GT-I9000T"
Build.VERSION.INCREMENTAL | "UBJP9"
Build.VERSION.RELEASE | "2.2.1"
Build.VERSION.SDK | 8
System.getProperty("os.version") | "2.6.32.9"
Similar devices NOT facing that issue:
Property | Values
---------------------------------+------------------------------------
Build.DEVICE | "GT-I9000"
Build.VERSION.INCREMENTAL | "AOJP4"
Build.VERSION.RELEASE | "2.2"
Build.VERSION.SDK | 8
System.getProperty("os.version") | "2.6.32.9"
Try clearing the editor before you set your values. I had the same problem and it worked for me.
Example:
Editor e = PreferenceManager.getDefaultSharedPreferences(getParent()).edit();
e.clear();
e.putStringSet(key, value);
It is possible to work around the issue of permissions by using sharedUserId which should be the same for any of your signed apps.
http://developer.android.com/reference/android/R.attr.html#sharedUserId
I too had a problem with saving and then retrieving data. I had my Save and Load code in a class that extends Application because I wanted a single instance of my data. I could see the String being saved, no errors in LogCat and yet when I try to load it, again with no error, my String is empty. I never checked whether the data actually went into the file so I have no idea whether there was a failure on Save or Load or both.
My code was more or less as follows: (comboToSave is simply a string generated by Gson from a simple data class)
in one method to save:
SharedPreferences sharedPref = activity.getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString(getString(R.string.prefCombos), comboToSave);
editor.commit();
in another method to load:
SharedPreferences sharedPref = activity.getPreferences(Context.MODE_PRIVATE);
String loadedComboText = sharedPref.getString(getString(R.string.prefCombos), "");
After lots of head scratching and not knowing what to do I changed the code that retrieves the sharedPref value from
SharedPreferences sharedPref = activity.getPreferences(Context.MODE_PRIVATE);
to
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
There is some more here on the difference between the two (although in my case it seems exactly the same)
Still the result on my Galaxy S3 was the same. However, testing both versions on other devices, including VDs (virtual devices) worked.
In the first version of the code I passed the Activity from the calling activity; for save from the activity where the final bit of data is collected from the user, and for loading from my main activity so that I had it ready when the app is started.
I played with uninsalling the app and re-intalling, turning the device off and on again last night to no avail.
I have now moved the save and load methods from the application class to the activity where I complete the input i.e. both load and save code is now in the same activity. I have tested this with both variations of the code and they both work. I get back what I save. I then moved all the code back to the Application class and it works; this leads me to believe that somehow with all the installing/uninstalling I somehow managed to get it working. Point is: the code is correct - if it does not work the device and/or settings are probably to blame
I have the same problem, and i suffered from it for a while , finally i found the solution ,
and it is so easy , just pass the direct reference of the activity , and do not use any general context
public SessionManagment(Activity mContextActivity){
// this.contextActivity = mContext;
sharedPrefSession = mContextActivity.getSharedPreferences(
Constants.SHARED_PREFERANCES_LIGHT_TIGER_SESSION_FILE_NAME,
Context.MODE_PRIVATE);
}//enden constructor
the code above is the constructor of the class that i have written for session management , and
and when i call it in the code in the main ActivityFramgment in a AsyncTask i call it like this
SessionManagment sessionManagment = new SessionManagment(referanct2thisActivity);
where referanct2thisActivity is defined in "onCreate" function of fragment activity like this
referanct2thisActivity = this;
hope that will help others in the future