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.
Related
Currently I am working on the second version of my vocabulary app. The first version does not provide as much documentation to expand the application. so I decided to start from the ground up.
I have an activity, every time I send it to background and then get it back to foreground, some objects became null and activity restarts. In onResume, I logged the value of the null object.
The old activity (version 1) does not have that problem.
I tried avoid that null problem whilst saving activities states and objects to SaveInstanceState and restore them. but RestoreInstance was never called.
here is my code
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_query);
Log.e(TAG, "onCreate");
/* Initialize DATA */
data = new DataHandler(this);
/* Initialize VIEWS */
btnPhase = findViewById(R.id.query_voc_phase);
cardPath = findViewById(R.id.query_voc_path);
queryView = findViewById(R.id.query_view);
btnShow = findViewById(R.id.query_btn_show_answer);
btnRight = findViewById(R.id.query_btn_mark_right);
btnWrong = findViewById(R.id.query_btn_mark_wrong);
btnNext = findViewById(R.id.query_btn_show_next);
}
#Override
protected void onStart() {
super.onStart();
Log.e(TAG, "onStart");
/* set OnClickListener */
btnShow.setOnClickListener(this);
btnRight.setOnClickListener(this);
btnWrong.setOnClickListener(this);
btnNext.setOnClickListener(this);
//init Variables
queryList = new ArrayList<>();
idList = new ArrayList<>();
listGroupings = new ArrayList<>();
answeredList = new ArrayList<>();
//init Preferences
preferences = PreferenceManager.getDefaultSharedPreferences(this);
//set Visibility of view items based on Preferences
if (!preferences.getBoolean(Preferences.SHOW_CARD_PATH_ENABLED, true))
cardPath.setVisibility(View.GONE);
//load preferences
userID = preferences.getInt(Preferences.CURRENT_USER, -1);
//check if flashing card is enabled and get time
flash_time = preferences.getBoolean(Preferences.FLASH_CARD_ENABLED, false) ? preferences.getInt(Preferences.FLASH_TIME, flash_time) : -1;
//get data from intent
Intent intent = getIntent();
if (intent.hasExtra(QUERY_CONTAINER_LIST)) {
//get data into the list
HashMap<String, ArrayList<Integer>> queryData = data.getCardsToQuery(intent.getStringArrayListExtra(QUERY_CONTAINER_LIST));
idList = queryData.get("iList");
listGroupings = queryData.get("listGroupings");
}
// change active Fragment to initial fragment
changeFragment(QueryViews.Simple);
}
#Override
protected void onResume() {
super.onResume();
Log.e(TAG, "onResume");
Log.e(TAG, "current card is null " + (currentCard == null));
//loads a new card if there is none
if(currentCard == null) loadNext();
}
/*
InstanceState
*/
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Log.e(TAG, "RESTORE");
queryList = savedInstanceState.getParcelableArrayList("queryList");
idList = savedInstanceState.getIntegerArrayList("iList");
listGroupings = savedInstanceState.getIntegerArrayList("listGroupings");
answeredList = savedInstanceState.getParcelableArrayList("answeredList");
currentCard = (VocabularyCard)savedInstanceState.getSerializable("currentCard");
lastCard = (VocabularyCard)savedInstanceState.getSerializable("lastCard");
}
#Override
protected void onSaveInstanceState(Bundle outState) {
Log.e(TAG, "SAVE");
outState.putParcelableArrayList("queryList", queryList);
outState.putIntegerArrayList("idList", idList);
outState.putIntegerArrayList("listGroupings", listGroupings);
outState.putParcelableArrayList("answeredList", answeredList);
outState.putParcelable("currentCard", currentCard);
outState.putParcelable("lastCard", lastCard);
super.onSaveInstanceState(outState);
}
Spotting below error, maybe it is a typo error, but may be the culprit of your null object issue. You restore iList inside onRestoreInstanceState,
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Log.e(TAG, "RESTORE");
idList = savedInstanceState.getIntegerArrayList("iList");
}
But inside onSaveInstanceState, you put idList,
#Override
protected void onSaveInstanceState(Bundle outState) {
Log.e(TAG, "SAVE");
...
outState.putIntegerArrayList("idList", idList);
...
super.onSaveInstanceState(outState);
}
I found a solution. I found out that onRestoreInstanceState will called if device rotates. First of all if something became null, you should first check if the object was assigned a value. in this case i overlooked, that the object was never assigned a value.
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
I have a ListView.
I populate this list from 2 editTexts
When I move activity and go back to it the entries are gone again.
I kind of understand why this is but dont know how to correct it.
ListView lv2 = (ListView) findViewById(R.id.listView2);
final SimpleAdapter simpleAdpt = new SimpleAdapter(this, planetsList, android.R.layout.simple_list_item_1, new String[]{"planet"}, new int[]{android.R.id.text1});
planetsList.add(createPlanet("planet", "testme"));
lv2.setAdapter(simpleAdpt);
button21.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
iinitList();
simpleAdpt.notifyDataSetChanged();
editText5.setText("");
editText6.setText("");
}
});
}
private void iinitList() {
String st,str;
Double db;
if (editText5.getText().toString()!= "" && editText6.getText().toString()!="") {
st = editText5.getText().toString();
str = editText6.getText().toString();
db = Double.parseDouble(str);
planetsList.add(createPlanet("planet", ""+st+
": \n" +db+""));
}
}
HashMap<String, String> createPlanet(String key, String name) {
HashMap<String, String> planet = new HashMap<String, String>();
planet.put(key, name);
return planet;
}
As you can see I have added a value to the list manually called test also, when I move activity this stays in the list, I would love if the editText entries were to stay in there also when I move activities.
Activities can be destroyed when you navigate to a new one or rotate. This will clear anything that is only referenced by the activity, like your EditTexts. However, Android provides a nice utility for saving things you want to remain in a method called, which you can override in your activity:
#Override
protected void onSaveInstanceState(Bundle state) {
// Put your values in the state bundle here
}
#Override
protected void onCreate(Bundle savedState) {
// Load your UI elements as usual
if (savedState != null) {
// Load your state from the bundle
}
}
That same bundle will be given back to you in onCreate, where you create your UI to begin with so you can reload the state from it.
This is a really good description of how activities work:
http://developer.android.com/reference/android/app/Activity.html
Hi i have the following codes that stores an arraylist into a list then save it into SharedPreference. But when i re-run the app, the values are gone and no matter how many times i invoke adding more elements into the arraylist, it will only invoke once.
The following is my codes:
//debug usage
Button z = (Button)findViewById(R.id.button3);
z.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
testres.add("1");
testres.add("2");
testres.add("3");
}
});
SharedPreferences prefs=this.getSharedPreferences("yourPrefsKey", Context.MODE_PRIVATE);
SharedPreferences.Editor edit=prefs.edit();
Set<String> set = new HashSet<String>();
set.addAll(testres);
edit.putStringSet("yourKey", set);
edit.commit();
And this is how i retrieve:
SharedPreferences prefs=this.getSharedPreferences("yourPrefsKey", Context.MODE_PRIVATE);
Set<String> set = prefs.getStringSet("yourKey", null);
List<String> sample= new ArrayList<String>(set);
testres = new ArrayList<String> (set);
for(int i =0; i<testres.size();i++){
System.out.println("Printing: "+testres.get(i));
}
No matter how many times i invoke onclick, the testres will have only 1,2,3 in the arraylist and if i restart the app, the elements in the arraylist is gone. Do advise thank you so much!
Update: I managed to retrieve my values on start based on the answers below but still unable to add more elements into the arraylist. The arraylist is stuck with elements 1,2,3 . Do advise.
No matter how many times i invoke onclick, the testres will have only
1,2,3 in the arraylist and if i restart the app
Because of you are saving values in SharedPreferences on Activity start which only save ArrayList with default values.
To save testres with items you have added on Button click also use SharedPreferences related code inside onClick method after adding items in ArrayList.
#Override
public void onClick(View arg0) {
testres.add("1");
testres.add("2");
testres.add("3");
//... save testres ArrayList in SharedPreferences here
}
SharedPreferences prefs=this.getSharedPreferences("yourPrefsKey", Context.MODE_PRIVATE);
SharedPreferences.Editor edit=prefs.edit();
Button z = (Button)findViewById(R.id.button3);
z.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
testres.add("1");
testres.add("2");
testres.add("3");
Set<String> set = new HashSet<String>();
set.addAll(testres);
edit.putStringSet("yourKey", set);
edit.commit();
}
});
I have a ListView in a ListFragment that is populated via a database query. It loads all the data just fine when it first populates the list in onCreate(). But when I requery the database, assign the return value to assignmentsCursor, and call notifyDataSetChanged(), it doesn't update adapter.mCursor. If I go into the Debugging mode in Eclipse I can see that assignmentsCursor.mCount has changed, but when I look at adapter.mCursor.mCount, it's the same as before. (Yes, I'm checking after notifyDataSetChanged() has been called.)
The relevant parts of my code:
SimpleCursorAdapter adapter;
Cursor assignmentsCursor;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
course = savedInstanceState.getShort(Values.ASSIGNMENT_KEY_COURSE);
}
setRetainInstance(true);
// Create and array to specify the fields we want
String[] from = new String[] { Values.KEY_TITLE, Values.ASSIGNMENT_KEY_COURSE };
// and an array of the fields we want to bind in the view
int[] to = new int[] { R.id.assignment_list_title, R.id.assignment_list_course };
// Need to give assignmentsCursor a value -- null will make it not work
updateAdapter();
adapter = new SimpleCursorAdapter(context, R.layout.assignment_list_item, assignmentsCursor, from, to);
setListAdapter(adapter);
}
public void onResume() {
super.onResume();
updateAdapter();
refresh();
}
/**
* Updates the content in the adapter.
*/
public void updateAdapter() {
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean showingCompleted = sharedPrefs.getBoolean(Values.ASSIGNMENT_KEY_SHOWING_COMPLETED, false);
if (course < 0) // Showing assignments from all courses
if (showingCompleted)
assignmentsCursor = DbUtils.fetchAllAssignments(context, Values.ASSIGNMENT_LIST_FETCH, null, true);
else
assignmentsCursor = DbUtils.fetchIncompleteAssignments(context, Values.ASSIGNMENT_LIST_FETCH, null, true);
else // Showing assignments from specified course
if (showingCompleted)
assignmentsCursor = DbUtils.fetchAllAssignments(context, Values.ASSIGNMENT_LIST_FETCH, course, true);
else
assignmentsCursor = DbUtils.fetchIncompleteAssignments(context, Values.ASSIGNMENT_LIST_FETCH, course, true);
}
private void refresh() {
updateAdapter();
adapter.notifyDataSetChanged();
}
Help me!! ;)
P.S. If you need any more details/code, just let me know. I am positive that the database is querying correctly though.
Thanks to Deucalion, I have determined that I incorrectly assumed that the adapter was referencing the assignmentsCursor variable as opposed to creating a copy of the Cursor for itself. What I needed to do was just call adapter.changeCursor(assignmentsCursor) so that it updated its local copy. Thanks again Deucalion!