This is my first time using SharedPreferences in my Android app. Since I will be using the SharedPreferences over and over again, I have created a utility class called SharedPreferencesUtil which contains a lot of static methods which allow me to access and modify the values. For example:
/**
* This method is used to add an event that the user
* is looking forward to. We use the objectId of a ParseObject
* because every object has a unique ID which helps to identify the
* event.
* #param objectId The id of the ParseObject that represents the event
*/
public static void addEventId(String objectId){
assert context != null;
prefs = context.getSharedPreferences(Fields.SHARED_PREFS_FILE, 0);
// Get a reference to the already existing set of objectIds (events)
Set<String> myEvents = prefs.getStringSet(Fields.MY_EVENTS, new HashSet<String>());
myEvents.add(objectId);
SharedPreferences.Editor editor = prefs.edit();
editor.putStringSet(Fields.MY_EVENTS, myEvents);
editor.commit();
}
I have a few of questions:
1. Is it a good decision to have a utility class SharedPreferencesUtil ?
2. Is the use of assert proper?
3. Is that how I will add a String to the set?
In general I think utility classes like this are fine. A few recommendations I'd have are:
Initialize your Context in a subclass of Application (in Application.onCreate()) and store a reference to that in your utility class. You don't have to worry about a memory leak if you ensure you only use the application context instead of an Activity context, and since SharedPreferences doesn't use any theme attributes, there's no need to use an Activity context anyway.
Check and throw an exception warning that the class hasn't been initialized yet if you try to use it without initialization. This way you don't need to worry about checking for a null context. I'll show an example below.
public final class Preferences {
private static Context sContext;
private Preferences() {
throw new AssertionError("Utility class; do not instantiate.");
}
/**
* Used to initialize a context for this utility class. Recommended
* use is to initialize this in a subclass of Application in onCreate()
*
* #param context a context for resolving SharedPreferences; this
* will be weakened to use the Application context
*/
public static void initialize(Context context) {
sContext = context.getApplicationContext();
}
private static void ensureContext() {
if (sContext == null) {
throw new IllegalStateException("Must call initialize(Context) before using methods in this class.");
}
}
private static SharedPreferences getPreferences() {
ensureContext();
return sContext.getSharedPreferences(SHARED_PREFS_FILE, 0);
}
private static SharedPreferences.Editor getEditor() {
return getPreferences().edit();
}
public static void addEventId(String eventId) {
final Set<String> events = getPreferences().getStringSet(MY_EVENTS, new HashSet<String>());
if (events.add(eventId)) {
// Only update the set if it was modified
getEditor().putStringSet(MY_EVENTS, events).apply();
}
}
public static Set<String> getEventIds() {
return getPreferences().getStringSet(MY_EVENTS, new HashSet<String>());
}
}
Basically, this avoids you having to always have a Context on hand to use SharedPreferences. Instead, it always retains a reference to the application context (provided you initialize it in Application.onCreate()).
You can check out how to use SharedPreferences properly here and check another example on the Android docs.
EDIT: As to your design question, it really shouldn't matter to have a static class or not.
Your SharedPreferences are shared throughout the app, and although you can create multiple SharedPreferences objects, they will essentially store and save to the same part of your app as if you just used one object.
Related
I have been looking around for an answer to this question and found a number of similar questions but none seemed to be quite similar enough and none of the solutions I found around the web solved the problem.
For an Android app I am writing, I need to save the Android preferences to a database. In order to do this I wanted to create a separate class with functions that I can call to sync, update etc the preferences. So this is the problem
Call initiateSaveSettings() in the syncSettings class from the mainActivity
The way I try to do this now is by calling:
syncSettings sync = new syncSettings();
sync.initiateSettingSave();
Into the syncSettings class:
public class syncSettings extends Context {
public void initiateSettingsSave(){
PreferenceManager.setDefaultValues(syncSettings.this, R.xml.root_preferences, false);
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
}
}
etc.
The problem is that the SharedPreferences requires the extends Context and in order for that to work Android Studio gives the error to change syncSettings to
public abstract class syncSettings extends Context {
Doing this gives an error for new syncSettings() when calling the function, taking the abstract away gives the error in the syncSettings class. What can I do to make this work? If you need more information please let me know.
Thank you in advance.
Edit: I am very much new to Android development so if I say or ask something dumb, that may very well be why...
Try this:
public class syncSettings {
private Context context;
public syncSettings(Context context){
this.context = context;
}
public void initiateSettingsSave(){
PreferenceManager.setDefaultValues(syncSettings.this, R.xml.root_preferences, false);
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
}
}
And in your activity:
syncSettings sync = new syncSettings(this);
sync.initiateSettingSave();
I don't understand your reason for extends Context Ten. But you can use this class.
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
public class PrefUtils {
/**
* Called to save supplied value in shared preferences against given key.
* #param context Context of caller activity
* #param key Key of value to save against
* #param value Value to save
*/
public static void saveToPrefs(Context context, String key, String value) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
final SharedPreferences.Editor editor = prefs.edit();
editor.putString(key,value);
editor.commit();
}
/**
* Called to retrieve required value from shared preferences, identified by given key.
* Default value will be returned of no value found or error occurred.
* #param context Context of caller activity
* #param key Key to find value against
* #param defaultValue Value to return if no data found against given key
* #return Return the value found against given key, default if not found or any error occurs
*/
public static String getFromPrefs(Context context, String key, String defaultValue) {
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
try {
return sharedPrefs.getString(key, defaultValue);
} catch (Exception e) {
e.printStackTrace();
return defaultValue;
}
}
/**
*
* #param context Context of caller activity
* #param key Key to delete from SharedPreferences
*/
public static void removeFromPrefs(Context context, String key) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
final SharedPreferences.Editor editor = prefs.edit();
editor.remove(key);
editor.commit();
}
}
I am trying to save user's language using SharedPreference. MainActivity is my main activity and SettingsActivity is where I ask and save user's language.
Saving:
private SharedPreferences preferenceSettings;
private SharedPreferences.Editor preferenceEditor;
private static final int PREFERENCE_MODE_PRIVATE = 0;
public void save(String lg){
preferenceSettings = getPreferences(PREFERENCE_MODE_PRIVATE);
preferenceEditor = preferenceSettings.edit();
preferenceEditor.putString("language", lg);
preferenceEditor.commit();
finish();
}
Reading:
preferenceSettings = getPreferences(PREFERENCE_MODE_PRIVATE);
String LanguageS = preferenceSettings.getString("language", "0");
with this code, I can successfully save and read from same activity(SettingsActivity) but when I return to my main activity, I can't read.
I am using this code to read from my main activity but it always returns "0".
private SharedPreferences preferenceSettings;
private SharedPreferences.Editor preferenceEditor;
private static final int PREFERENCE_MODE_PRIVATE = 0;
private static String Lang = "0";
preferenceSettings = getPreferences(PREFERENCE_MODE_PRIVATE);
Lang = preferenceSettings.getString("language", "0");
What am I doing wrong? I am controlling these lines for almost 1 hour but couldn't find any mistakes.
This is happening beacuse, the method that you're using getPreferences(PREFERENCE_MODE_PRIVATE)
returns the preferences saved by Activity's class name as described here :
Retrieve a SharedPreferences object for accessing preferences that are private to this activity. This simply calls the underlying getSharedPreferences(String, int) method by passing in this activity's class name as the preferences name.
So, when you're saving the prefs in SettingsActivity it's being saved under the name "SettingsActivity"
but when you're getting the prefs in MainActivity it's returning you the prefs saved under name "MainActivity"
So, you should use getSharedPreferences (String name, int mode) method with the same name instead.
Instead of using getPreferences(int mode), you should use the getSharedPreferences(String name, int mode)
If you read the java doc on the getPreferences(int mode) it says:
Retrieve a {#link SharedPreferences} object for accessing preferences
that are private to this activity.
This simply calls the underlying
{#link #getSharedPreferences(String, int)} method by
passing in this activity's class name as the preferences name. #param
mode Operating mode.
Use {#link #MODE_PRIVATE} for the default
operation, {#link #MODE_WORLD_READABLE} and {#link
MODE_WORLD_WRITEABLE} to control permissions.
#return Returns the single SharedPreferences instance that can be used
to retrieve and modify the preference values.
Currently, I have a class whose constructor takes a username, password, and Context. I would like to be able to access this object from anywhere, so I was thinking of implementing a singleton pattern.
The current constructor uses the credentials passed in to authenticate future api calls through that class. If I were to implement the singleton pattern, my first thought would to be have the getInstace() method take a username, password, etc.., but it seems wrong to have to pass that info everytime i grab an instance. Because of this I was thinking of adding some sort of .authenticate(usr, pswrd) method to be called when grabbing the first instance.
My question is, is this the correct approach? If not, what would be a good way to handle this? Heres the current code:
constructor:
public Play(String username, String password, Context context) {
api = getApi(username, password);
Intent intent = new Intent(context, MyService.class);
context.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
//check if first run
//if so, call api for info and store locally
//if not, update as needed
SharedPreferences pref = context.getSharedPreferences("pref", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = pref.edit();
if (pref.getBoolean("first_run", true)) {
loadInitialData(context);
}
editor.putBoolean("first_run", false);
editor.commit();
}
Singleton pattern restricts the instantiation of a class and ensures that only one instance of the class exists in the java virtual machine. The singleton class must provide a global access point to get the instance of the class. Singleton pattern is used for logging, drivers objects, caching and thread pool
This code is not tested but should give you an idea how you can use singleton pattern while using SharedPrefrencess.
Constructor is private, So only getInstance() method can access the instance, so you will create an instance of this class if it doesn't exists or if instantiated previously use that instance
synchronization is required to make sure when multiple thread are trying to make a instance for the first time
import android.content.Context;
import android.content.SharedPreferences;
/**
* Created by Pankaj Nimgade on 23-05-2016.
*/
public class Play {
/**
* volatile keyword ensures that multiple threads handle the uniqueInstance
* variable correctly when it is being initialized to Singleton instance
*/
private volatile static Play play;
private static final String XML_FILE = "play_xml_file.xml";
private static final String KEY_DATA = "SOME_DATA_KEY";
private static final String KEY_USERNAME = "SOME_USERNAME_KEY";
private static final String KEY_PASSWORD = "SOME_PASSWORD_KEY";
private static SharedPreferences sharedPreferences;
private static SharedPreferences.Editor editor;
private Play() {
}
public static Play getInstance(Context context) {
if (play == null) {
synchronized (Play.class) {
if (play == null) {
sharedPreferences = context.getSharedPreferences(XML_FILE, Context.MODE_PRIVATE);
editor = sharedPreferences.edit();
play = new Play();
}
}
}
return play;
}
public boolean saveSomeData(String someData) {
editor.putString(KEY_DATA, someData);
return editor.commit();
}
public String readSomeData() {
return sharedPreferences.getString(KEY_DATA, "default Value");
}
public boolean saveUserNameData(String username) {
editor.putString(KEY_USERNAME, username);
return editor.commit();
}
public String readUserNameData() {
return sharedPreferences.getString(KEY_USERNAME, "default username Value");
}
public boolean savePasswordData(String password) {
editor.putString(KEY_PASSWORD, password);
return editor.commit();
}
public String readPasswordData() {
return sharedPreferences.getString(KEY_PASSWORD, "default password value");
}
}
in this above approach I am making instance creation of the class lazy, as the instance will only be created if demanded, although the code is thread safe and will work on all Java version you may want to consider different approach to implement this if you are using Java 5 and above.
https://sourcemaking.com/design_patterns/singleton/java/1
public class Singleton {
// Private constructor prevents instantiation from other classes
private Singleton() {}
/**
* SingletonHolder is loaded on the first execution of Singleton.getInstance()
* or the first access to SingletonHolder.INSTANCE, not before.
*/
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
The inner class is referenced no earlier (and therefore loaded no earlier by the class loader) than the moment that getInstance() is called. Thus, this solution is thread-safe without requiring special language constructs (i.e. volatile or synchronized).
I must pass an ArrayList from one Activity A to another Activity B.
I did it using getSerializableExtra and putExtra methods. I already know the meaning of these methods, but I don't know if stuff that I passed using them is stored permanently in the new activity or if it is necessary to reload activity A in order to retrieve my data in B.
So the question is: how can I load my data in a initial splash screen and then use it in all my others activity without reloading the splash screen?
Don't use Preference Class! Preferences are only used for settings values. For passing data to another Activity use Serializable or Parcelable. Remember that all the objects which will be passed to another activity have to implement Serializable or Parcelable. So you extend the ArrayList to a custom Class which implements Parcelable or Serializable.
You do this like this:
Intent intent = new Intent(getContext(), SomeClass.class);
intent.putSerializableExtra("value", <your serializable object>);
startActivity(intent);
and receive them like
YourObject yourObject = getIntent().getSerializableExtra("value")
or look here for Parcelable
Help with passing ArrayList and parcelable Activity
Data processed in Activity A does not need to process again in Activity B. If the data is computed in A and you send it to B computed, B receives it computed already.
Here are some ways to do it right: http://developer.android.com/guide/topics/data/data-storage.html
You can use Preference class, in which you can define its static instance. Than create variable according your desire datatype (even ArrayList). Make property for get and set of this variable.
Set the value on splash screen and get anywhere in application where you need.
Try this, if you need , I will upload code also.
I have written some code regarding that, it would help other activities to fetch data easily, use this when the data is not confidential,
public class HelperShared {
public static final String score = "Score";
public static final String tag_User_Machine = "tag_User_Machine",
tag_Machine_Machine = "tag_Machine_Machine",
tag_Draw_Machine = "tag_Draw_Machine",
tag_Total_Machine = "tag_Total_Machine";
public static SharedPreferences preferences;
public static Editor editor;
public HelperShared(Context context) {
this.preferences = context.getSharedPreferences(score,
Activity.MODE_PRIVATE);
this.editor = preferences.edit();
}
/*
* Getter and Setter methods for Machine
*/
public void setUserMachine(int UserMachine) {
editor.putInt(tag_User_Machine, UserMachine);
editor.commit();
}
public void setMachineMachine(int MachineMachine) {
editor.putInt(tag_Machine_Machine, MachineMachine);
editor.commit();
}
public void setDrawMachine(int DrawMachine) {
editor.putInt(tag_Draw_Machine, DrawMachine);
editor.commit();
}
public void setTotalMachine(int TotalMachine) {
editor.putInt(tag_Total_Machine, TotalMachine);
editor.commit();
}
public int getUserMachine() {
return preferences.getInt(tag_User_Machine, 0);
}
public int getMachineMachine() {
return preferences.getInt(tag_Machine_Machine, 0);
}
public int getDrawMachine() {
return preferences.getInt(tag_Draw_Machine, 0);
}
public int getTotalMachine() {
return preferences.getInt(tag_Total_Machine, 0);
}
}
if your question is "how can I load my data in a initial splash screen and then use it in all my others activity without reloading the splash screen?" Than I have better solutions for you.
Create a Class Memdata.java
public class Memdata{
private static Memdata instance = null;
private String userobject;
public static Memdata getInstance(){
if ( instance == null){
instance = new Memdata();
}
return instance;
}
public String getuserobject() {
return userobject;
}
public void setuserobject(String userobject) {
this.userobject= userobject;
}
}
on You Splash Screen' onCreate method, set the value
Memdata obj = Memdata.getInstance();
obj.setuserobject("hello");
Than in any activity, where you want to access this variable, just make its object and get value.
Like in MyActivity class
Memdata obj = Memdata.getInstance();
String str = obj.getuserobject()
You can define any type of variable according your requirement.
You can extend the base Application class and add member variables to it:
public class MyApp extends Application {
private String appLevelString;
public String getAppLevelString() {
return this.appLevelString;
}
public void setAppLevelString(String val) {
this.appLevelString= val;
}
}
You will have to update the manifest file as follows:
<application android:icon="#drawable/icon"
android:label="#string/app_name"
android:name="MyApp">
You can get and set data like this:
//For setting
((MyApp) this.getApplication()).setAppLevelString("Test string");
//For getting
String str = ((MyApp) this.getApplication()).getAppLevelString();
I am providing Settings of my JAVA project in a Prefs.java class with static attributes and static methods. But the token for OAuth2 needs to be assigned on runtime. Is this a good way to go... ?
public class Prefs {
//known before runtime
public static final String SERVER_BASE_URL ="http://api.mycompany.com/";
//needs to be set on startup through the setter method
private static String token;
public static String getToken() {
return token;
}
public static void setToken( String token ) {
Prefs.token = token;
}
public static String getXyEndpointUrl() {
return SERVER_BASE_URL + "/xy";
}
}
I would advice against such design. This type of static variables no better than global variables. This page gives a few reasons why you should avoid them. Here are a few of them.
Non-locality
No Access Control or Constraint Checking
Implicit coupling
Concurrency issues
Testing and Confinement
But the token for OAuth2 needs to be assigned on runtime. Is this a good way to go... ?
Here it really seems to me like you would want to pass such token to the constructor of the Prefs object.
Static variables are object-oriented substitutes for global variables in C. Try to avoid them whenever possible.
Many times you only need one object, in your case it's the Prefs object.
public class Prefs {
//known before runtime
public final String SERVER_BASE_URL ="http://api.mycompany.com/";
//needs to be set on startup through the setter method
private String token;
public String getToken() {
return token;
}
public void setToken( String token ) {
Prefs.token = token;
}
public String getXyEndpointUrl() {
return SERVER_BASE_URL + "/xy";
}
}
public class Program {
protected Prefs prefs;
protected Other prefsAware;
public Program() {
prefs = new Prefs();
prefsAware = new Other(prefs);
}
// or even (if you don't like constructor mediated passing of prefs)
public Prefs getPrefs() {
return prefs;
}
}
typically global configs are read from properties files at runtime. You can have varying config files for the development, QA, production environments.
The only thing you need to at least be aware of is that in a web application, if you set static variables from a web request, you could munge the data if you do not synchronize the setter. If you are only reading then you should be fine.
An alternative to what you are doing is why not inject the OAuth key into a service that handles the authentication concerns of your system? You could do what you need to do with a static resource, but in this case, but you should be aware that you don't really need a static variable to hold the key.