Strange unexplained behaviour in SharedPreferences Android - java

I'm not new to sharedPreferences, and I decided to use them to store the titles of courses in which users create, and then are loaded into a listView. The course titles are stored in a set which is then put into the sharedPreference. The issue here is when i input the first course(as a user), it gets saved persistently, but adding more courses also saves as well, and works while the app is active, but on app restart, the sharedPreference returns back to the initial value of only one item in the set.
Naturally, i looked up the issue, and was able to solve it by using editor.clear() which i got from this reference https://looksok.wordpress.com/2012/09/21/sharedpreferences-not-saved-after-app-restart/
Notice that the solution was described as "the tricky one". I really want to know what this line meant. I've checked on the issue, but still can't understand why this case is so different, and such abnormalities scare me whenever i use those classes. So here's my code:
// Shared Preferences
SharedPreferences customPrefs;
// Editor for Shared preferences
SharedPreferences.Editor editor;
private CustomCourseDBHelper customDBHelper;
private final static int ADD_CUSTOM_COURSE_REQUEST = 1;
private final static int EDIT_CUSTOM_COURSE_REQUEST = 2;
private final static String TITLE_PREFS = "customCourseTitles";
private final static String TITLE_PREF_LIST = "customCourseList";
private ListView cListView;
private Set<String> allTitles = new LinkedHashSet<>();
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.custom_course_list);
mToolbar = (Toolbar) findViewById(R.id.tool_bar_shadow);
cListView = (ListView) findViewById(R.id.customQuestionList);
addButton = (FloatingActionButton) findViewById(R.id.addButton);
cAdapter = new CustomCourseAdapter(this);
setSupportActionBar(mToolbar);
cListView.setAdapter(cAdapter);
//declare shared prefs
customPrefs = getSharedPreferences(TITLE_PREFS, MODE_PRIVATE);
//set on long press options
optionsList.add("Delete Course");
//set icon for add button
addButton.setImageDrawable(getResources()
.getDrawable(R.drawable.ic_add_white_24dp));
addButton.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
Intent i = new Intent(CustomCourseList.this, AddCustomCourseActivity.class);
startActivityForResult(i, ADD_CUSTOM_COURSE_REQUEST);
}
});
}
And here's where the saving occurs
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
if (resultCode == RESULT_OK && requestCode == ADD_CUSTOM_COURSE_REQUEST){
CustomCourseItem cci = new CustomCourseItem(data);
cAdapter.add(cci);
//save the provided details in a database
customDBHelper = new CustomCourseDBHelper(this, cci.getPath());
customDBHelper.addCustomCourse(cci);
editor = customPrefs.edit();
Set<String> mSet = customPrefs.getStringSet(TITLE_PREF_LIST, null);
if (mSet != null)
allTitles = mSet;
//store the given database names in a set in a shared preference
allTitles.add(cci.getPath());
editor.clear(); //this line here, without it, the sharedPref is not persistent,
//i didn't see any clear reason as to why this occurs, when i read the documentary
editor.putStringSet(TITLE_PREF_LIST, allTitles);
editor.commit();
}
//listen request for edits made to courses
if (resultCode == RESULT_OK && requestCode == EDIT_CUSTOM_COURSE_REQUEST){
if (data.getBooleanExtra("edited", false)){
restructureDbOnEdit();
}
}
}
PS. i omitted some unnecessary code like UI implementations and other non-sharedPreference related stuff.
So even though the app runs well now, any further insight on this issue would be really welcomed.

Well I finally found what went wrong after doing a proper search, check out the answer provided here. Note that this is a bug in android and happens to string sets only. Thanks
Set<String> in android sharedpreferences does not save on force close

Related

LinkedHashSet does not main insertion order?

I am currently building an Android app where I display quotes from famous people. I have a home screen and 2 other screens, where I am displaying all quotes and the other where I display favourite quotes.
So, when I hit the like button in the screen of AllQuotesActivity the quote and author will be saved in a LinkedHashSet, which will be saved in SharedPreferences, so my FavouriteQuotes Activity can obtain the data. I can obtain the data, but the data is mixed, even though other links say that LinkedHashSet maintains the insertion order. Maybe I did something wrong. Here are the important code snippets:
AllQuotesActivity.java:
SharedPreferences sharedPref;
Set<String> set = new LinkedHashSet();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.all_quotes);
Resources res = getResources();
Context context = getApplicationContext();
this.sharedPref = context.getSharedPreferences(
"MyPref", Context.MODE_PRIVATE);
final String[] quotesAndAuthors = res.getStringArray(R.array.quotes);
button3.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
set.add(quotesAndAuthors[counter]);
set.add(quotesAndAuthors[counter + 1]);
}
});
}
#Override
public void onPause() {
super.onPause();
Log.d("RichQuotes", "Its paused mkay");
Editor editor = sharedPref.edit();
editor.putStringSet("quotesAndAuthors", this.set);
editor.commit();
}
}
FavouriteQuotesActivity.java:
SharedPreferences sharedPref;
Set<String> set = new LinkedHashSet();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.favourite_quotes);
Resources res = getResources();
Context context = getApplicationContext();
this.sharedPref = context.getSharedPreferences(
"MyPref", Context.MODE_PRIVATE);
set = sharedPref.getStringSet("quotesAndAuthors", null);
for (Iterator<String> it = set.iterator(); it.hasNext(); ) {
String s = it.next();
Log.v("test", s);
}
I removed unnecessary code.
In the FavouriteQuotesActivity.java I am logging the set to check its values. The log-outputs and the outputs on the screen are the same, both unsorted the same way.
Set set = new LinkedHashSet();
In this line, you instantiate a new empty LinkedHashSet object. This object was then assigned to the variable named set.
set = sharedPref.getStringSet("quotesAndAuthors", null);
In this line, you reassigned the set variable to point to some other object, some Set object returned by your call to getStringSet. We do not know the concrete class of this second object that implements the Set interface. You can ask by calling getClass.
Your first set, the empty LinkedHashSet, went unused. With no other references pointing to it, that set became a candidate for eventual garbage-collection.

SharedPreferences If/Else Statement does not work correctly when I force close app

Summary of problem: When I force close the app by opening task window and swiping it closed my if/else statement is not working properly. I have the Name Selection Activity as the Default Launcher Activity. The name selection Activity should only pop up if there are no Shared Prefs, meaning the user has not selected a name yet from the spinner. But even after the user has selected a name and it is stored in Share Prefs, when I force close the app I still get returned to the name selection activity
What I have tried I have tried if (stringNamePackage.equals("")) and if (stringNamePackage == "") and if (NAME.equals("")) and if (NAME == "") At first I thought it was because maybe my Shared Prefs was not saving the name correctly but it is not that, the name still appears correctly when I force close it. But when I try to add the if/else statment it always just sends me back to the name selection activity regardless if I have a name saved in Shared Prefs or not. I have spent 4 hours trying to get this to work and I am at my wits end. I also spent hours looking at different stack articles of how to save and retrieve Shared Pref data. I even tried how to check the SharedPreferences string is empty or null *android and it is still not working.
NameSelectionActivity
public class NameSelection extends AppCompatActivity implements AdapterView.OnItemSelectedListener {
Spinner nameSpinner;
String stringNamePackage;
Button bSaveSelection;
Context context;
public ArrayAdapter<CharSequence> adapter;
public static String SHARED_PREFS = "sharedPrefs";
public static String NAME = "name";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_name_selection);
context = this;
nameSpinner = findViewById(R.id.horizonNameSpinner);
stringNamePackage = "";
//Create the list to populate the spinner
List<String> nameList = new ArrayList<>();
nameList.add("01");
nameList.add("02");
nameList.add("03");
//Array Adapter for creating list
//adapter = ArrayAdapter.createFromResource(this, R.array., android.R.layout.simple_spinner_item);
adapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item, nameList);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_item);
nameSpinner.setAdapter(adapter);
nameSpinner.setOnItemSelectedListener(this);
//If User has not selected name, start this activity to allow user to pick Name, else, send user to Main Activity
//else start MainActivity
if (stringNamePackage .equals("")) {
Toast.makeText(this, "Welcome, please select Name", Toast.LENGTH_LONG).show();
} else {
Intent sendToMainActIntent = new Intent(this, MainActivity.class);
startActivity(sendToMainActIntent);
}
//Save Selection Button Code
bSaveSelection = findViewById(R.id.bSaveSelection);
bSaveSelection.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent myIntent = new Intent(view.getContext(), MainActivity.class);
//Saves name to SharedPrefs
saveName();
//Sends user to MainActivity
startActivity(myIntent);
finish();
}
});
}
//Stores name selected in SharedPreferences
public void saveName() {
SharedPreferences app_preferences = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = app_preferences.edit();
editor.clear();
//Stores selection in stringNamePackage
editor.putString(NAME, stringNamePackage );
editor.commit();
}
#Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
//Put user selection into String text
String text = adapterView.getItemAtPosition(i).toString();
stringNamePackage = text;
}
#Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
}
Main Activity
The purpose of this activity case is to send the user back to the name Selection screen when they hit the "Test" button which will erase the shared prefs and hence, trigger the if/else statement and making the user pick a name. This is how I get and set the Name in Main Activity:
in onCreate
Getting name
SharedPreferences app_preferences = PreferenceManager.getDefaultSharedPreferences(context);
nameSharedPref = app_preferences.getString(NAME, "");
name.setText(nameSharedPref);
Clearing Name
SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = mPrefs.edit();
editor.clear();
editor.commit();
finish();
What I expect: I expect the user to have to pick a name the first time they boot up the app. Then, every time they open the app an if/else statement will check if the user has a name or not in Shared Prefs. If they have a name they will go directly to the Main Activity. If they don't then they will go back to the Name Selection Activity.
To get data and store data to your SharedPreferences you use this:
SharedPreferences sharedPreferences = getSharedPreferences(getPackageName(), Context.MODE_PRIVATE);
PreferenceManager.getDefaultSharedPreferences(context) is used to build "Settings" screens or similar, the first one you use it to store/retrieve arbitrary data not necessarily binded to UI.
Even more if you want to organize your SharedPreferences you could append to getPackageName() different keys like:
getSharedPreferences(getPackageName() + ".booleans", Context.MODE_PRIVATE);
getSharedPreferences(getPackageName() + ".flags", Context.MODE_PRIVATE);
getSharedPreferences(getPackageName() + ".keys", Context.MODE_PRIVATE);
each one of them is different file that stores shared preferences, you can ommit the prepending dot ., but for naming consistency it'll be better to keep it, there is no really need for the extra keys, but if you have some sort of OCD, that might be "relaxing" ;-).
Then you can use your Android Studio's Device Explorer to browse the shared preferences, they are located under "data/data/your.package.name/shared_prefs"

Back button restores modified text Views

I will simplify my code to address the problem specifically:
I have an activity A with some TextViews, which text is set to certain key values stored in SharedPreferences, in the activity's OnCreate method. Each textview has a button besides it. When a button is clicked it opens a new activity B which displays an adapter with different text strings. When the user clicks one, the new string is stored in preferences and the user is directed back to Activity A through an intent, and so OnCreate method is called and the textview is updated with the selected text. This works perfectly.
However, my problem is:
When a user does this and updates the textview, if they press Back button once, it will take them to Activity B, but if pressed twice that will take them to Activity A before updating the TextView and thus displaying the old textview, despite having stored in SharedPreferences the updated value. How can this be fixed?
A more simplified version of my problem is, I have a TextView in my layout, and a button which if pressed, deletes it and refreshes the Activity. User presses the delete button, text view disappears, but then presses back button and TextView is restored. That's what I dont want.
I have researched all the back button methodologies and savedInstanceState documentation but I still havent found something that works. I also tried adding an UpNavigation button in my action bar but it does the same effect than the back button.
ACTIVITY A (All these bits of code are called in OnCreate)
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
String sound1name = prefs.getString("sound1", "1");
TextView sound1TV = findViewById(R.id.sound1);
sound1TV.setText(sound1Name);
ImageView sound1btn = findViewById(R.id.sound1_btn);
sound1btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent1 = new Intent(getApplicationContext(), SoundSelector.class);
startActivity(intent1);
}
});
ACTIVITY B (calls adapter)
AudioFileAdapter aFilesAdapter = new AudioFileAdapter(SoundSelector.this, audioFileList, soundID);
ListView listView = findViewById(R.id.sounds_list);
listView.setAdapter(aFilesAdapter);
ADAPTER IN ACTIVITY B (OnClickListener when text is selected)
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(contextL);
SharedPreferences.Editor editor = settings.edit();
editor.putString("sound1", sound1string);
editor.apply();
Intent intent1 = new Intent(getContext(), SoundEditor.class);
con.startActivity(intent1);
Im not sure if it is the Activity Lifecycle I have to modify, or intents, or something else but if someone could point me in the right direction I would really appreciate it, if you need any more information or code I'll post as soon as possible.
For storing and retrieving shared preferences try the following:
Storing
SharedPreferences preferences = getSharedPreferences("com.appname", Context.MODE_WORLD_WRITEABLE);
SharedPreferences.Editor editor = preferences.edit();
editor.putString("sound1", "YOUR STRING HERE");
editor.apply();
Retrieving
SharedPreferences prfs = getSharedPreferences("com.appname", Context.MODE_PRIVATE);
String soundString = prfs.getString("sound1", "");
Your intent looks fine, are you sure you're passing Activity A's name?
For your second scenario, you could store if the text view was deleted in the shared preference, so when the back button is pressed, it won't display it again in the previous activity.
Something like this
if (isDeleted.equals("Yes")) {
textView.setVisibility(View.INVISIBLE);
}
The way Activity B is navigating back to Activity A by restarting the activity in the front and not by onBackPressed() navigation. Besides if the navigation is an important component to update the string value then the recommended method would be to use startActivityForResult() and update the preference and the TextView upon onActivityResult() of Activity A
class ActivityA extends AppCompatActivity {
private static final int activityRequest = 0x22;
public static final String keyPref = "keyToSharedPrefData";
private TextView mTextView;
private boolean isHidden = false;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.a_activity);
mTextView = findViewById(R.id.someTextView);
final String textForTextView = SharedPrefUtils.getString(keyPref);
mTextView.setText(textForTextView);
final Button button = findViewById(R.id.someButton);
if (button != null) {
button.setOnClickListener((view) -> {
final Intent intent = new Intent(ActivityA.this, AcitivtyB.class);
startActivityForResult(intent, activityRequest);
});
}
final deleteButton = findViewById(R.id.delete_button);
if (deleteButton != null) {
deleteButton.setOnClickListener((view) -> {
mTextView.setText("");
isHidden = true;
});
}
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data){
if (requestCode == activityRequest && resultCode == ActivityB.resultSuccess && data != null) {
if (data.containsKey(ActivityB.resultKey)) {
SharedPrefUtils.saveString(keyPref,
data.getString(ActivityB.resultKey, SharedPrefUtils.getString(keyPref));
if (mTextView != null) {
mTextView.setText(SharedPrefUtils.getString(keyPref));
}
}
}
if (isHidden) {
mTextView.setVisibility(View.GONE);
}
}
}
in ActivityB you can
class ActivityB extends AppCompatActivity {
public static final int resultSuccess = 0x11
public static final int resultFailure = 0x33
public static final String resultKey = "keyForResult"
private void onListItemClick(final String soundString) {
// optional you can also do this
SharedPrefUtils.saveString(ActivityA.keyPref, soundString);
// better to do this
final Intent returnIntent = getIntent() != null ? getIntent() : new Intent();
returnIntent.putExtra(resultKey, soundString);
if (getCallingActivity() != null) {
setResult(returnIntent, resultSuccess);
}
onBackPressed();
}
}

Having Trouble Passing And Recieving Data From My Intent

I'm trying to make a pretty simple app to help my girlfriend feel safer.
Im really bad at this, and a little help would go a long way. I've been trying to work with intents, and I really feel as if I'm super close to the solution at hand, I just need a little help.
So, the opening page is supposed to wait until you have data in your shared Preferences and then it will act on it.
The second page is supposed to take some data from EditTexts and store it in your intent. For some reason though, my data is not being stored, and when I pull something from the intent it is "".
CLASS 1:
public void ActivateAlarm(View view){
Bundle myBundle = getIntent().getExtras();
if(myBundle == null){
Log.i("The bundle is empty!", "Smashing success!");
}
else{
SharedPreferences sharedPreferences = this.getSharedPreferences("com.example.jackson.distressalarm", Context.MODE_PRIVATE);
String NumberToCall = sharedPreferences.getString("CallNumber", "");
String TextOne = sharedPreferences.getString("1Text", "");
String TextTwo = sharedPreferences.getString("2Text", "");
String TextThree = sharedPreferences.getString("3Text", "");
Button myButton = (Button) findViewById(R.id.button);
myButton.setText(TextOne);
Log.i("MY NUMBER TO CALL", NumberToCall);
/*Take in the data from the settings. Check it for consistency.
1)Are the numbers empty?
2)Is the number 911 or a 7 digit number?
3)Do they have a passcode?
4)Is the number real? No philisophical BS
*/
}
}
CLASS 2:
public void GoToAlarm(View view){
EditText NumberToCall = (EditText) findViewById(R.id.callNumber);
EditText text1 = (EditText) findViewById(R.id.textNumOne);
EditText text2 = (EditText) findViewById(R.id.textNumTwo);
EditText text3 = (EditText) findViewById(R.id.textNumThree);
Intent intent = new Intent(this, AlarmActive.class);
intent.putExtra("callNumber", NumberToCall.getText().toString());
intent.putExtra("1Text", text1.getText().toString());
intent.putExtra("2Text", text2.getText().toString());
intent.putExtra("3Text", text3.getText().toString());
startActivity(intent);
}
I think the problem is coming from a bit of a mix-up between Intents and SharedPreferences.
An Intent is a way to pass data from one activity to another. You're passing data correctly in Class 2, but you're not retrieving it in Class 1. Here's how you can do that:
String NumberToCall = intent.getStringExtra("CallNumber");
String TextOne = intent.getStringExtra("1Text");
String TextTwo = intent.getStringExtra("2Text");
String TextThree = intent.getStringExtra("3Text");
SharedPreferences are a way to save user data. If you want to save data in addition to passing it between activities, you'll need to add it to SharedPreferences with the following code:
SharedPreferences preferences = this.getSharedPreferences("com.example.jackson.distressalarm", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putString("callNumber", NumberToCall.getText().toString());
editor.putString("1Text", text1.getText().toString());
editor.putString("2Text", text2.getText().toString());
editor.putString("3Text", text3.getText().toString());
editor.apply();
You can retrieve the values with the code you were using in Class 1.

Save The value of Variable in Java/Android [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Making data persistent in android
Edited:
I am building an android app which works on the principle of a simple counter.
public class TasbeehActivity extends Activity {
/** Called when the activity is first created. */
int count;
ImageButton imButton;
TextView display;
static String ref = "Myfile";
static String key = "key";
SharedPreferences myPrefs;
boolean check = false;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
imButton = (ImageButton) findViewById(R.id.bCount);
display = (TextView) findViewById(R.id.tvDisplay);
myPrefs = getSharedPreferences(ref, 0);
if(check){
String dataReturned = myPrefs.getString(key, "0");
count = Integer.parseInt(dataReturned);
display.setText(""+count);
}
imButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
Random rand = new Random();
display.setTextColor(Color.rgb(rand.nextInt(255),
rand.nextInt(255), rand.nextInt(255)));
count++;
display.setText("" + count);
}
});
}
#Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
String data = display.getText().toString(); SharedPreferences.Editor
editor = myPrefs.edit(); editor.putString(key, data);
editor.commit();
check = true;
}}
I want to save the value of count so that when my app restarts after closing it should have the value of count before closing app.
Also when I change orientation of the emulator i.e. from portrait to landscape or vice versa count gets set to 0.
Is there any solution for both of these problems?
You should use SharedPreferences to save your variable, and Override OnConfigurationChange because it's probably calling your oncreate method (don't forget to add android:configChanges="orientation|keyboardHidden|screenSize" in your manifest)
I Hope this will help you.
1. use sharedpreferences for saving or retrieving value of count when app is restarts after closing
2. see handle orientation change in android count gets set to 0 when I change orientation of the emulator i.e. from portrait to landscape or vice versa
Try these....
android:configChanges="orientation" , This will prevent your Activity to again restart when orientation changes.
Use Bundle, but thats only for small amount of data. Serialization and Deserialization will be needed.
You can also use onRetainNonConfigurationInstance(), and getLastNonConfigurationInstance(), for storing and retrieving data.
See this link for further details:
http://developer.android.com/guide/topics/resources/runtime-changes.html

Categories

Resources