Accessing SharedPreferences through static methods - android

I have some information stored as SharedPreferences. I need to access that information from outsite an Activity (in from a domain model class). So I created a static method in an Activity which I only use to get the shared preferences.
This is giving me some problems, since apparently it is not possible to call the method "getSharedPreferences" from a static method.
Here's the message eclipse is giving me:
Cannot make a static reference to the non-static method
getSharedPreferences(String, int) from the type ContextWrapper
I tried to work around this by using an Activity instance, like this:
public static SharedPreferences getSharedPreferences () {
Activity act = new Activity();
return act.getSharedPreferences("FILE", 0);
}
This code gives a null point exception.
Is there a work-around? Am I going into an android-code-smell by trying to do this?
Thanks in advance.

Cristian's answer is good, but if you want to be able to access your shared preferences from everywhere the right way would be:
Create a subclass of Application, e.g. public class MyApp extends Application {...
Set the android:name attribute of your <application> tag in the AndroidManifest.xml to point to your new class, e.g. android:name="MyApp" (so the class is recognized by Android)
In the onCreate() method of your app instance, save your context (e.g. this) to a static field named app and create a static method that returns this field, e.g. getApp(). You then can use this method later to get a context of your application and therefore get your shared preferences. :-)

That's because in this case, act is an object that you just create. You have to let Android do that for you; getSharedPreferences() is a method of Context, (Activity, Service and other classes extends from Context). So, you have to make your choice:
If the method is inside an activity or other kind of context:
getApplicationContext().getSharedPreferences("foo", 0);
If the method is outside an activity or other kind of context:
// you have to pass the context to it. In your case:
// this is inside a public class
public static SharedPreferences getSharedPreferences (Context ctxt) {
return ctxt.getSharedPreferences("FILE", 0);
}
// and, this is in your activity
YourClass.this.getSharedPreferences(YourClass.this.getApplicationContext());

I had a similar problem and I solved it by simply passing the current context to the static function:
public static void LoadData(Context context)
{
SharedPreferences SaveData = context.getSharedPreferences(FILENAME, MODE_PRIVATE);
Variable = SaveData.getInt("Variable", 0);
Variable1 = SaveData.getInt("Variable1", 0);
Variable2 = SaveData.getInt("Variable2", 0);
}
Since you are calling from outside of an activity, you'll need to save the context:
public static Context context;
And inside OnCreate:
context = this;
Storing the context as a static variable, can cause problems because when the class is destroyed so are the static variables. This sometimes happens when the app is interrupted and becomes low on memory. Just make sure that the context is always set before you attempt to use it even when the class setting the context is randomly destroyed.

Here's a better alternative to storing your shared preferences in static fields.
Similar to what has been suggested here, create a class that extends Application
Make the constructor for your class take Context as a parameter.
Use your context to get shared preferences and store them in private variables.
Create public variables to return the retrieved data.
e.g
public class UserInfo extends Application{
private String SAVED_USERID;
private String SAVED_USERNAME;
public UserInfo(Context context) {
SharedPreferences prefs = context.getSharedPreferences(FILE, MODE_PRIVATE);
SAVED_USERNAME = prefs.getString("UserName", null);
SAVED_USERID = prefs.getString("UserID", null);
}
public String getSavedUserName() {
return SAVED_USERNAME;
}
public String getSavedUserID() {
return SAVED_USERID;
}
}
usage in your activity
UserInfo user = new UserInfo(this.getApplicationContext());
String SAVED_USERNAME = user.getSavedUserName();
String SAVED_USERID = user.getSavedUserID();

I had the same need - some of my preferences need to be accessed often, and efficiently. I also imagine that reading and writing a string from SharedPreferences is slightly slower than getting and setting a static variable (but likely to an insignificant degree). I also just kind of got used to using static fields, retrieving Preference values only at startup, and saving them on close.
I didn't love my options for keeping static references to the SharedPreferences/contexts directly, but so far this workaround has sufficed.
My solution:
Create a Settings class with all the static variables you need.
When the application initializes, retrieve SharedPreferences fields and immediately set all Settings fields (I call a "loadSharedPrefs()" method at the end of MainActivity's onCreate method).
In the SettingsActivity's preferenceChangeListener's initialization, set the appropriate static field in the Settings class. (I call a "setAppropriateSetting(key, value)" method at the beginning of SettingsActivity's onPreferenceChange()).
Use your static preferences wherever, whenever!

public static String getPreferenceValue(Context context) {
SharedPreferences sharedPreferences =
PreferenceManager.getDefaultSharedPreferences(context);
String key = context.getString(R.string.pref_key);
String defaultVal = context.getString(R.string.pref_default);
return sharedPreferences.getString(key,defaulVal);
}

Related

How to access sharedpreference value in non-Activity Java class?

I want to get Shared preference values in non-Activity Url Constants class where I need to check url which will come from a previous Activity. I am using common Shared Preference Utility class where I am using Shared Preference Manager to put and get values through shared preferences; however whenever I try to access shared preference value in Url constants class, I cannot access the common shared preference utility class. How can I get the value ? Please help.
My Shared Preference class is:
public class Preference {
private static final String PREFIX = "json";
public static void setString(String key, String value, Context context) {
SharedPreferences prefs =
PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = prefs.edit();
editor.putString(key, value);
editor.apply();
}
public static String getString(String key, Context context) {
SharedPreferences preferences =
PreferenceManager.getDefaultSharedPreferences(context);
return preferences.getString(key, null);
}
You should pass context, instead doing any hacks with context
Ideally, we should get this done once and access it anywhere. What I mean is we can create a single instance of Sharedpreference when starting Application class and make any calls on this object.
public class AppController extends Application {
static AppController appController;
public static AppController getInstance(){
return appController;
}
#Override
public void onCreate() {
super.onCreate();
Fabric.with(this, new Crashlytics());
appController = this;
}
}
get it named in manifest first before use in application tag with name attribute.
<application
android:name=".AppController"
......
......
</application>
So we can initialize here our SharedPreference or get application instance like this. We can also use Dagger for this and do much more.
AppController.getInstance().getApplicationContext().getSharedPreferences("userdetails", MODE_PRIVATE);
OR
we can just pass context to some constructor of a non-actvitiy/fragment class.
AS show your code you pass context that's not problem but the problem with your class name Preference it should be similar to java inbuilt class like java.android.preference and java.util.pref so make sure you are pointing your project class not android java default calss. or you may try to change your util class with other

How to use sharedpreference from its class?

I store a value in Activity class from a spinner , is it possible to get the value without context ?
SharedPreference.class
public static int getPreferencedCurrency(){
SharedPreferences prefs = getSharedPreferences(Constants.SHARED_PREFERENCES_NAME,Context.MODE_PRIVATE);
return prefs.getInt(Constants.CURRENCY_PREFERRED,0);
}
Error on getSharedPreferences
What exactly do you mean by storing a value in the SP class? What exactly do you need to do with that value?
You can easily store a value in the SP of your app, and it will always be accessable.
I sounds like you should pass a Context into the constructor of your class, preferably the Application Context to avoid a memory leak, and use it when you need to access the SharedPreferences.
Something like this:
public class SomeClass{
private Context con;
public SomeClass(Context c){
this.con = c;
}
public static int getPreferencedCurrency(){
SharedPreferences prefs = con.getSharedPreferences(Constants.SHARED_PREFERENCES_NAME,Context.MODE_PRIVATE);
return prefs.getInt(Constants.CURRENCY_PREFERRED,0);
}
}
Use the Application Context when initializing an instance of the class:
SomeClass sc = new SomeClass(getApplicationContext());

Referencing a non-static method in a static class in Android - getSharedPref

I have following code :
Context context = Activity.getApplicationContext();
SharedPreferences settings = context.getSharedPreferences("AutoMsgSharedPrefs", MODE_PRIVATE);
// Writing data to SharedPreferences
SharedPreferences.Editor editor = settings.edit();
editor.putString("key", "some value");
editor.commit();
I have been trying to use SharedPrefs to store messages given in - "Conversation" class as in sample - https://developer.android.com/samples/MessagingService/index.html. But, I get "can not reference non-static method from a static class if I try to achieve it in constructor of "Conversation" class. So How do I resolve this?
Here is the screenshot of error if I update as suggested :
Here
Context context = Activity.getApplicationContext();
This line not return a valid Context for your application to call getSharedPreferences.
To call getSharedPreferences from non Activity,Service,... classes you should need to pass valid context from application component like from Activity,Service,..
To get Context in Conversation use Conversation class constructor which is already created in given example you will need to add one more parameter:
Context mContext;
public Conversation(int conversationId, String participantName,
List<String> messages,Context mContext) {
.....
this.mContext=mContext;
}
Now use mContext to call getSharedPreferences method from Conversation class :
SharedPreferences settings = mContext.getSharedPreferences("AutoMsgSharedPrefs",
Context.MODE_PRIVATE);
To get the context no need to use Activity class. Change this code
Context context = Activity.getApplicationContext();
to
Context context = getApplicationContext();
Explanation: Activity class does not have a static method getApplicationContext(), because this method is non static, so you need to have an object instance. So call this method on Activity on Context instance.
As #ρяσѕρєя K already pointed out you have to somehow grant your non-Context class access to your Context instance. For example through introducing a new parameter.
public Conversation(int conversationId, String participantName,
List<String> messages, Context context) {
.....
}
But keep in mind:
It is discouraged to save references to long-life and heavy weight components like Contexts in your classes because this strong reference will exclude the context from the garbage collection and thus causing memory leaks.
So instead of storing your Context you can use it to initialize your Conversation object as you like and let the scope of the constructor take care of discarding the short-term reference to your Context.
If you should need a Context multiple times though, you could write a method which takes a Context instance as a parameter and call it to do the dirty work:
public void doStuff(Context context) {
// do your work here
}

Where should I synchronize on this static SharedPreference helper class?

I have created a class holding SharedPreferences access in a static manner. Looking at the AOSP ContextImpl.java's SharedPreferenceImpl, I see that synchronized(this) is used when put and get are executed.
Should I still add synchronized somewhere in my code below?
public class AppPreferences {
// Get static SharedPreferences Editor
private static Editor getEditor(Context ctx) {
return PreferenceManager.getDefaultSharedPreferences(ctx).edit();
}
// Get static SharedPreferences
private static SharedPreferences getPref(Context ctx) {
return PreferenceManager.getDefaultSharedPreferences(ctx);
}
public static String getUserName(Context ctx, String defaul) {
return getPref(ctx).getString("user_name", defaul);
}
public static void setUserName(Context ctx, String text) {
getEditor(ctx).putString("user_name", text).commit();
}
}
In android.app.ContextImpl there is a static field
private static final HashMap<String, SharedPreferencesImpl> sSharedPrefs =
new HashMap<String, SharedPreferencesImpl>();
(aside private static final HashMap ?! /aside).
This is populated here. So all threads in an application sharing the same context (I have asked here but I am still not quite 100% certain) will share this static map of SharedPreferencesImpl instances - Now whenever you call edit() you get a new EditorImpl instance - so in the "synchronized(this)" you refer to in your question the this refers to the instance of EditorImpl at hand - which does not do much - it just synchronizes access to the internal map of the EditorImpl. But the (different) editors synchronize on the (common) SharedPreferencesImpl instance when they are about to modify this (SharedPreferencesImpl) instance. So in commit() for instance commitToMemory() is called where the synchronization is on SharedPreferencesImpl.this. Keep in mind though that the writes to disk are enqueued in random order (see the javadoc for enqueueDiskWriteSo and notice in commit that no lock is held between writing to memory and enqueing for write to disk). So you should be safe modifying the preferences as long as you do not depend on order of modifications and do not depend on atomically checking and setting a preference value (which needs synchronizing of your own)
NB the code I quote is for 2.3.1_r1 - hopefully still valid

How to use getString() on static String before onCreate()?

I am trying to use getString() to get an String from resources to assign it to an String array before my activity is created:
private static final String[] MenuNames = {
Resources.getSystem().getString(R.string.LCMeterMenu),
Resources.getSystem().getString(R.string.FrecMenu),
Resources.getSystem().getString(R.string.LogicAnalyzerMenu),
"Prueba con achartengine",
Resources.getSystem().getString(R.string.BrazoMenu)
};
When I use Resources.getSystem().getString(R.string.LCMeterMenu), Eclipse doesn't complain but I get an error at runtime:
Caused by: android.content.res.Resources$NotFoundException: String Resource ID #0x7f0a000a
But if I put inside onCreate():
Log.i("StringR", "String: " + getString(R.string.LCMeterMenu));
I get the String but I can't assign it to the final String I defined before. If I use only getString() before onCreate() I get and static error message. How can I use resources before onCreate() for global variables?
You cannot initialize a static final field from resources; the field needs to be initialized at the time the class is initialized and that happens before the application resources have been bound at run time. (By the way, the reason you cannot use Resources.getSystem() is that the Resources object you obtain that way contains only system resources, not any application resources.)
If you need those strings available before the application resources are bound, the only practical thing to do is to put the strings into the code directly. However, the "Android way" would be to organize your code so initialization only needs to happen during (or after) onCreate(). Just initialize the string array in onCreate() and don't worry about making the fields static or final.
If you don't want the string array to be associated with a particular activity, then you can subclass Application and read the array from resources inside the application class's onCreate() method. (You also need to declare your custom application class in the manifest.) However, the docs recommend against such an approach. (Since the array is private, I suspect that it is closely tied to a single activity anyway, so the use of an Application subclass doesn't seem warranted.)
An alternative is to declare a singleton class for your array. The singleton accessor function then needs a Context so it can retrieve the resources if necessary:
public class StringArray {
private static String[] theArray;
public static String[] getArray(Context context) {
if (theArray == null) {
theArray = context.getResources().getStringArray(R.array.my_strings);
}
return theArray;
}
}
(This assumes the string data are defined in a <string-array> resource like #JaiSoni suggested in his answer.) Once again, the member field cannot be declared final.
No, you can't use Resources before onCreate(). You can get the instance of Resources in onCreate() by using getResources() where you can get all the Strings. Also the strings are already declared as static by defining them in the strings.xml.
Pseudo code for accessing the Resources,
Resources res = getResources();
String app_name = res.getString(R.string.app_name);
Another approach could be to initialize the static array with resource identifiers (which are already available as opposed to the resources themselves).
private static final int[] MenuNames = {
R.string.LCMeterMenu,
R.string.FrecMenu,
...
};
This way, you can defer the loading of resources to when they are actually available:
String s = getResources().getString(MenuNames[i]);
The following is a working approach to initialize static final variables in android from XML, such as strings.xml.
Subclass application and provide a "static context"
Register the application class in manifest
Use the static context to initialize your constants
1. MyApplication.java
public abstract class MyApplication extends Application {
private static Context context;
#Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
}
/**
* Returns a "static" application context. Don't try to create dialogs on
* this, it's not gonna work!
*
* #return
*/
public static Context getContext() {
return context;
}
}
2. AndroidManifest.xml
<application
android:name=".android.application.MyApplication"
<!-- ... -->
</application>
3. Your application code, e.g. Activity
private static final String[] MenuNames = {
getContext().getString(R.string.LCMeterMenu),
getContext().getString(R.string.FrecMenu),
getContext().getString(R.string.LogicAnalyzerMenu),
"Prueba con achartengine",
getContext().getString(R.string.BrazoMenu)
};
protected static Context getContext() {
return MyApplication.getContext();
}
For working examples refer to AbstractApplication and PreferencesServiceSharedPreferences.
Note that this approach also has its downsides:
Apart from being opposed to the "Android way" (as #Ted Hopp suggested in his answer),
it makes testing a bit difficult. That is why the call to MyApplication.getContext() is wrapped in another method. As it is a static method, overriding it in testing code is not simple. But you could use a framework such as Powermock for this purpose.
In addition it is a bit prone to NullPointerExceptions. As soon as the context is null (e.g. in your testing code) the application code crashes. One option to overcome this, is to do the initialization in a constructor, where you could react to getContext()returning null (see example).
Whatever you get by the getString(int resId) will already be a constant for your application. Why do you have to keep it in another final static variable. You can read it like that whenever you want, right?

Categories

Resources