Android : SharedPreferences and MVC pattern - android

I'm developping my app using the MVC pattern. To store/access data, my controllers use a class named "DataStorage", and for now this class allows to store/access simple parameters about my app (username, data storage path, ...). In other words, I want to call a few methods like "getParameter(String key)" or "setParameter(String key, String value)".
I think SharedPreferences would be the most convenient way to store these parameters, so my get/setParameters use this class.
In all the examples I have seen, SharedPreferences is called from an Activity and there is no problem to call methods such as "getSharedPreferences" or "getApplicationContext"/"getContext". Because my DataStorage class is not an activity, for now I ask my first activity to give its context when creating a new DataStorage instance, and it works well to store my parameters. My problem : I want to be able to remove parameters from another activity using clear + commit methods. But it doesn't work (parameters are still there), and I think the reason is I give the 2nd activity context when creating another instance of DataStorage. The problem might be something else though, I've been practicing Android for only 2 days now...
To summarize how my app works :
Activity 1 creates a DataStorage class and provides its context to the DataStorage constructor. The DataStorage might store a parameter into a SharedPreferences file (or not...)
When I run my app again, if a particular parameter is set in the SharedPreferences file, then I start Activity 2 instead of Activity 1. Using the menu on Activity 2, I want to be able to clear the SharedPreferences file (in order to get Activity 1 again when I restart the app), so I create another DataStorage instance (and I provide Activity 2 context) and I call the method to clear all parameters.
As I said, first part works well (I can store parameters), but clear & commit do nothing to my SharedPreferences file.
I don't want to put a piece of code for this directly in my activities.
Can you help me with this ? What am I doing wrong in the way I use SharedPreferences ?
Thank you for your help !
Edit :
public class DataStorage {
private Context context;
private String settingsFilename;
private SharedPreferences settings;
public DataStorage(Context activityContext, String filename) {
context = activityContext;
settingsFilename = filename;
settings = context.getSharedPreferences(settingsFilename, Context.MODE_PRIVATE);
}
public void newSharedPreference(String key, String value) {
settings.edit().putString(key, value).apply();
settings.edit().commit();
}
public String getSharedPreference(String key) {
return settings.getString(key, null);
}
public void clearPreferences() {
settings.edit().clear();
settings.edit().commit();
Toast.makeText(context,settings.toString(), Toast.LENGTH_LONG).show();
}
}
In my first activity (the code is part of onCreate method) :
DataStorage storage = new DataStorage(this, getResources().getString(R.string.sharedPreferencesFile));
username = storage.getSharedPreference("username");
Toast.makeText(this, username, Toast.LENGTH_LONG).show();
if (username != null) {
Intent nextActivity = new Intent(this, ActivityMainMenu.class);
startActivity(nextActivity);
} else {
setContentView(R.layout.activity_name);
}
In my 2nd activity :
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
switch(id) {
case R.id.action_clearSharedPref :
storage.clearPreferences();
break;
case R.id.action_leave :
System.exit(RESULT_OK);
}
return super.onOptionsItemSelected(item);
}
(Storage is constructed exactly as I did in my first Activity)
I tried to replace "this" by getApplicationContext() in DataStorage constructor, but it didn't work.

From Editor Class Overview
Interface used for modifying values in a SharedPreferences object. All changes you make in an editor are batched, and not copied back to the original SharedPreferences until you call commit() or apply().
You need to update to change methods at your DataStorage
public void newSharedPreference(String key, String value) {
settings.edit().putString(key, value).apply();
}
and
public void clearPreferences() {
settings.edit().clear().apply();
Toast.makeText(context,settings.toString(), Toast.LENGTH_LONG).show();
}
Reason of issue is next
settings.edit().clear(); // clear is ok, but it won't be saved because
settings.edit().commit(); // create new editor and commit nothing

Related

Shared Preferences reset data when app crashed. Please guide

My app got crashed and all the data in shared preference got cleared.
I am saving some flags and maintaining user session in shared preference.
One of the flag is IsFirstLaunch, which tells me whether app is launching for first time or not, if returns true then I am downloading some data from server and storing in SQLite database.
Please guide, thanks in advance.
So after the crash when it went to load the Preferences there was a blank in the preferences xml file which caused the preferences to reset.
To avoid this you could put all preference modifications in synchronized blocks or even use one synchronized static method for all preference writing.
I think - you need a better way of managing and storing the data you're saving.
The next time the shared preferences were accessed however, the xml file was cleared and started new.
for example :
private static final class SharedPreferencesImpl implements SharedPreferences {
...
public String getString(String key, String defValue) {
synchronized (this) {
String v = (String)mMap.get(key);
return v != null ? v : defValue;
}
}
...
public final class EditorImpl implements Editor {
public Editor putString(String key, String value) {
synchronized (this) {
mModified.put(key, value);
return this;
}
}
...
}
}

How to pass a value to an activity from another WITHOUT starting it?

I want to pass a value from activity A to activity B without actually starting the activity B (therefore this rules out the use of Intents and putExtra). The activity B may or may not be started but when it does it needs to display the value passed to it by activity A.
I searched high and low but couldn't find any relevant solution to this seemingly simple question. Any help will be appreciated!
You can't find a solution, because it's something that goes against any logic.
Activity B can't do anything if not started and visible. Activity B might even already be destroyed by the system without you knowing.
You can use a lot of things to set some variables, which your Activity B can read such as in your Application, somewhere in your XML or simply any random class.
Still, you can not actually DO anything with any of these options, until you start Activity B and when you can, it will just effectively be the same as giving the extra data in the Intent.
Use global class like :
public class AppConfig {
Context context;
public AppConfig(Context mxt) {
context = mxt;
}
public static String filepath = null;
Then, in activity A before onCreate() :
AppConfig config;
Context context;
and in onCreate() method put this :
context = getApplicationContext();
config = new AppConfig(context);
And, in second Activity B before onCreate() :
AppConfig config;
Context context;
And, in onCreate() method put this :
context = getApplicationContext();
config = new AppConfig(context);
And set the value where you want. Hope this will help you.
You can use shared Preferences. Using this one Activity can set Value into it, and other activity can read from it.
So you need to keep a value for an activity . If it starts it means you have to use those values, otherwise you will discard those values. For this you can use a separate class with a variable of datatype that you want and you can create getter setter for that and you can use it.
Make use of the classes
public class GetSet {
public GetSet() {
super();
}
private static boolean passwordSet = false;
public static boolean isPasswordSet() {
return passwordSet;
}
public static void setPasswordSet(boolean passwordSet) {
GetSet.passwordSet = passwordSet;
}
}
You can access ths using GetSet.setPasswordSet(false);
and Boolean flag = GetSet.isPasswordSet();
Set the value as Global:
public class Global {
public static int mValue;
public static int getValue() {
return mValue;
}
public static void setValue(int mValue) {
Global.mValue = mValue;
}
}
I was looking for answers to that but I couldn't find it. So I found a way to do that.
First Activity
Intent intent = new Intent(this, SecondActivity.class);
intent.putExtra("Flag" , true);
startActivity(intent);
Second Activity
boolean flag;
flag = getIntent().getBooleanExtra("Flag" ,false);
if(flag == true)
{
this.finish();
}
So now you may send any data you want it will open the Second Activity and then immediately close it after you wouldn't even realize it. You may use Shared prefences to save your data for after usage.

Cannot save Shared preferences, it crashes because the context is null, fragment already replaced

The app was first made with activities, but because of an issue, I have to use 2 fragments with an FragmentManager instead. Now, because of this I had to refactor a lot of code.
So my 2 fragments are an videoplayerfragment and videoRecorderFragment.
Sometimes I send an request to get via a JSON, the list of videos in the videoPlayer, and if I want to make a video, I change the fragment. Now if the response is late, I will get the OnSuccess of the request, trying to save the credentials, but the context of the videoplayer is null, because I have replaced the fragment.
This is part of the UserCredentialsPersistence class:
private static SharedPreferences obtainSharedPreferences(Context context) {
return context.getSharedPreferences(USER_CREDENTIALS_KEY,
Context.MODE_PRIVATE);
}
public static boolean saveToDownloadCount(Context context,
int download_count) {
Editor e = context.getSharedPreferences(USER_CREDENTIALS_KEY,
Context.MODE_PRIVATE).edit();
e.putInt(USER_TO_DL_COUNT_KEY, download_count);
return e.commit();
}
This is the important part of the VideoPlayerFragment:
videoRequest.requestNotification = new RequestNotification() {
#Override
public void onSuccess(Object sender) {
Log.d("#VideoPlayerActivity", "success");
playHideUpAnimation();
new_videos = new ArrayList<VideoData>(VideoDataManager.getInstance().getNewVideos());
UserCredentialsPersistence.saveToDownloadCount(getActivity(), UserCredentialsPersistence.restoreToDownloadCount(getActivity()) + new_videos.size());
all_videos = videodb.getTotalVideoListFromDB();
Here it crashes because the getActivity is null when calling any method from UserCredentialsPersistence.
I have also tried to call it from the FragmentManager:
OnSuccess at the requestNotification calling:
((VideoHolderActivity) getActivity()).saveToDLCount(new_videos.size());
And this being:
public void saveToDLCount(Integer size){
UserCredentialsPersistence.saveToDownloadCount(VideoHolderActivity.this, UserCredentialsPersistence.restoreToDownloadCount(VideoHolderActivity.this) + size);
}
But still, the context is null. Any idea how I can get past this?
I created a BaseFragment class. All my Fragments extend this class. And in this class I have a
interface, which is implemented by my FragmentNavigator.
Then i simply call from the fragments the functions that i need from the FragmentManager. Here i have put all the functions that need context. And after getting the response from them, i send it back into the fragments, by doing a cast to the current fragment i use.

Accessing SharedPreferences through static methods

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);
}

Passing data through intents instead of constructors

Having developed many desktop GUI apps (from Xt to Qt, Java Awt/Swt/Swing, etc) I really find it difficult to get used to Android.
Suppose I have the MainView Activity class which explicitly calls DetailedView via intent mechanism as shown next:
Since an Activity class is instantiated via onCreate() how do I
customize it? (No constructor, only
pass data through intent!)
Is there a way to get a reference for the DetailedView instance in
MainActivity?
Is there a way to get a reference for the MainActivity instance in
DetailedView?
How can I pass the reference to FrontEnd to the DetailedView class?
Intent.putExtras() allows only for
certain data types to pass to the
intent receiver class.
MainActivity {
...
FrontEnd fe;
...
public void onCreate(Bundle savedInstanceState) {
...
Intent myIntent = new Intent(this, DetailedView.class);
...
}
protected void onListItemClick(ListView l, View v, int position, long id) {
...
startActivityForResult(myIntent,..);
...
}
}
One way of passing simple data between activities/services of a specific app is to use the SharedPreferences functionality of android.
This may not be the most elegant code to get the job done, but I routinely create a static "utility" class in my Android projects to allow for 1 line get and set of simple data types via shared preferences
private static final String PREFERENCE_FILE_NAME = "com.snctln.util.test.SharedPreferencesFile";
private static final String BOOL_VALUE_ONE = "bValueOne";
public static boolean getBooleanValue1(Context context)
{
SharedPreferences prefs = context.getSharedPreferences(PREFERENCE_FILE_NAME, Context.MODE_PRIVATE);
return prefs.getBoolean(BOOL_VALUE_ONE, true); // return true if the value does not exist
}
public static void setBooleanValue1(Context context, int appWidgetId, boolean actualvalue)
{
SharedPreferences.Editor prefs = context.getSharedPreferences(PREFERENCE_FILE_NAME, Context.MODE_PRIVATE).edit();
prefs.putBoolean(BOOL_VALUE_ONE, actualvalue);
prefs.commit();
}
I frequently cheat and use static 'getInstance' calls to communicate between Activities and views. This works as long as they're both in the same proc, and I've yet to have a data access failure...but I'm sure it's only a matter of time...IF you're looking for a hacky quick fix this could be it, otherwise you have to pass data through intents.

Categories

Resources