Reputation: 965
When using a thread/task within an android service that implements the OnSharedPreferenceChangeListener interface, the changes made in the preference screen aren't reflected back to the thread/task object within the android service.
I want to accomplish two things:
SharedPreference data should be loaded when MyTask is constructed and initialized.
When preference change occurs, MyTask object must be updated with the new preference values set in the preference screen.
The problem is: preference initialization and preference changes are not reflected to the MyTask object.
This is my setup (only essential parts are mentioned):
MyService.class:
public class MyService extends Sevice {
private MyTask myTask;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (!serviceStarted) {
serviceStarted = true;
myTask = new MyTask(this);
Thread t = new Thread(myTask);
t.start();
}
return Service.START_STICKY;
}
@Override
public void onDestroy() {
myTask.cancel();
super.onDestroy();
}
}
MyTask.class:
public MyTask implements Runnable, OnSharedPreferenceChangeListener {
private Context mContext;
private boolean mCancelled;
public MyTask(Context context) {
mContext = context;
}
@Override
public void run() {
while(!mCancelled) {
// do something
}
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key) {
// FIXME: DOESN'T GET CALLED after change in preference!!!!
Log.d(TAG, "Key= " + key);
}
public void cancel() {
mCancelled = true;
}
}
preference_devices.xml:
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<PreferenceCategory
android:key="pref_category_devices"
android:title="@string/pref_category_devices_title" >
<CheckBoxPreference
android:defaultValue="true"
android:key="pref_devices_server"
android:title="@string/pref_devices_server_title" />
</PreferenceCategory>
</PreferenceScreen>
I have tried coding a SharedPreferences listener object as a member field of the MyTask class and register/unregister the listener from the provided context, but that didn't work either. These changes also didn't work:
MyTask.class (using SharedPreference listener as field member of class):
public MyTask implements Runnable {
private Context mContext;
private boolean mCancelled;
private boolean mServerEnabled;
private SharedPreferences mPrefs;
private SharedPreferences.OnSharedPreferenceChangeListener
mPreferenceListener;
public MyTask(Context context) {
mContext = context;
mPrefs = mContext.getSharedPreferences("pref_category_devices",
Context.MODE_PRIVATE);
mPreferenceListener = new OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(
SharedPreferences sharedPreferences, String key) {
// FIXME: DOESN'T GET CALLED after change in preference!!!!
Log.d(TAG, "Key= " + key);
}
};
mPrefs.registerOnSharedPreferenceChangeListener(mPreferenceListener);
// set the initial value of the preference setting
mServerEnabled = mPrefs.getBoolean("pref_devices_server", false);
}
@Override
public void run() {
while(!mCancelled) {
// do something
}
}
public void cancel() {
mCancelled = true;
}
}
I have now reached the point of throwing my computer out of the window :(
Any help in the right direction is highly appreciated :)
EDIT: In the code
mPrefs = mContext.getSharedPreferences("pref_category_devices", Context.MODE_PRIVATE);
I assumed that the first argument should be the preference category name of the preference file, like: "pref_category_devices". THIS IS INCORRECT! The first argument must be a shared preference file name. That didn't solve the problem, but at least now you know to not fall for this pitfall.
=== SOLUTION: === See answer of Mr_and_Mrs_D + code below this line:
Change in MyTask:
mPrefs = mContext.getSharedPreferences("pref_category_devices",
Context.MODE_PRIVATE);
into:
mPrefs = PreferenceManager.getDefaultSharedPreferences(mContext);
mPreferenceListener = new OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals("preference_name_here")) {
mPrefValue = sharedPreferences.getBoolean(key, false);
// do something with boolean pref value
}
}
};
mPrefs.registerOnSharedPreferenceChangeListener(myPreferenceListener);
Where mPrefValue is a field member of type boolean in MyTask that needs to be set when the "preference_name_here" preference changes.
Upvotes: 0
Views: 2425
Reputation: 34036
Change :
private volatile boolean mCancelled; //otherwise the myTask thread may never stop
For your problem :
if (!serviceStarted) {
serviceStarted = true;
myTask = new MyTask(this);
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
sp.registerOnSharedPreferenceChangeListener(myTask); //err, you must register
Thread t = new Thread(myTask); t.start();
}
Docs :
These preferences will automatically save to SharedPreferences as the user interacts with them. To retrieve an instance of SharedPreferences that the preference hierarchy in this activity will use, call getDefaultSharedPreferences(android.content.Context) with a context in the same package as this activity.
[emphasis mine]
Edit : your second snippet probably fails cause you get the wrong shared prefs - you must get the default ones - I thought it was failing because of :
SharedPreferences.onSharedPreferenceChangeListener not being called consistently
Upvotes: 1