I am building an AAR Library. Since there is a complex setting in my library. I hope to help users that using this library can easily set/remember their setting. Thus, I try to make my AAR Library to manage sharedPreference for users.
For example, here is the structure of my project
AppAAA (dependent on LibBBB)
| - MainActivity
LibBBB
| - LibSetting
| - LibSettingFragment
| - LibSettingActivity
| - ... - preferences.xml
I try to implement something like:
public class LibSetting() {
static final String KEY_SERVER_PORT = "PREF_KEY_SERVER_PORT";
SharedPreferences pref;
Context context;
public LibSetting(Context context) {
this.context = context;
this.pref = ((Activity)context).getPreferences(Context.MODE_PRIVATE);
}
public int getServerPort() {
return pref.getInt("PREF_KEY_SERVER_PORT", 50005);
}
public void showSettingActivity() {
Intent i = new Intent(context.getApplicationContext(), LibSettingActivity.class);
context.startActivity(i);
}
}
The implementation of LibSettingActivity and LibSettingFragment are trivial. I basically just call "addPreferencesFromResource(R.xml.preferences);" to load my preference to set (NOTE: I use the same key, PREF_KEY_SERVER_PORT, in the xml).
However, it is not working. The preference set by the LibSettingFragment is totally not propagated to the preference loaded by pref.getXXX();
My guess is because the LibSettingFragment has no way to know that I want to set the preference from the "application's context" rather than the lib's context. How can I do it?
The implementation of LibSettingActivity and LibSettingFragment are trivial. I basically just call "addPreferencesFromResource(R.xml.preferences);" to load my preference to set (NOTE: I use the same key, PREF_KEY_SERVER_PORT, in the xml).
I do not recommend this, if you plan on distributing this library. Your library is now polluting the app's main SharedPreferences.
The preference set by the LibSettingFragment is totally not propagated to the preference loaded by pref.getXXX();
That is because you are using the wrong SharedPreferences. A PreferenceFragment uses the app's main SharedPreferences, retrieved via PreferenceManager.getDefaultSharedPreferences().
Using your own "private" SharedPreferences in a library is OK, though it does pose some problems for some apps (e.g., those that need to control all data storage, for encryption).
CommonsWare gives the right answer. But for people still wanna use app's sharedPreference (rather than the DefaultSharedPreference), following hack will help to do so:
#Override
public void onResume() {
super.onResume();
// Set up a listener whenever a key changes
getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
}
#Override
public void onPause() {
super.onPause();
// Set up a listener whenever a key changes
getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
}
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
// passed as an input argument from the app
appPref.edit().putInt(key, 99999).commit();
}
Related
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
I need to create a session and change it at times. In a specific activity should recover it and compare it to a different variable and modify the value of this session. I tried to create a class for this, but the change of activity, the value back to null. I need it to remain until the application is closed.
below:
import android.app.Application;
public class Util extends Application {
private static String idCorrente;
public void onCreate() {
super.onCreate();
idCorrente="0";
}
public static String getIdCorrente() {
return idCorrente;
}
public static void setIdCorrente(String id) {
Util.idCorrente = id;
}
}
I do not know exactly the right way to do it.
You need to store the data on the device somehow. I would recommend reading the Storage Options page of the Android Developers Guide.
Specifically, I think you will find SharedPreferences well-suited for your application.
I have many shared preference for my app (mostly relating to color customization) and I'm unsure what the best method is to store/use them at runtime.
Currently I am doing something like this (with more or less preferences depending on the view) in every activity/fragment:
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getActivity());
int buttonbg = settings.getInt("buttonmenu_bg", 0);
int buttontxt = settings.getInt("buttonmenu_txt", 0);
int headerclr = settings.getInt("header", 0);
And then using those to set the various colors in the display. This seems like a lot of overhead to have to call the PreferenceManager each time and go through all that.
So I started looking at creating an application class, reading the preferences in once and using static variables from the application class in the activities/fragment to set the display.
My question is, are there any drawbacks or gotchas to doing this that I should consider before I venture further down the Application class path?
If you are not using so many static variables so this may not affect your application.But the problem with static variable may arise when your app goes to background and the app running on front requires memory so it may clear your static data,so when you will go to your app you may find nothing (null) in place of static data.
The purpose of the Application class is to store global application state or data (in memory of course), so your approach is correct. I've used it multiple times and it works like a charm.
What I usually do is to create a Map member variable and provide methods for getting and putting values into it, looks like this:
package com.test;
...
...
public class MyApp extends Application{
private Map<String, Object> mData;
#Override
public void onCreate() {
super.onCreate();
mData = new HashMap<String, Object>();
}
public Object get(String key){
return mData.get(key);
}
public void put(String key,Object value){
mData.put(key, value);
}
}
Then from my activities, I just do ((MyApp) getApplication()).get("key") or ((MyApp) getApplication()).put("key",object). Also, don't forget to set the android:name attribute in your manifest file, under the application tag:
<application
...
...
android:name="com.test.MyApp">
</application>
Is there any particular reason why you are not setting the display colors in res/values/styles.xml?
So my problem is as follows: I have 2 services running in different processes and would like to keep it this way. One is busing data from databases to bound applications and the second is polling for incoming data through sockets. I feel keeping these in independent process would be better. The problem is that I would like to have a shared preferences between the two services and would like to implement OnSharedPreferenceChangeListener to update setting needed for polling and busing data. I can't implement OnSharedPreferenceChangeListener in the services since they run on different processes. I could implement this on the PreferenceActivity but how do I communicate to the services for immediate update? I do not want to use AIDL and worry about binding. There is the possibility of creating broadcast receivers and sending out intents but these seems like a big work around if the settings menu grows. Any other great ideas out there?
all right here is your answer...
for the preference of this example lets take 3 classes - 2 services service A and B (href A,B) and Settings(preferenceActivity)
initialize the two services as
public class ServiceA/B extends serice implements OnSharedPreferenceChangeListener{
#Overside
public void onCreate(....){
Settings.getPrefs(this).registerOnSharedPreferenceChangeListener(this);
}
#Override
protected void onResume() {
super.onResume();
Settings.getPrefs(this).registerOnSharedPreferenceChangeListener(this);
}
#Override
protected void onPause() {
super.onPause();
// Unregister the listener whenever a key changes
Settings.getPrefs(this)
.unregisterOnSharedPreferenceChangeListener(this);
}
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key) {
System.out.println("++"+key+"++");
if(key == "KEYA"||key == "KEYC")
Do_what_ever_you_want();
if (key == "KEYB")
do_anything();
}
do_anything(){}
Do_what_ever_you_want();
}
Shared preference Part.
public class Settings extends PreferenceActivity implements
OnSharedPreferenceChangeListener{
public static final String PREFS_PRIVATE = "PREFS_PRIVATE";
public static final String MASTERKEY = "!##$%^&*";
public static final String KEYA = "KEYA";
public static final String KEYB = "KEYB";
public static final String KEYC = "KEYC";
--- the create and get methods for getting and sharing data in the prefs... .....
// get them from just a google search.
}
I have this system implemented in one of my applicaiton... and deployed... so fiddle around these basics and let me know how it is goes...
Rajesh...
I've created a simple SharedPreferences based on ContentProvider, which can be used accross processes, you can find them from my bitbucket https://bitbucket.org/ABitNo/zi/src/783b6b451ba1/src/me/abitno/zi/provider/preference
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.