I am currently working on an app with SharedPreferences and I am trying to dynamically change the value that appears on the Prefs activity screen.
On the screen I have a preferencelistview which contains months of the year and two edit boxes to store info in relation to chosen month. Upon selection of a month, I want a few things to happen:
Inserts a row in a database with the selected month (if it had not been previously selected) -- This I can get to work just peachy
Changes the values of the sharedpreference edit boxes to the values stored in the row of the database --this is where I am having trouble
When the editboxes are updated, they should update the corresponding row in the db based on the selected month -- working without issue.
So, basically, my problem is with item#2. I have a function that checks when a new month is selected from the list:
public void setMonth(SharedPreferences sp) {
ContentValues cv = new ContentValues();
cv.clear();
int sel_month = Integer.valueOf(sp.getString("pActivityMonth", String.valueOf(this.month))); //listvew works with strings, convert to int
int stored_new = 0; //default value
int stored_ren = 0; //default value
Log.d(TAG, String.valueOf(sel_month));
String[] col = { Database.C_MONTH, Database.C_QNEW, Database.C_QRENEW };
String[] arg = { String.valueOf(sel_month) };
Cursor c = db.query(Database.RR_TABLE, col, Database.C_MONTH + "=?", arg, null, null, null);
if (c.getCount()<1) { //New month, insert row
cv.put(Database.C_MONTH, sel_month);
db.insertWithOnConflict(Database.RR_TABLE, null, cv, SQLiteDatabase.CONFLICT_IGNORE);
} else { //row exists, get stored values
Log.d(TAG, "cursor count: " + String.valueOf(c.getCount()));
c.moveToFirst();
stored_new = c.getInt(c.getColumnIndex(Database.C_QNEW));
stored_ren = c.getInt(c.getColumnIndex(Database.C_QRENEW));
Log.d(TAG, "stored_new: " + String.valueOf(stored_new));
}
//Change edittext preferences to stored values
editor.putString("pNewQuota", String.valueOf(stored_new));
editor.putString("pRenewQuota", String.valueOf(stored_ren));
editor.apply();
editor.commit();
}
So, I select a new month (july), click on one of the edittext options to update the info (lets say I entered 20), then select a different month (August), with a default value of 0. If I then click on the edittext again, the dialog continues to show 20 as opposed to 0. If I change this 0 to something else, it stores in the correct row of the database, but then this value continues to persist after changing to another month (say, back to July).
My Logcat shows that the function is grabbing the correct values, and that the putString is being run. Is the PreferenceScreen not able to refresh itself? Thanks for any help.
If I understand correctly, you appear to have a PreferenceActivity which is using both SharedPreferences and an SQLiteDatabase to persist/save the same data. While a PreferenceActivity seems like a pre-built ListView designed for data entry, its actually a lot more: the PreferenceActivity will automatically save and load the Preferences to SharedPreferences, which is a special datastore assigned to your application and managed by the Android OS.
Although intended to store preferences, you could use SharedPreferences to persist some data if you want; either automatically via Preferences, or manually with calls to an editor. You can interrupt and/or modify this persistence in a number of ways, such as creating a preference subclass to replace the behaviors, the use of an OnPreferenceChangeListener to modify behaviors, or toggling persistence via a call to setPersistent(boolean).
An alternative to using PreferenceActivity in this way would be to create an Activity with an AdapterView, or perhaps a ListActivity, and use SimpleCursorAdapter to automate binding the data to the view.
To answer your question regarding persisting changes to SharedPreferences in a PreferenceActivity, I've provided some roughly outlined some sample code in context and outlined a couple potential trouble spots. It is not intended to represent a usable solution so much as guve an idea of what you'll need and where, in what I hope is a clear (if verbose) manner that shouldn't be too difficult to adapt to your project.
/* Preference value modification example.
* This is a code sample with some context, not a solution.
* #author codeshane.com
* */
public class StackOverflow_Question_11513113 extends PreferenceActivity {
/*
*/
class MyPreferenceActivity extends PreferenceActivity {
//Preference keys that were either set in xml, or possibly in source via .setKey():
private final String pref_group_key = "some_prefs_key";
private final String list_pref_key = "some_list_key";
private final String edittext_pref_key = "some_edittext_key";
private PreferenceActivity mPreferenceActivity; /* the activity */
private SharedPreferences mSharedPreferences; /* auto-saving preferences */
private Editor mEdit;
private PreferenceScreen mPreferenceScreen; /* root of the preference hierarchy */
private PreferenceGroup mPreferenceGroup; /* optional grouping of preferences */
private ListPreference mListPreference; /* a pop-up selectable-items-list */
private EditTextPreference mEditTextPreference; /* a pop-up text entry box */
private int stored_new;
private int stored_ren;
/* ... */
public void setMonth() { /*SharedPreferences sp*/
/* ... */
//int stored_new = 0; //default value
//int stored_ren = 0; //default value
/* ... */
/* Should only call either apply() or commit(), not both; calling a .commit() after an .apply() can block the thread (always fun),
* not to mention they essentially do the same thing, just synchronously vs asynchronously.
* http://developer.android.com/reference/android/content/SharedPreferences.Editor.html#apply()
* */
// mEdit.apply(); //API level 9+, void, asynchronous.
// mEdit.commit(); //API level 1+, returns boolean, synchronous.
}
/* ... */
#Override
public void onCreate(Bundle savedInstanceState) {
/* Called once when the Activity is created. */
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.affirm_prefs);
/* ... */
}
#Override
protected void onResume() {
super.onResume();
/* Called when the Activity is created or comes to foreground. */
if (null==mSharedPreferences){init(this);}
mSharedPreferences.registerOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener);
/* manually load data from shared preferences that IS NOT REPRESENTED
* by a Preference already, or for which Persistent has been manually
* set to false. */
stored_new = mSharedPreferences.getInt("stored_new", 0); //If not found, assigns a default value of 0.
stored_ren = mSharedPreferences.getInt("stored_ren", 0); //If not found, assigns a default value of 0.
}
#Override
protected void onPause() { /* Called when the Activity goes to background. */
super.onPause();
mSharedPreferences.unregisterOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener);
/* manually save data to shared preferences that IS NOT REPRESENTED
* by a Preference already, or for which Persistent has been manually
* set to false. */
mEdit.putInt("stored_new", stored_new);
mEdit.putInt("stored_ren", stored_ren);
mEdit.commit();
}
private boolean init(PreferenceActivity activity){
mPreferenceActivity = activity; // or PreferenceActivity.this;
mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
mPreferenceScreen = mPreferenceActivity.getPreferenceScreen();
mPreferenceGroup = (PreferenceGroup)mPreferenceScreen.findPreference(pref_group_key);
mListPreference = (ListPreference)mPreferenceScreen.findPreference(list_pref_key);
mEditTextPreference = (EditTextPreference)mPreferenceScreen.findPreference(edittext_pref_key);
return true;
}
/* When registered to a preference, this listener is notified of changes before saving.
* */
OnPreferenceChangeListener onPreferenceChangeListener = new OnPreferenceChangeListener(){
public boolean onPreferenceChange(Preference preference, Object newValue) {
boolean persistNewValue = true;
/* This occurs when a user has changed a preference, but before it is persisted */
/* this is a good place for your "setMonth" logic,
* at least in a PreferenceActivity*/
updateElement(preference, newValue);
changeMoreStuffBonusMethod(preference);
return persistNewValue;
}};
/* When registered to SharedPreferences, this listener is notified of all saves to SharedPreferences, even if the data is the same
* */
OnSharedPreferenceChangeListener onSharedPreferenceChangeListener = new OnSharedPreferenceChangeListener(){
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
}
};
private boolean updateElement(Preference prefUpdating, Object value){
if (null==prefUpdating) return false;
/* This function is for manually changing the value before it
* is saved by the PreferenceActivity. */
/* ... */
/* Can match exact instances, such as our mListPreference */
if (mListPreference.equals(prefUpdating)) {
/* set selected item by item's index id */
mListPreference.setValueIndex(0);
/* or set selected item by item's key*/
mListPreference.setValue("item_key");
}
/* Can also match by Class, such as any EditTextPreference */
if (prefUpdating instanceof EditTextPreference){
EditTextPreference etp = (EditTextPreference)prefUpdating;
String oldText = etp.getText();
etp.setText("Some new text in edit text box"); /* or */
etp.setText(String.valueOf(stored_new)); /* or */
etp.setText(oldText.trim()); /* remove leading/trailing whitespace */
etp.setText(dataModel.get(prefUpdating.getKey())); /* if you use a model.. */
}
/* ... */
}
private void changeMoreStuffBonusMethod(Preference prefUpdating){
/* Change the preference text */
prefUpdating.setTitle("Click Me!");
prefUpdating.setSummary("I am a cool preference widget!");
/* Change the pop-up list (not items) text */
if (prefUpdating instanceof ListPreference){
ListPreference lp = (ListPreference)prefUpdating;
lp.setDialogTitle("I am the pop-up.");
lp.setDialogMessage("Select something from my list.");
}
/* If persistent, PreferenceActivity should handle saving: */
Log.v(TAG,prefUpdating.getClass().getSimpleName()+".isPersistent="+String.valueOf(mListPreference.isPersistent()));
}
}
}
Related
I am currently developing an android app. I need to do something when the app is launched for the first time, i.e. the code only runs on the first time the program is launched.
You can use the SharedPreferences to identify if it is the "First time" the app is launched.
Just use a Boolean variable ("my_first_time") and change its value to false when your task for "first time" is over.
This is my code to catch the first time you open the app:
final String PREFS_NAME = "MyPrefsFile";
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
if (settings.getBoolean("my_first_time", true)) {
//the app is being launched for first time, do something
Log.d("Comments", "First time");
// first time task
// record the fact that the app has been started at least once
settings.edit().putBoolean("my_first_time", false).commit();
}
I suggest to not only store a boolean flag, but the complete version code.
This way you can also query at the beginning if it is the first start in a new version. You can use this information to display a "Whats new" dialog, for example.
The following code should work from any android class that "is a context" (activities, services, ...). If you prefer to have it in a separate (POJO) class, you could consider using a "static context", as described here for example.
/**
* Distinguishes different kinds of app starts: <li>
* <ul>
* First start ever ({#link #FIRST_TIME})
* </ul>
* <ul>
* First start in this version ({#link #FIRST_TIME_VERSION})
* </ul>
* <ul>
* Normal app start ({#link #NORMAL})
* </ul>
*
* #author schnatterer
*
*/
public enum AppStart {
FIRST_TIME, FIRST_TIME_VERSION, NORMAL;
}
/**
* The app version code (not the version name!) that was used on the last
* start of the app.
*/
private static final String LAST_APP_VERSION = "last_app_version";
/**
* Finds out started for the first time (ever or in the current version).<br/>
* <br/>
* Note: This method is <b>not idempotent</b> only the first call will
* determine the proper result. Any subsequent calls will only return
* {#link AppStart#NORMAL} until the app is started again. So you might want
* to consider caching the result!
*
* #return the type of app start
*/
public AppStart checkAppStart() {
PackageInfo pInfo;
SharedPreferences sharedPreferences = PreferenceManager
.getDefaultSharedPreferences(this);
AppStart appStart = AppStart.NORMAL;
try {
pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
int lastVersionCode = sharedPreferences
.getInt(LAST_APP_VERSION, -1);
int currentVersionCode = pInfo.versionCode;
appStart = checkAppStart(currentVersionCode, lastVersionCode);
// Update version in preferences
sharedPreferences.edit()
.putInt(LAST_APP_VERSION, currentVersionCode).commit();
} catch (NameNotFoundException e) {
Log.w(Constants.LOG,
"Unable to determine current app version from pacakge manager. Defenisvely assuming normal app start.");
}
return appStart;
}
public AppStart checkAppStart(int currentVersionCode, int lastVersionCode) {
if (lastVersionCode == -1) {
return AppStart.FIRST_TIME;
} else if (lastVersionCode < currentVersionCode) {
return AppStart.FIRST_TIME_VERSION;
} else if (lastVersionCode > currentVersionCode) {
Log.w(Constants.LOG, "Current version code (" + currentVersionCode
+ ") is less then the one recognized on last startup ("
+ lastVersionCode
+ "). Defenisvely assuming normal app start.");
return AppStart.NORMAL;
} else {
return AppStart.NORMAL;
}
}
It could be used from an activity like this:
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
switch (checkAppStart()) {
case NORMAL:
// We don't want to get on the user's nerves
break;
case FIRST_TIME_VERSION:
// TODO show what's new
break;
case FIRST_TIME:
// TODO show a tutorial
break;
default:
break;
}
// ...
}
// ...
}
The basic logic can be verified using this JUnit test:
public void testCheckAppStart() {
// First start
int oldVersion = -1;
int newVersion = 1;
assertEquals("Unexpected result", AppStart.FIRST_TIME,
service.checkAppStart(newVersion, oldVersion));
// First start this version
oldVersion = 1;
newVersion = 2;
assertEquals("Unexpected result", AppStart.FIRST_TIME_VERSION,
service.checkAppStart(newVersion, oldVersion));
// Normal start
oldVersion = 2;
newVersion = 2;
assertEquals("Unexpected result", AppStart.NORMAL,
service.checkAppStart(newVersion, oldVersion));
}
With a bit more effort you could probably test the android related stuff (PackageManager and SharedPreferences) as well.
Anyone interested in writing the test? :)
Note that the above code will only work properly if you don't mess around with your android:versionCode in AndroidManifest.xml!
Another idea is to use a setting in the Shared Preferences. Same general idea as checking for an empty file, but then you don't have an empty file floating around, not being used to store anything
I solved to determine whether the application is your first time or not , depending on whether it is an update.
private int appGetFirstTimeRun() {
//Check if App Start First Time
SharedPreferences appPreferences = getSharedPreferences("MyAPP", 0);
int appCurrentBuildVersion = BuildConfig.VERSION_CODE;
int appLastBuildVersion = appPreferences.getInt("app_first_time", 0);
//Log.d("appPreferences", "app_first_time = " + appLastBuildVersion);
if (appLastBuildVersion == appCurrentBuildVersion ) {
return 1; //ya has iniciado la appp alguna vez
} else {
appPreferences.edit().putInt("app_first_time",
appCurrentBuildVersion).apply();
if (appLastBuildVersion == 0) {
return 0; //es la primera vez
} else {
return 2; //es una versiĆ³n nueva
}
}
}
Compute results:
0: If this is the first time.
1: It has started ever.
2: It has started once, but not that version , ie it is an update.
Here's some code for this -
String path = Environment.getExternalStorageDirectory().getAbsolutePath() +
"/Android/data/myapp/files/myfile.txt";
boolean exists = (new File(path)).exists();
if (!exists) {
doSomething();
}
else {
doSomethingElse();
}
There is support for just this in the support library revision 23.3.0 (in the v4 which means compability back to Android 1.6).
In your Launcher activity, first call:
AppLaunchChecker.onActivityCreate(activity);
Then call:
AppLaunchChecker.hasStartedFromLauncher(activity);
Which will return if this was the first time the app was launched.
You can use Android SharedPreferences .
Android SharedPreferences allows us to store private primitive
application data in the form of key-value pair .
CODE
Create a custom class SharedPreference
public class SharedPreference {
android.content.SharedPreferences pref;
android.content.SharedPreferences.Editor editor;
Context _context;
private static final String PREF_NAME = "testing";
// All Shared Preferences Keys Declare as #public
public static final String KEY_SET_APP_RUN_FIRST_TIME = "KEY_SET_APP_RUN_FIRST_TIME";
public SharedPreference(Context context) // Constructor
{
this._context = context;
pref = _context.getSharedPreferences(PREF_NAME, 0);
editor = pref.edit();
}
/*
* Set Method Generally Store Data;
* Get Method Generally Retrieve Data ;
* */
public void setApp_runFirst(String App_runFirst)
{
editor.remove(KEY_SET_APP_RUN_FIRST_TIME);
editor.putString(KEY_SET_APP_RUN_FIRST_TIME, App_runFirst);
editor.apply();
}
public String getApp_runFirst()
{
String App_runFirst= pref.getString(KEY_SET_APP_RUN_FIRST_TIME, "FIRST");
return App_runFirst;
}
}
Now Open Your Activity & Initialize .
private SharedPreference sharedPreferenceObj; // Declare Global
Now Call this in OnCreate section
sharedPreferenceObj=new SharedPreference(YourActivity.this);
Now Checking
if(sharedPreferenceObj.getApp_runFirst().equals("FIRST"))
{
// That's mean First Time Launch
// After your Work , SET Status NO
sharedPreferenceObj.setApp_runFirst("NO");
}
else
{
// App is not First Time Launch
}
You could simply check for the existence of an empty file, if it doesn't exist, then execute your code and create the file.
e.g.
if(File.Exists("emptyfile"){
//Your code here
File.Create("emptyfile");
}
If you are looking for a simple way, here it is.
Create a utility class like this,
public class ApplicationUtils {
/**
* Sets the boolean preference value
*
* #param context the current context
* #param key the preference key
* #param value the value to be set
*/
public static void setBooleanPreferenceValue(Context context, String key, boolean value) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
sp.edit().putBoolean(key, value).apply();
}
/**
* Get the boolean preference value from the SharedPreference
*
* #param context the current context
* #param key the preference key
* #return the the preference value
*/
public static boolean getBooleanPreferenceValue(Context context, String key) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
return sp.getBoolean(key, false);
}
}
At your Main Activity, onCreate()
if(!ApplicationUtils.getBooleanPreferenceValue(this,"isFirstTimeExecution")){
Log.d(TAG, "First time Execution");
ApplicationUtils.setBooleanPreferenceValue(this,"isFirstTimeExecution",true);
// do your first time execution stuff here,
}
I made a simple class to check if your code is running for the first time/ n-times!
Example
Create a unique preferences
FirstTimePreference prefFirstTime = new FirstTimePreference(getApplicationContext());
Use runTheFirstTime, choose a key to check your event
if (prefFirstTime.runTheFirstTime("myKey")) {
Toast.makeText(this, "Test myKey & coutdown: " + prefFirstTime.getCountDown("myKey"),
Toast.LENGTH_LONG).show();
}
Use runTheFirstNTimes, choose a key and how many times execute
if(prefFirstTime.runTheFirstNTimes("anotherKey" , 5)) {
Toast.makeText(this, "ciccia Test coutdown: "+ prefFirstTime.getCountDown("anotherKey"),
Toast.LENGTH_LONG).show();
}
Use getCountDown() to better handle your code
FirstTimePreference.java
for kotlin
fun checkFirstRun() {
var prefs_name = "MyPrefsFile"
var pref_version_code_key = "version_code"
var doesnt_exist: Int = -1;
// Get current version code
var currentVersionCode = BuildConfig.VERSION_CODE
// Get saved version code
var prefs: SharedPreferences = getSharedPreferences(prefs_name, MODE_PRIVATE)
var savedVersionCode: Int = prefs.getInt(pref_version_code_key, doesnt_exist)
// Check for first run or upgrade
if (currentVersionCode == savedVersionCode) {
// This is just a normal run
return;
} else if (savedVersionCode == doesnt_exist) {
// TODO This is a new install (or the user cleared the shared preferences)
} else if (currentVersionCode > savedVersionCode) {
// TODO This is an upgrade
}
// Update the shared preferences with the current version code
prefs.edit().putInt(pref_version_code_key, currentVersionCode).apply();
}
Why not use the Database Helper ? This will have a nice onCreate which is only called the first time the app is started. This will help those people who want to track this after there initial app has been installed without tracking.
I like to have an "update count" in my shared preferences. If it's not there (or default zero value) then this is my app's "first use".
private static final int UPDATE_COUNT = 1; // Increment this on major change
...
if (sp.getInt("updateCount", 0) == 0) {
// first use
} else if (sp.getInt("updateCount", 0) < UPDATE_COUNT) {
// Pop up dialog telling user about new features
}
...
sp.edit().putInt("updateCount", UPDATE_COUNT);
So now, whenever there's an update to the app that users should know about, I increment UPDATE_COUNT
My version for kotlin looks like the following:
PreferenceManager.getDefaultSharedPreferences(this).apply {
// Check if we need to display our OnboardingSupportFragment
if (!getBoolean("wasAppStartedPreviously", false)) {
// The user hasn't seen the OnboardingSupportFragment yet, so show it
startActivity(Intent(this#SplashScreenActivity, AppIntroActivity::class.java))
} else {
startActivity(Intent(this#SplashScreenActivity, MainActivity::class.java))
}
}
Hi guys I am doing something like this. And its works for me
create a Boolean field in shared preference.Default value is true
{isFirstTime:true}
after first time set it to false. Nothing can be simple and relaiable than this in android system.
I'm currently writing a major upgrade to my language teaching App, and wanted to use SharedPreferences in order to store user progress. Basically, a recyclerview displays a list of topics, which lead to exercises -complete 5 exercises, and the topic is finished.
At this point, I call saveProgress(), and store the topic number and an int representing completion (1) in sharedPreferences.
However, how and where in the recylclerView code should I call the check for a topic being completed? Currently in my bindData method of my ViewHolder inner class, I have:
SharedPreferences sp = getActivity().getSharedPreferences("phraseprefs", Context.MODE_PRIVATE);
//the number topic serves as the key name of the preference
int completed = sp.getInt(Integer.toString(mTopic.topicRef), 0);
if (completed ==1){
//we check if exercise completed, if so, make view greyed out
float alpha= 0.65f;
nameTextView.setAlpha(alpha);
nameTextView.setBackgroundColor(Color.GREY);
itemView.setAlpha(alpha);
itemView.setBackgroundColor(Color.GREY);
}
This doesnt seem to work however. Any tips would be appreciated!
EDIT: Added in SaveProgress() method below, note that "topic" in this context is a number from 0-xxx, at increments of 5 per topic (i.e. topic 0,5,10, with exercises 0-4,5-9,10-14, respectively)
public void saveProgress(){
SharedPreferences sp = getActivity().getSharedPreferences("phraseprefs", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
//we use 1 to represent completion instead of boolean because we can also sum these numbers
//for a completion percentage if necessary
editor.putInt(Integer.toString(topic), 1);
editor.commit();
}
EDIT2: Solved by a mixture of trial and error and magic.
First, initialize the textViews in ViewHolder constructor.
Second, Fetch sharedPreferences in OnBindViewHolder
Third, call methods to change the textViews within onBindViewHolder, provided the shared Preference meets your conditions.
Thank you all
I think RecyclerView shouldn't know about SharedPreferences at all. You probably have a model class Topic which contains all the information about a topic. You can include your completion int in this class. Something like this.
class Topic {
/* all topic data */
private int completion;
public int getCompletetion() {
return compeletion;
}
public void setCompletion(int completion) {
this.completion = completion;
}
public Topic(/*Other Topics fields*/) {
}
public Topic(/*Other Topic fields*/, int completion) {
/* initialization of other fields */
this.completion = completion;
}
}
Then you can load and set a completion int for each Topic in your array (I mean List or whatever container you use). Something like this
List<Topic> topicsList = new ArrayList<>();
/* add your topics to the list from whatever source */
for (Topic topic : topicsList) {
topicsList.setCompletion(loadCompletion(topic));
}
YourAdapter adapter = new YourAdapter(topicsList)
/* ... */
Now, in order to check completion int for a Topic in your data array in your RecyclerView you can just call topic.getCompletion(). It would look something like this
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
Topic topic = topicsList.get(position);
int completion = topic.getCompletion();
switch (completion) {
case '0':
holder.modifyViewForCompletionZero();
break;
case '1':
holder.modifyViewForCompletionOne();
break;
/* Call holder methods for other completion states */
}
}
And your inner ViewHolder would contain methods for adjusting view for each state.
This question gets thrown around here a lot. However, my side of the implementation is buggy, so it would help if you guys can help me out. Thanks in advance. Sorry if this question is so noob-like.
I develop Court Counter, which can be found here. I recently started to add saving support for my app. However, I'm not sure if I'm doing it correctly. All other stack overflow topics mention Editor, however Android Studio corrects me to SharedPreferences.Editor. I'm assuming this changed as of Marshmallow or whatever.
Anyway, I added these lines of code for saving and loading:
/**
* Saves your current session
* SharedPreferences key = courtCounter
* team A key = teamA
* team B key = teamB
*/
public void saveSession(View v) {
//We will get saved preferences!
SharedPreferences prefs = this.getSharedPreferences("courtCounter", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putInt("teamA",scoreA);
editor.putInt("teamB",scoreB);
editor.commit();
}
/**
* Loads previous session
* The config is same as saveSession
*/
public void loadSession(View v) {
//We will get the preferences!
SharedPreferences prefs = this.getSharedPreferences("courtCounter", Context.MODE_PRIVATE);
scoreA = prefs.getInt("teamA", 0);
scoreB = prefs.getInt("teamB", 0);
}
saveSession is called when the score gets updated, which you can see below:
/**
* New way of adding score
*
* #param score Score that you need to add to the team
* #param team Team that you want the score added to
* #param last Last clicked item, for example, a1. This will be used for the undo action.
*/
public void scoreAdd(int score, char team, String last) {
Button undo = (Button) findViewById(R.id.undo_button);
undo.setEnabled(true);
lastClicked = last;
if (team == 'a') {
scoreA += score;
displayScore(scoreA, 'a');
}
if (team == 'b') {
scoreB += score;
displayScore(scoreB, 'b');
}
saveSession();
}
However, this throws an error, saying that I didn't supply a (view) or whatever in the brackets. How can I fix this? I don't need any input handlers, but Android Studio would freak out if I didn't make one.
Right now, the code works fine over here:
public void resetAll(View view) {
Button undo = (Button) findViewById(R.id.undo_button);
undo.setEnabled(false);
lastClicked = "reset";
scoreA = 0;
scoreB = 0;
displayScore(scoreA, 'a');
displayScore(scoreB, 'b');
saveSession(view);
}
I'm assuming this is because the resetAll has a view input parameter. However, resetAll is called from an onClick.
Other places where code does not work include the onCreate.
Any ideas?
Your saveSession is not even using the View v within the method, why are you making it as a parameter? Remove it.
public void saveSession() {
...
}
public void resetAll(View view) {
...
saveSession(); // I remove the view here.
}
You have a lot of unnecessary params, unnecessary since your method doesn't even use it.
SharedPreferences doesn't work correct in one existing apps. I tried many different ways but still not working. Always get default values app start again.
It's working when I use same code in created new app.
It's working all of other existing apps.
Do you know why?
String default_user = "Default_User";
SharedPreferences pref = this.getSharedPreferences("TEST_SHAREDPREF", MODE_PRIVATE);
String user = pref.getString("user", default_user);
Log.d("SHARED CHECK", user);
if (user.equals(default_user)) {
SharedPreferences.Editor edit = pref.edit();
edit.putString("user", "new_user");
boolean ok = edit.commit();
user = pref.getString("user", default_user);
Log.d("SHARED WRITE", user);
Toast.makeText(this, user + " Save process: " + ok, Toast.LENGTH_LONG).show();
} else {
Log.d("SHARED READ", user);
Toast.makeText(this, "READ SharedPrefs: " + user, Toast.LENGTH_LONG).show();
}
EDIT: log results
that block always return this for which is incorrect app and I don't know why
//first run
SHARED CHECK Default_User
SHARED WRITE new_user
//each time after first
SHARED CHECK Default_User
SHARED WRITE new_user
That block always return this for which are all apps
//first run
SHARED CHECK Default_User
SHARED WRITE new_user
//each time after first
SHARED CHECK new_user
SHARED READ new_user
When you call apply() or commit() the changes are first saved to the app's memory cache and then Android attempts to write those changes onto the disk. What is happening here is that your commit() call is failing on the disk but the changes are still made to the app's memory cache, as is visible in the source.
It is not enough to read the value from the SharedPreferences as that value might not reflect the true value that is on the disk but only that stored in the memory cache.
What you are failing to do is to check the boolean value returned from the commit() call, it is probably false for your problematic case. You could retry the commit() call a couple of times if false is returned.
Just use below method and check.
Create one Java class AppTypeDetails.java
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
public class AppTypeDetails {
private SharedPreferences sh;
private AppTypeDetails() {
}
private AppTypeDetails(Context mContext) {
sh = PreferenceManager.getDefaultSharedPreferences(mContext);
}
private static AppTypeDetails instance = null;
public synchronized static AppTypeDetails getInstance(Context mContext) {
if (instance == null) {
instance = new AppTypeDetails(mContext);
}
return instance;
}
// get user status
public String getUser() {
return sh.getString("user", "");
}
public void setUser(String user) {
sh.edit().putString("user", user).commit();
}
// Clear All Data
public void clear() {
sh.edit().clear().commit();
}
}
Now Set value to SharedPreferences.
AppTypeDetails.getInstance(MainActivity.this).setUser(<user name>);
Get Value form SharedPreferences.
String userName = AppTypeDetails.getInstance(MainActivity.this).getUser();
Now do any thing with the userName.
Always check
if(userName.trim().isEmpty())
{
// Do anything here.
}
because In SharedPreferences we set user name blank ("")
or
you can set user name null in SharedPreferences then you need check
if(userName != null){
//do anything here
}
For clear data from SharedPreferences.
AppTypeDetails.getInstance(MainActivity.this).setUser("");
or
AppTypeDetails.getInstance(MainActivity.this).clear();
One thing you could try is to get a new SharedPreference instance after committing and see what happens:
SharedPreferences pref = this.getSharedPreferences("test", MODE_PRIVATE);
String user = pref.getString("user", default_user);
if (user.equals(default_user)) {
pref.edit().putString("user", "new_user").commit();
SharedPreferences newPref = this.getSharedPreferences("test", MODE_PRIVATE);
user = newPref.getString("user", default_user);
}
Your editor is committing a new preference map into disk, but it is possible that the old SharedPreference instance is not notified of the change.
Instead of using edit.commit();, you should use edit.apply();. Apply
will update the preference object instantly and will save the new
values asynchronously, so allowing you to read the latest values.
Source - Also read detailed difference between commit() and apply() in this SO Post.
The "Source" post referenced above also states pretty much the same problem and switching to apply() seems to have resolved the issue there.
Problem statement in the referenced post:
The problem is that when I am accessing this values, it is not
returning updated values, it gives me a value of SharedPreferences.
But when I am confirming the data in XML file ,the data updated in
that.
PS: And the reason that the same code block is not working on this one app and working on all other apps could also be that you are either using the block at different places or updating the value somewhere else too.
Your code is right, the code works on any app very well, looks like that in some part of your app the shared preferences are been modified, the only way to find a solution is review all your code, because if this problem only happens on one app, it's somewhere on your app that the shared preferences are been modified, for good practices, you should have only one file class for the management of your preferences on that way you can comment or find usage for a method and you can find where the shared preferences was been modified.
BTW the best way to store an user, password, or any account info is using Account Manager.
For good practices you can see this sample PreferenceHelper class.
public class PreferencesHelper {
public static final String DEFAULT_STRING_VALUE = "default_value";
/**
* Returns Editor to modify values of SharedPreferences
* #param context Application context
* #return editor instance
*/
private static Editor getEditor(Context context){
return getPreferences(context).edit();
}
/**
* Returns SharedPreferences object
* #param context Application context
* #return shared preferences instance
*/
private static SharedPreferences getPreferences(Context context){
String name = "YourAppPreferences";
return context.getSharedPreferences(name,
Context.MODE_PRIVATE);
}
/**
* Save a string on SharedPreferences
* #param tag tag
* #param value value
* #param context Application context
*/
public static void putString(String tag, String value, Context context) {
Editor editor = getEditor(context);
editor.putString(tag, value);
editor.commit();
}
/**
* Get a string value from SharedPreferences
* #param tag tag
* #param context Application context
* #return String value
*/
public static String getString(String tag, Context context) {
SharedPreferences sharedPreferences = getPreferences(context);
return sharedPreferences.getString(tag, DEFAULT_STRING_VALUE);
}}
I used the below code pattern in one of my app and I always get the latest value stored in SharedPreferences. Below is the edited version for your problem:
public class AppPreference
implements
OnSharedPreferenceChangeListener {
private static final String USER = "User";
private static final String DEFAULT_USER = "Default_User";
private Context mContext;
private String mDefaultUser;
private SharedPreferences mPref;
private static AppPreference mInstance;
/**
* hide it.
*/
private AppPreference(Context context) {
mContext = context;
mPref = PreferenceManager.getDefaultSharedPreferences(context);
mPref.registerOnSharedPreferenceChangeListener(this);
reloadPreferences();
}
/**
* #param context
* #return single instance of shared preferences.
*/
public static AppPreference getInstance(Context context) {
return mInstance == null ?
(mInstance = new AppPreference(context)) :
mInstance;
}
/**
* #return value of default user
*/
public String getDefaultUser() {
return mDefaultUser;
}
/**
* Set value for default user
*/
public void setDefaultUser(String user) {
mDefaultUser = user;
mPref.edit().putString(USER, mDefaultUser).apply();
}
/**
* Reloads all values if preference values are changed.
*/
private void reloadPreferences() {
mDefaultUser = mPref.getString(USER, DEFAULT_USER);
// reload all your preferences value here
}
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
reloadPreferences();
}
}
You can set the value as below in your activity:
AppPreference.getInstance(this).setDefaultUser("user_value");
To get the updated value for saved user, use below code:
String user = AppPreference.getInstance(this).getDefaultUser();
Hope this solution will fix the problem you are facing.
For saving and retrieving data from SharedPrefrence create a util type methods in your Utility class:
Below I have given code snippet for both the methods i.e for saving and retrieving SharedPrefrence data:
public class Utility {
public static void putStringValueInSharedPreference(Context context, String key, String value) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = preferences.edit();
editor.putString(key, value);
editor.commit();
}
public static String getStringSharedPreference(Context context, String param) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
return preferences.getString(param, "");
}
}
Now you just need to call Utility.putStringValueInSharedPreference(Activity_Context,KEY,VALUE); to put data in prefrence and Utility.getStringSharedPreference(Activity_Context,KEY); to get value from prefrence.
This methodology reduces chances of error; May this will be helpful for you.
I recommend controlling your preference check with another preference value.
String user = pref.getString("user", default_user);
In your preference check:
String getStatus = pref.getString("register", "nil");
if (getStatus.equals("true")) {
// move on code stuff
}
else{
// ask for preferences
}
In the part where the preference is added.
edit.putString("user", "new_user");
editor.putString("register", "true");
edit.commit;
Don't know if this is correct or not, but you can implement a Shared preference change listener and you can log in that and check if the shared preference is not called again in any other class, which is again setting the value to Default user.
I Mean you got to make sure that there is no other code resetting the value of user.
The code itself looks fine and you say it works in other apps. Since you have confirmed the XML file gets properly created, the only remaining option is that something happens to the file before you read it during app launch. If there is no other code in the app that handles any shared preferences, then something must wipe the file during the launch.
You should take careful look at launch configuration of this particular app and compare to other apps where this is working. Maybe there is some option that wipes user data when app is started. I know there is such an option at least for emulator (in Eclipse), but I am not sure if you are running this in emulator or device.
You should also try launching the app directly from the device instead of IDE. This tells if the problem is in the app itself or IDE configuration. And try moving this from onCreate (where I presume this is) to onResume and compare does it work when you resume to the app versus when you completely restart it.
Finally I solved that problem.
A method was added by one of other developers.
All files under data/data/packageName folder except libs folder were being deleted by this method.
I think they tried delete cache folder.
Removed this method and it solved.
I have an Activity with some Fragments and have to persist some fair amount of data but not fair enough for SQL. Now what is the best practice in use of SharedPreferences? I wanna avoid calls to the file and the commits of it as much as possible. Because I assume parsing of that file and especially the commits are bad for performance.
I know this question, which says that the call on the SharedPreferences file always returns the same object. But what about the commit?
Should I use f.e. a Bundle to save my data and persist them at once when the Activity goes in the background? Or should I always persist a portion of my data like in every Fragment? Or I just hunting ghosts?
I think this is an unnecessary and premature optimization that won't realistically have any performance impact. How much data are you storing in SharedPreferences? I think you are just hunting ghosts.
If you are using it as a means of communication between fragments then you are using it for an unintended purpose.
edit: For some further evaluation, SharedPreferences basically stores things in a Key/Value map. This makes it really convenient to store and retreive simple things such as user preferences (hence the name). If you need to do anything more complex than that, you can quickly see how cumbersome it would become using a Key/Value map, which is why it would make sense to move to a database storage like SQLite. With a database you get the obvious benefit of using queries. Basically the point of SharedPreferences is added convenience to developers so that you don't need to create a full database to store simple values. See here for more:
Pros and Cons of SQLite and Shared Preferences
Not sure what "fair amount of data" really is, but use SQL - that's why it is here for. I do really so no excuse to not do so, knowing how really easy it is. If you never tried sqlite on android (which could explain why you want to try to avoid it :) then go thru elementary tutorial and you are really done.
You can use a public class and we have to call it's method whenever we required.
example:
SessionManager mSessionManager = new SessionManager(this);
mSessionManager.putStringData("key", "value");
The Class is given below:
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
public class SessionManager {
// Shared Preferences
SharedPreferences pref;
// Editor for Shared preferences
Editor editor;
// Context
Context _context;
// Shared pref mode
int MODE_MULTI_PROCESS = 0;
// Sharedpref file name
private static final String PREF_NAME = "SharedPref_Name";
private SharedPreferences getPref() {
return _context.getSharedPreferences(PREF_NAME, Context.MODE_MULTI_PROCESS);
}
// Constructor
public SessionManager(Context context) {
this._context = context;
pref = _context.getSharedPreferences(PREF_NAME, MODE_MULTI_PROCESS);
editor = pref.edit();
}
/**
* Set the String data in the preferences.
*/
public void putStringData(String keyname, String value) {
editor.putString(keyname, value);
editor.commit();
}
/**
* #return the string data from the prefs
*/
public String getStringData(String keyName) {
return pref.getString(keyName, "");
}
/**
* Set the int data in the preferences.
*/
public void putIntData(String keyname, int value) {
editor.putInt(keyname, value);
editor.commit();
}
/**
* #return the boolean data from the prefs
*/
public int getIntData(String keyName) {
return pref.getInt(keyName, 0);
}
/**
* Set the boolean data in the preferences.
*/
public void putBooleanData(String keyname, boolean value) {
editor.putBoolean(keyname, value);
editor.commit();
}
/**
* #return the boolean data from the prefs
*/
public boolean getBooleanData(String keyName) {
return pref.getBoolean(keyName, false);
}
/**
* Set the long data in the preferences.
*/
public void putLongData(String keyname, long value) {
editor.putLong(keyname, value);
editor.commit();
}
/**
* #return the long data from the prefs
*/
public long getLongData(String keyName) {
return pref.getLong(keyName, 99);
}
/**
* remove data from pref
*
* #param keyName
*/
public void removeData(String keyName) {
editor.remove(keyName);
editor.commit();
}
//Save arrayList of Model type
public void saveAssignedLocationsToSharedPrefs(List<Locations> LocationModel) {
Gson gson = new Gson();
String jsonLocation = gson.toJson(LocationModel);
editor.putString("LocationArray", jsonLocation);
editor.commit();
}
//get arrayList of Model type
public ArrayList<Locations> getAssignedLocationsFromSharedPrefs() {
List<Locations> LocationData;
String jsonLocation = pref.getString("LocationArray", null);
Gson gson = new Gson();
Locations[] LocationItems = gson.fromJson(jsonLocation,
Locations[].class);
LocationData = Arrays.asList(LocationItems);
LocationData = new ArrayList<Locations>(LocationData);
return (ArrayList<Locations>) LocationData;
}
}