Android - right way to store a value for repeated use? - android

I have a service in my app that is always running but the global static variables seem to get reset when the phone is idle for a while (possibly the app is getting closed). Please let me know the optimal way to store a value for repeated use, maybe once in 2-5 mins.
Will using a SharedPreference cause high overhead if accessed once in 2-5 mins ?
Appreciate your help.

SharedPreference is best option.
public class AppPreference {
public static final String APP_NAME_KEY= "your_app_name";
public static final String SAMPLE_KEY = "sample";
public SharedPreferences preferences;
private SharedPreferences.Editor editor;
private String sample;
public AppPreference(Context context) {
preferences = context.getSharedPreferences(APP_NAME_KEY, Context.MODE_PRIVATE);
editor = preferences.edit();
}
public void setSample(String sample) {
this.sample= sample;
editor.putString(SAMPLE_KEY , this.sample);
editor.commit();
}
public String getSample() {
return preferences.getString(SAMPLE_KEY, null);
}
}
You can use Integer, Float, boolean values according to your requirement.

Related

Is it safe to keep a static reference to a SharedPreferences and its Editor?

Im going to make something like:
private static SharedPreferences sharedPreferencesInstance;
public static SharedPreferences getSharedPreferences(final Context context){
if (context==null)
return sharedPreferencesInstance;
if (sharedPreferencesInstance == null)
sharedPreferencesInstance = context.getApplicationContext().getSharedPreferences("prefs", Context.MODE_PRIVATE);
return sharedPreferencesInstance;
}
private static SharedPreferences.Editor sharedPreferencesEditorInstance;
public static SharedPreferences.Editor getSharedPreferencesEditor(final Context context){
if (context==null)
return sharedPreferencesEditorInstance;
if (sharedPreferencesEditorInstance == null)
sharedPreferencesEditorInstance = context.getApplicationContext().getSharedPreferences("prefs", Context.MODE_PRIVATE).edit();
return sharedPreferencesEditorInstance;
}
but is it safe in meaning of Context leaks?
To answer the question authoritatively, it is safe to store the SharedPreferences instance as a static reference. According to the javadocs it is a singleton, so its source from getSharedPreferences is already a static reference.
It is not safe to store the SharedPreferences.Editor because it is possible two threads may be manipulating the same editor object at the same time. Granted, the damage this would cause is relatively minor if you happen to have already been doing it. Instead, get an instance of an editor in each editing method.
I highly recommend using a static reference to your Application object instead of passing in Context objects for every get. All instances of your Application class are singletons per process anyways, and passing around Context objects is usually bad practice because it tends to lead to memory leaks via reference holding, and is unnecessarily verbose.
Finally, to answer the unasked question if you should lazily-load or greedily-initialize the reference to your static SharedPreferences, you should lazily load in a static getter method. It may work to greedily-initialize a reference with final static SharedPreferences sReference = YourApplication.getInstance().getSharedPreferences() depending on the chain of class imports, but it would be too easy for the class loader to initialize the reference before the Application has already called onCreate (where you would initialize the YourApplication reference), causing a null-pointer exception. In summary:
class YourApplication {
private static YourApplication sInstance;
public void onCreate() {
super.onCreate();
sInstance = this;
}
public static YourApplication get() {
return sInstance;
}
}
class YourPreferencesClass {
private static YourPreferencesClass sInstance;
private final SharedPreferences mPrefs;
public static YourPreferencesClass get() {
if (sInstance == null)
sInstance = new YourPreferencesClass();
return sInstance;
}
private final YourPreferencesClass() {
mPrefs = YourApplication.get().getSharedPreferences("Prefs", 0);
}
public void setValue(int value) {
mPrefs.edit().putInt("value", value).apply();
}
public int getValue() {
return mPrefs.getInt("value", 0);
}
}
You will then use your statically available preferences class as such:
YourPreferencesClass.get().setValue(1);
A final word about the thread-safety and memory observability. Some astute observers may notice that YourPreferencesClass.get() isn't synchronized, and hence dangerous because two threads may initialize two different objects. However, you can safely avoid synchronization. As I mentioned earlier, getSharedPreferences already returns a single static reference, so even in the extremely rare case of sInstance being set twice, the same underlying reference to SharedPreferences is used. Regarding the static instance of YourApplication.sInstance, it is also safe without synchronization or the volatile keyword. There are no user threads in your application running before YourApplication.onCreate, and therefore the happens-before relationship defined for newly created threads ensures that the static reference will be visible to all future threads that may access said reference.
I think it is safe. I always use a "KeyStoreController" with a static reference to a SharedPreferences object (singleton). I would suggest you to use an Application context instead of passing a context every time. This is an example of my code:
public class KeyStoreController{
private static KeyStoreController singleton = null;
private SharedPreferences preferences = null;
private KeyStoreController(Context c){
preferences = PreferenceManager.getDefaultSharedPreferences(c);
}
public static KeyStoreController getKeyStore(){
if( singleton == null){
singleton = new KeyStoreController(MainApplication.getContext());
}
return singleton;
}
public void setPreference(String key, Object value) {
// The SharedPreferences editor - must use commit() to submit changes
SharedPreferences.Editor editor = preferences.edit();
if(value instanceof Integer )
editor.putInt(key, ((Integer) value).intValue());
else if (value instanceof String)
editor.putString(key, (String)value);
else if (value instanceof Boolean)
editor.putBoolean(key, (Boolean)value);
else if (value instanceof Long)
editor.putLong(key, (Long)value);
editor.commit();
}
public int getInt(String key, int defaultValue) {
return preferences.getInt(key, defaultValue);
}
public String getString(String key, String defaultValue) {
return preferences.getString(key, defaultValue);
}
public boolean getBoolean(String key, boolean defaultValue) {
return preferences.getBoolean(key, defaultValue);
}
public long getLong(String key, long defaultValue) {
return preferences.getLong(key, defaultValue);
}
If you are passing around the Context it is best to be passing along an ApplicationContext. It might be easier if you just make a static ApplicationContext to reference and then just use the SharedPreferences when you need them from within your classes (if that approach works for you).
If you have the argument for the calls a Context then you shouldn't have to worry about leaks unless you are holding on to it.
But, I think that you will be just fine doing what you are doing conceptually.
Why not just create a static class and use it as a utility so you never have to keep a reference to your SharedPreferences at all. You also never have to initialize an instance of this class and can just call PreferencesUtil.getUserName(context) so long as you have a context to supply.
public static class PreferencesUtil{
private static final String USER_NAME_KEY = "uname";
public static void setUserName(String name, Context c){
SharedPreferences sharedPref = getPreferences(c);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString(USER_NAME_KEY, name);
editor.commit();
}
public static String getUserName(Context c){
return getPreferences(c).getString(USER_NAME_KEY, "");
}
private SharedPreferences getPreferences(Context context){
return context.getPreferences(Context.MODE_PRIVATE);
}
}

In libgdx on Android, how do I save game state in case app is killed?

In Android, we usually save all of our state variables in the onSaveInstanceState() callback by putting them in the Bundle provided.
How do people deal with saving/loading game state variables with libGDX as the classes can't use/return a Bundle object?
This is quit simple! You can use Preferences for that. You can store values in the Preferences. On android the backend uses the SharedPreferences from Android itself. On desktop its saved as an xml somewhere in the user folder.
I wrote a simple Helper to save options and get the options of my game. Here is some code out of it. (Note, dont forget to flush after saving something)
public class PreferencesHelper {
public final static String PREF_NAME_OPTION = "options";
private final static String VOLUMEN = "volumen";
private final static String VIBRATE = "vibrate";
private final static String EFFECT_VOLUMEN = "effect";
private final static String FIRST_START = "start";
private Preferences optionPref = Gdx.app.getPreferences(PREF_NAME_OPTION);;
public PreferencesHelper() {
optionPref = Gdx.app.getPreferences(PREF_NAME_OPTION);
}
public float getVolumen() {
return optionPref.getFloat(VOLUMEN);
}
public void setVolumen(float vol) {
optionPref.putFloat(VOLUMEN, vol);
optionPref.flush();
}
public boolean getVibrate() {
return optionPref.getBoolean(VIBRATE);
}
public void setVibrate(boolean vibr) {
optionPref.putBoolean(VIBRATE, vibr);
optionPref.flush();
}
public float getEffectVolumen() {
return optionPref.getFloat(EFFECT_VOLUMEN);
}
public void setEffectVolumen(float eff) {
optionPref.putFloat(EFFECT_VOLUMEN, eff);
optionPref.flush();
}
}
This is how i save my options. To save an character you do the same but save all importand stuff you need, to recreate your character when loading the game again. You can also have more than one prefrerence!
I hope this helped.

is SharedPreferences access time consuming?

I'm currently trying to test a third party service for my app, and need to identify each test that is being done at every specific run.
Since more than one test can take place every time I run the testApp, I need to Identify every test.
What I thought of, is storing the device name and build (not many devices here), and an index for each test.
private String getTestId(){
SharedPreferences settings = getPreferences(0);
SharedPreferences.Editor editor = settings.edit();
int testNumber = settings.getInt("id", 0);
editor.putInt("id", testNumber+1);
editor.commit();
String id = Build.DEVICE + Build.VERSION.RELEASE+" - test number: "+testNumber;
return id;
}
Is running this function every time I run a test time consuming, or can I do this without fearing the coast?
if the answer is "time consuming", what would you suggest I do every time I run a test in order to differentiate every test?
About SharedPreferences.
SharedPreferences caches after first load, so disk access to load data will take time but once. You can try to load SharedPreferences early in your test suite to avoid this penalty.
For persisting your data you should opt for SharedPreferences.Editor.apply() instead of SharedPreferences.Editor.commit() since appy is asynchronous. But please do read the documentation about both to see which one applies in your case.
The question already has an answer, but in case others come and are looking for a code sample, I put together this utility class for interacting with the SharedPreferences.
Calling commit() will use the apply() method if it's available, otherwise it will default back to commit() on older devices:
public class PreferencesUtil {
SharedPreferences prefs;
SharedPreferences.Editor prefsEditor;
private Context mAppContext;
private static PreferencesUtil sInstance;
private boolean mUseApply;
//Set to private
private PreferencesUtil(Context context) {
mAppContext = context.getApplicationContext();
prefs = PreferenceManager.getDefaultSharedPreferences(mAppContext);
prefsEditor = prefs.edit();
//Indicator whether or not the apply() method is available in the current API Version
mUseApply = Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD;
}
public static PreferencesUtil getInstance(Context context) {
if (sInstance == null) {
sInstance = new PreferencesUtil(context);
}
return sInstance;
}
public boolean getBoolean(String key, boolean defValue) {
return prefs.getBoolean(key, defValue);
}
public int getInt(String key, int defValue) {
return prefs.getInt(key, defValue);
}
public String getString(String key, String defValue) {
return prefs.getString(key, defValue);
}
public String getString(String key) {
return prefs.getString(key, "");
}
public void putBoolean(String key, boolean value) {
prefsEditor.putBoolean(key, value);
}
public void putInt(String key, int value) {
prefsEditor.putInt(key, value);
}
public void putString(String key, String value) {
prefsEditor.putString(key, value);
}
/**
* Sincle API Level 9, apply() has been provided for asynchronous operations.
* If not available, fallback to the synchronous commit()
*/
public void commit() {
if (mUseApply)
//Since API Level 9, apply() is provided for asynchronous operations
prefsEditor.apply();
else
//Fallback to syncrhonous if not available
prefsEditor.commit();
}
}
I've noticed that when you use methods like putInt() the first time for a specific key it can take a significant amount of time. Besides, it should be equivalent to any other ways of writing to a file.

Initialization of static variables in a class of utility functions

For my Android application, I have written a class which is composed of utility functions which are needed at various activites in the application.In this class, I need a context variable(for working with files) and an instance of preference manager and preference editor.Also, a long integer represnting the current date as a timestamp is needed:
private static long today;
private static Context myContext;
private static SharedPreferences sharedPrefs;
private static Editor editor;
Which is correct way to initialize these variables. I have tried doing it via a private constructor as shown below, but I am getting errrors.
private NetworkController()
{
//Getting the Unix timestamp for today
GregorianCalendar aDate = new GregorianCalendar();
GregorianCalendar tDate = new
GregorianCalendar(aDate.get(Calendar.YEAR),aDate.get(Calendar.MONTH),
aDate.get(Calendar.DAY_OF_MONTH), 0, 0, 0);
today = (tDate.getTimeInMillis())/1000;
//The preferences manager for reading in the preferences
sharedPrefs = PreferenceManager.getDefaultSharedPreferences(myContext);
//The preferences editor for modifying the preferences values
editor = sharedPrefs.edit();
}
One approach would be to create an instance of this class in every activity where its used but I don,t want to do that.Any other approach is possible?
If you have a set of things that you use everywhere and only want one instance of, you can use what's called a singleton. For example, here is a very simple one that holds an integer called level:
public class Utility {
private static Utility theInstance;
public int level;
private Utility() {
level = 1;
}
public static getUtility() {
if (theInstance == null) {
theInstance = new Utility();
}
return theInstance;
}
}
Then you can use this like:
Utility u = Utility.getUtility();
u.level++;
However, many people discourage the use of singletons, since they can lead to confusing program behaviour. A good article on this topic is Singletons are Pathological Liars. Singletons can be useful in some situations, but you should be aware of the traps involved in using them.
#Greg is right, just don't use any static stuff for what you want to do. There is no reason you don't want to have normal objects here. Pass the context as parameter and instanciate you objects when you need them to serve you :
private long today;
private Context myContext;
private SharedPreferences sharedPrefs;
private Editor editor;
public NetworkController( Context context )
{
this.context = context;
//Getting the Unix timestamp for today
GregorianCalendar aDate = new GregorianCalendar();
GregorianCalendar tDate = new
GregorianCalendar(aDate.get(Calendar.YEAR),aDate.get(Calendar.MONTH),
aDate.get(Calendar.DAY_OF_MONTH), 0, 0, 0);
today = (tDate.getTimeInMillis())/1000;
//The preferences manager for reading in the preferences
sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this.context);
//The preferences editor for modifying the preferences values
editor = sharedPrefs.edit();
}
Singletons are a bad way of programming things, it makes things very hard to test. Even you don't yet use tests, don't use singletons, there lead to very poor quality code and real ball of muds when things get more complicated.
Here you can do this:
public class NetworkController {
SharedPreferences settings;
SharedPreferences.Editor editor;
public NetworkController(Context context){
settings = PreferenceManager.getDefaultSharedPreferences(context);
editor = settings.edit();
}
public void saveName(String name){
editor.putString("name", name).commit();
}
public String getName(){
return settings.getString("name");
}
public static long getTimeStamp(){
return System.currentTimeMillis();
}
}
You can use the class like below:
NetworkController prefs = new NetworkController(context); // Context being an Activity or Application
prefs.saveName("blundell");
System.out.println(prefs.getName()); // Prints 'blundell';
System.out.println(NetworkController.getTimeStamp()); // Prints 1294931209000
If you don't want to create an instance in every class you could create on instance in your Application and always reference that:
public class MyApplication extends Application {
private NetworkController myPrefs;
public NetworkController getPrefs(){
if(myPrefs == null){ // This is called lazy initialization
myPrefs = new NetworkController(this); // This uses the Application as the context, so you don't have issues when Activitys are closed or destroyed
}
return myPrefs;
}
}
You need to add the MyApplication to your manifest:
<application
android:name="com.your.package.MyApplication"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name">
To use this single instance you would do this:
public class MyActivity extends Activity {
public void onCreate(Bundle savedInstanceState){
super(savedInstanceState);
NetworkController prefs = ((NetworkController) getApplicationContext()).getPrefs();
// use this object just like shown above
prefs.saveName("blundell"); // etc
}
}
There's already a bunch of good suggestions posted here, but I suppose another approach for these kind of 'utility'/'helper' functions is to simply pass in the parameters you need the logic to work on. In your case, in stead of trying to make the logic work on a local Context reference, you could simply pass it in:
public static void NetworkController(Context context) {
//Getting the Unix timestamp for today
GregorianCalendar aDate = new GregorianCalendar();
GregorianCalendar tDate = new
GregorianCalendar(aDate.get(Calendar.YEAR),aDate.get(Calendar.MONTH),
aDate.get(Calendar.DAY_OF_MONTH), 0, 0, 0);
long today = (tDate.getTimeInMillis())/1000;
//The preferences editor for modifying the preferences values
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
...
}
The other variables you can calculate/deduce on the fly. It'll probably mean a bit more garbage collection, but should be relatively safe in terms of memory management.

Read shared preference when context is changed

I have a DialogView which stores settings in shared preferences. It is located in package A and i have another activity which is located in package B, which should be able to read these preferences.
So I created a wrapper class, which takes context and shared preference name and retrive these settings. When shared preferences are set at the first time everything works great, but when I change it, I got the same result, which was set at first time.
Problem is I save preference in one process and need to be able to read them in another.
So it seems like Context has changed and I am not able to retrive new context. What should I do to get up to-date shared preference?
Thank you on advance.
Please take a look at my wrapper class
public class PhotoAppWidgetSettingsProxy extends Proxy {
private final static String PREFERENCES_NAME = PhotoAppWidgetSettingsProxy.class.getName();
private final static int PREFERENCES_MODE = Context.MODE_PRIVATE;
private Context mCtx = null;
private SharedPreferences pref = null;
private SharedPreferences.Editor editor = null;
public PhotoAppWidgetSettingsProxy(String name, Context context) {
super(name, context);
mCtx = context;
pref = context.getSharedPreferences(PREFERENCES_NAME, PREFERENCES_MODE);
editor = pref.edit();
}
private final static String FRAME = "FRAME";
/**
* Sets selected frame mode
* #param frame id
*/
public void setFrameMode(int frameId){
editor.putInt(FRAME, frameId);
Log.d(PREFERENCES_NAME, "SET MODE="+frameId);
boolean success = editor.commit();
Log.d(PREFERENCES_NAME, "SET MODE="+success);
}
/**
* Gets selected frame mode
* #return frame id
*/
public int getFrameMode(){
Log.d(PREFERENCES_NAME, "GET MODE="+pref.getInt(FRAME, 0));
return pref.getInt(FRAME, 0);
}
SOLVED:
private final static int PREFERENCES_MODE = Context.MODE_MULTI_PROCESS;
private final static int PREFERENCES_MODE = Context.MODE_MULTI_PROCESS;
When accessing shared preferences/values, I have found it useful to write a CustomApplication class extending Application. I can place any necessary fields/methods in there, and easily acquire them from any of the other Android classes by using:
CustomApplication app = (CustomApplication) getApplication();
int x = app.getX();
Does that help you at all?

Categories

Resources