OnSharedPreferenceChangeListener不在另一个线程任务中工作

时间:2013-12-12 21:31:20

标签: android multithreading sharedpreferences android-service android-preferences

在实现OnSharedPreferenceChangeListener接口的android服务中使用线程/任务时,首选项屏幕中所做的更改不会反映回android服务中的线程/任务对象。

我想完成两件事:

  • 构建和初始化MyTask时,应加载SharedPreference数据。

  • 发生首选项更改时,必须使用首选项屏幕中设置的新首选项值更新MyTask对象。

问题是:首选项初始化和首选项更改不会反映到MyTask对象。

这是我的设置(仅提到必要部分):

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>

我尝试将SharedPreferences侦听器对象编码为MyTask类的成员字段,并从提供的上下文中注册/取消注册侦听器,但这也不起作用。这些变化也不起作用:

MyTask.class(使用SharedPreference侦听器作为类的字段成员):

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

我现在已经到了将计算机扔出窗外的地步:(

非常感谢任何正确方向的帮助:)

编辑:在代码中

mPrefs = mContext.getSharedPreferences("pref_category_devices", Context.MODE_PRIVATE);

我假设第一个参数应该是首选项文件的首选项类别名称,例如:“pref_category_devices”。这是不正确的!第一个参数必须是共享首选项文件名。这并没有解决问题,但至少现在你知道不要陷入这个陷阱。

===解决方案:=== 在此行下方查看Mr_and_Mrs_D +代码的答案:

MyTask中的更改:

mPrefs = mContext.getSharedPreferences("pref_category_devices",
            Context.MODE_PRIVATE);

成:

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

其中mPrefValue是MyTask中boolean类型的字段成员,需要在“preference_name_here”首选项更改时设置。

1 个答案:

答案 0 :(得分:1)

更改:

private volatile boolean mCancelled; //otherwise the myTask thread may never stop

对于你的问题:

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

  

当用户与其进行交互时,这些首选项将自动保存到SharedPreferences。要检索此活动中的首选项层次结构将使用的SharedPreferences实例,使用与此活动相同的包中的上下文调用getDefaultSharedPreferences(android.content.Context)

[强调我的]

编辑:你的第二个片段可能会失败,因为你得到了错误的共享首选项 - 你必须得到默认的 - 我认为它失败了因为:

SharedPreferences.onSharedPreferenceChangeListener not being called consistently