I have some code that works 98% of the time, and 100% during my own testing so I can not really reproduce the problem other than having user devices experience this issue.
What I do in onPostExecute() is set a parameter like this:
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences( AddProblemActivity.this);
prefs.edit().putString("recent_problem_id", result ).commit();
and then go to the next activity:
Intent myIntent = new Intent(AddProblemActivity.this, ProblemActivity.class);
AddProblemActivity.this.startActivity(myIntent);
and then try to get that parameter there like this:
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
ProblemActivity.this);
// display a loading message before problem loads.
String recent_problem_id = prefs.getString( "recent_problem_id" , null );
if ( recent_problem_id == null )
{
// Sometimes it is null!
}
Would anyone know why this happens?
Thanks!
If you are trying to pass the data to a new activity, why not put it as a String extra in the intent? Then, get that String from the intent in the new activity. If you still need to store it, you can do so in the new activity's onCreate() after it pulls it from the intent.
Intent myIntent = new Intent(AddProblemActivity.this, ProblemActivity.class);
//Add results here
myIntent.putExtra("RecentProblemId", result);
AddProblemActivity.this.startActivity(myIntent);
Then, in the onCreate of your new Activity, do:
String recentProblemId = getIntent().getStringExtra("RecentProblemId");
Now, if you still need this information stored, do:
if(recentProblemId != null){
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
ProblemActivity.this);
prefs.putString("recent_problem_id",recentProblemId).commit();
}
I know this doesn't exactly answer your question as to why the String isn't always being committed to the preferences in onPostExecute(). However, the best practice for passing information between activities is via Intents and extras.
My guess as to why it may not always work for some users, is that their devices are not done writing the data to the shared preferences file before the new Activity starts and tries to read from that same file. Hope this helps.
First of all, see Raghav Sood's answer.
There is one delicate moment. You might start executing your AsyncTask than rotate the device. Activity will be recreated and in PostExecute you'll have wrong context so your preferences won't be saved.
If it's true you should use onRetainNonConfigurationInstance() to save appropriate task's instance.
I'm not sure about this, but I think the problem might be due to the difference in the Context you're passing. You're using the Context of AddProblemActivity first, and then the Context of ProblemActivity. Try using a set preference, like a filename one:
SharedPreferences prefs = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE);
Note that getSharedPreferences() is a method from Context, so you'll need to have a reference to an Activity or maybe Application Context in your AsyncTask to be able to use it.
Seems like your code is fine and there's no reason why it shouldn't work, the only reason will be some defect which is probably device related. My thoughts are since the Shared prefs are saved on local storage something can go wrong in the process.
As advised in the comments, it will be a must to add a device type log,
I would recommend you use "ACRA" - http://code.google.com/p/acra/ that can give you detailed reports with minimum effort (pay attention you can send a report not necceserily only on app crash).
Take a look at this thread, he shows a problem that maybe you are experiencing as well:
SharedPreferences will not save/load in PreferenceActivity.
If it's the case, a solution will be to handle the saving of this persistent data manually on the local storage or use a DB solution. Good luck :)
I think the issue is due to the shared preference setting not being written to your file system yet when you try to access it from your second activity. You mention that you write the setting from the onPostExecute method (of an AsyncTask perhaps?). When you start an AsyncTask there is no guarantee that it will start immediately. The only guarantee there is, is that it will start in a background thread. The platform can and will decide when to actually run the background thread, depending on system load, file system blocks or so. It might very well be so that your AsyncTask hasn't been started yet when you switch to the second activity (and hence the onPostExecute method hasn't been called yet).
Saving shared preferences are tricky as they write to the file system. You might end up blocking the main thread if you aren't careful. The SharedPreferences.Editor object has an apply method as well which will update the in-memory cache of your shared preferences immediately (making the changes available at once) and kick of a background thread to save the actual value to the file system as well. So my tip would be that if you have the possibility, you should try to call the apply method (from your main thread) instead of the commit method from (what I assume) a AsyncTask. The apply method requires API level 9 or higher.
For your reference: http://developer.android.com/reference/android/content/SharedPreferences.Editor.html
Edit:
The commit method will return a boolean value depending on the result of the write operation. You could (should?) check that return-value in order to at least be able to take correct counter measures on failure (like show a "Couldn't save your setting, please try again" toast or something).
Cheers,
--dbm
I could be wrong. But I believe a friend of mine had a similar problem once. I was stuck on this problem for hours. The results for more like 30% of the time it wouldn't work. I believe that the onPostExecute() runs on a separate thread when the Intent is instantiated and the activity is called. This is because an AsyncTask is implemented on a separate thread. Depending on the device, this would be called more likely then not. Our tablet it rarely happened, on the smart phone it would occur more commonly.
You can test this by debugging the application and looking at the AsyncThread thread and see when the call is made.
Yes, it is better to send the variable via the putExtra().
I hope this helps you understand why this occurred.
The commit action of SharePreferences is synchroneos so I don't think the fact that you are starting the new intent should effect it, only thing is that the commit action isn't fail safe, it can fail.
http://developer.android.com/reference/android/content/SharedPreferences.Editor.html#commit()
Returns true if the new values were successfully written to persistent
storage.
Maybe you should check that return value to make sure you managed to save the result.
Related
I really have to use another thread to read and write a sharedPreferences file?
I'm storing an integer array in sharedPreferences file, I convert it in string to store there, ok, this array corresponds to the itens of a gridView, then I have to read it on onCreate() and write it on onDestroy(), the problem is I'm calling an asyncTask on onCreate() and this is causing a lot of problems... the main one is that the onCreate() is been called many times (every time that other activity came in front mine, it is destroyed and restarted...), I found out in here that a kind bug occurs when other thead is called on onCreate(), then I had to save some things on onSaveInstanceState(), but this only resolved my problem in part. I already tested do not use other thread and read and write the file directly on UI thread, and this worked nicely, but I always heard I have to use other thread to any kind of I/O... then I'm in doubt...
After all, I can or I can't read and write a sharedPreferences file without call other thread?
Thx
I have to create a small android application for my college course work which presents 10 maths problems and take answers from users to calculate the score.
The program is supposed to have a main screen with "new game" button and a "continue" button.
How do I program to save data during the application run and retrieve them from the stored place to continue from that point if the user presses continue button? (what sort of a method I should be looking at for such a task? )
Thanks in advance.
Just use preference to store and retrieve value in the code.Here is the snippet
//Set Preference
SharedPreferences myPrefs = getSharedPreferences("myPrefs", MODE_WORLD_READABLE);
SharedPreferences.Editor prefsEditor;
prefsEditor = myPrefs.edit();
prefsEditor.putString("REFKEY", valuetobestored);
prefsEditor.commit();
//Get Preferenece
SharedPreferences myPrefs;
myPrefs = getSharedPreferences("myPrefs", MODE_WORLD_READABLE);
String output=myPrefs.getString("REFKEY", "");
It depends on the kind of data you want to store. If it is really basic, you would want to store them as name value pairs in the onSaveInstanceState method or as Preferences in the onPause method, if it is a bit more complex, you might want to store in a SQLiteDB.
The guide says
the method onSaveInstanceState(Bundle) is called before placing the
activity in such a background state, allowing you to save away any
dynamic instance state in your activity into the given Bundle, to be
later received in onCreate(Bundle) if the activity needs to be
re-created. See the Process Lifecycle section for more information on
how the lifecycle of a process is tied to the activities it is
hosting. Note that it is important to save persistent data in
onPause() instead of onSaveInstanceState(Bundle) because the latter is
not part of the lifecycle callbacks, so will not be called in every
situation as described in its documentation.
This is how you save data in onSaveInstanceState: Saving Android Activity state using Save Instance State
This is how you can save data in Preferences: Making data persistent in android
Just use SharedPreferences. Check this link out for more details. 2 early in the morning to give you an example of my own :P
How do I get the SharedPreferences from a PreferenceActivity in Android?
The preferences lasts as long as the application is installed on the device. One you delete if , it removes them as well.
Another cool thing you might want to know, if you want to check if your app is at it's first run, EVER, then just check a boolean value stored (or not stored on the first run) on the device. :) If there isn't one (it will return the default value), then just change it's value to true or something and as you'll check it every time the app runs, you'll know it's not the first run :)
Hope this helps, cheers.
May be this is not the best solution.. but this is a way to get required functionality..
in on click of continue button put this code
finish();
Intent k=new Intent(youractivitytivity.this,youractivity.class);
k.putExtra("qno",x+1)<---- next question number to appear on screen(x is the present question number)
k.putExtra("score",bool)<--- if the answer is right or wrong)
startActivity(k);
then in the on create of your activity.. get extras and check which question you have to display...
i think you have an array of 10 questions..
In order to initialize preferences with default values from XML file describing the preferences, I can call PreferenceManager.setDefaultValues(this, R.xml.preference, false). Sounds simple, but I'm not quite sure when exactly should I call this?
As I understand from the docs, the above call is only needed once, in situation when no preferences are set yet. As a result of this call, preferences residing in /data/data/<myapp>/shared_prefs are going to be set, so all subsequent attempts to read preferences will get me the default values. Logically, setDefaultValues should be called in every single code path that might be executed without preferences being already initialized. Over time, this turned out to be multiple places - main activity, another activity, background service, small BroadcastReceiver handling system messages... Right now I've put call to setDefaultValues in onCreate() for my Application object, as I'm already using it as convenient singleton for other things.
Questions:
Do I have a guarantee that every time my code executes, Application object will be created and onCreate will run?
How are you dealing with this problem? One other way would be to hardcode default values into getFoo(key, defValue) calls, but that effectively scatters your default settings across whole code.
EDIT: Essentially, I don't know which solution is worse: calling setDefaultValues every time I access prefs in given code path, or calling it in some common place (like app's onCreate) every time, no matter whether I need it or not.
I'm going to delete my original answer and answer the questions that you actually asked.
Yes, the Application object's onCreate will be executed at the start of every process. Keep in mind that doesn't guarantee it will be run each time you start your main activity. If Android still has your process running it will use that again (e.g. you still have a service running). So yes what you're doing will work and you're correct in observing it won't blow up.
I'm dealing with this problem by subclassing SharedPreferences (let's call it MyPrefs -- that's not what I call it but that's not important). The key features of MyPrefs are:
encapsulation of get/set methods instead of directly accessing the key names
Handling code for loading defaults. I'm being a little lazy by using a static boolean instead of an AtomicBoolean to tell me if the defaults have been loaded.
Having said that... it works for me, but if you're almost certain you'll be calling the SharedPreferences every time your code runs where you're at works as good as any.
Hope this helps more than my previous answer.
I am building a game, that is step based, so I need to be able to create forward and back button.
Each step has an Id and the info for it is taken from and SQLite database. I am going to be saving maxLevel, the maximum reached step, as well as currentStep, the current step, in the shared preferences. Now I was wondering how do I start a new action that would take someone to the next/previous step, without clogging up the memory too much. I know I can start a new action and pass it data something like this:
final Context mContext=this;
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putInt("count", 2);
editor.commit();
Intent myIntent = new Intent(mContext, PlayGame.class);
mContext.startActivity(myIntent);
Now my main worry here is the memory usage. If I go forward 2 times using this, then back 2 times and then forward 2 times again, won't it create 6 instances of the activity? That could be a massive memory hog.
So the question here is:
How do I implement a forward/back system with 1 activity, that is relaunched and retrieves data from a database?
For more background info: The activity is a TabHost, which has 4 tabs, and each of these tabs has an activity inside it. Each activity retrieves the data it needs from the database. Therefore my concern about the memory usage.
Edit:
I seem to have found a solution. I am calling the finish() function before I start the new activity. I do believe, that this should help avoid any leaks. am I right in thinking this?
Just a thought
How about running a back ground thread which will update your UI, Such as AsynTask. back and forward method will trigger background thread which will update your UI and can update data from SQL as well, By this you don't have to change activity
Helpful Links,
http://developer.android.com/resources/articles/painless-threading.html
http://topandroid.net/threads-handlers-asynctask
Happy Coding!
After your edition,
If your second activity has different data structure than this is a good idea to finish() as you are going to create new object anyway. But if data structure is same than probably using same objects and update their value will be more efficient.
One more edition, After updating your variables from background thread you may have to call set methods on your views for your UI to show a recent change. This is logical but just wanted to share it :)
Enjoy!
I'm using sharedPreferences to store some simple data that I access periodically. I've noticed that when I "put" something using the SharedPreferences.Editor (I do call commit()) when I later try to access the prefs with prefs.getAll() my newly added item is not there.
Oddly, if I close my app and fire it up again, it appears. It's like the prefs are not refreshing while my app is running. Is that by design? What gives?
BTW, I've noticed the same behavior while doing editor.remove("key"). I remove something (and call commit()), and when I call getAll() the deleted item is still in the Map returned. If I try to delete it again, my app force closes.
I must be doing something wrong. Any help is appreciated.
Thanks,
Bobby
This should not happen if you use SharedPreferences the right way. The only two scenarios I can think of when this happens is if:
commit() returns false (yes, it does really have a return value) or
you are trying to use the SharedPreferences across multiple processes, which is not supported yet.
Ok, my blunder. I was displaying the list of prefs in a freaking dialog. I missed that the dialog create was only called once so it never updated with my new values. Gah!
Thanks anyway!