【问题标题】:onSharedPreferenceChanged not fired if change occurs in separate activity?如果在单独的活动中发生更改,则不会触发 onSharedPreferenceChanged?
【发布时间】:2011-04-17 11:25:02
【问题描述】:

我在我的主要活动中实现了onSharedPreferenceChanged

如果我在主要活动中更改首选项,我的事件就会触发。

如果我通过我的首选项屏幕 (PreferenceActivity) 更改首选项,我的事件不会在首选项更改时触发(因为它是一个单独的活动并且单独引用 sharedPreferences?)

有没有人建议我应该如何克服这种情况?

谢谢!

EDIT1:我尝试在我的偏好活动中添加事件处理程序,但它永远不会触发。在我的偏好活动的 onCreate 期间调用以下方法。当我更改值时,它从不打印消息(msg()Log.d 的包装器)。

private void registerChangeListener () {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);

    sp.registerOnSharedPreferenceChangeListener(new OnSharedPreferenceChangeListener () {
        public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
            msg (" ***** Shared Preference Update ***** ");
            Intent i = new Intent();
            i.putExtra("KEY", key);
            i.setAction("com.gtosoft.dash.settingschanged");

            sendBroadcast(i);

            // TODO: fire off the event
        }
    });
}

【问题讨论】:

标签: android sharedpreferences


【解决方案1】:

如果您使用匿名类,OnSharedPreferenceChangeListener 会在您的情况下被垃圾收集。

要解决该问题,请使用PreferenceActivity 中的以下代码注册和取消注册更改侦听器:

public class MyActivity extends PreferenceActivity implements
    OnSharedPreferenceChangeListener {

@Override
protected void onResume() {
    super.onResume();
    // Set up a listener whenever a key changes
    getPreferenceScreen().getSharedPreferences()
            .registerOnSharedPreferenceChangeListener(this);
}

@Override
protected void onPause() {
    super.onPause();
    // Unregister the listener whenever a key changes
    getPreferenceScreen().getSharedPreferences()
            .unregisterOnSharedPreferenceChangeListener(this);
}

public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,String key) 
{
  // do stuff
}

此外请注意,只有在实际值发生变化时才会调用侦听器。再次设置相同的值不会触发监听器。

另见SharedPreferences.onSharedPreferenceChangeListener not being called consistently

【讨论】:

  • 天哪,你摇滚!多么棘手!!!我的 onsharedpreferencelistener 正在被垃圾收集!因此,根据您的链接,我为它创建了一个长期引用(全局成员变量),瞧,效果很好!
  • @pivotnig 当我尝试你的代码时,我得到一个编译错误。它不会接受这个,这是一个 PreferenceActivity 子类。这是您的代码中的其他内容吗?
  • @dpk 没有告诉我错误很难说是什么问题。无论如何,你应该为你的问题提出一个新问题。
  • 兄弟,你真棒。感谢您的回答。 (y)
  • 很好的答案!立即提供帮助
【解决方案2】:

这是因为垃圾收集器。它只工作一次。然后将引用作为垃圾收集。所以为监听器创建实例字段。

private OnSharedPreferenceChangeListener listner;

listner = new SharedPreferences.OnSharedPreferenceChangeListener() {        
        @Override
        public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
            //implementation goes here
        }
    };
    prefs.registerOnSharedPreferenceChangeListener(listner);

【讨论】:

  • 我认为这个答案最接近问题。如果在调用站点中正确定义,则侦听器似乎是垃圾收集的。因此,应该在其他地方保留对侦听器的引用以使其正常工作。
  • @hderanga 谢谢,这对我有用,因为我的课程没有扩展 PreferenceActivity
  • 任何内存泄漏问题?
【解决方案3】:

我和其他许多人一样来到这里,因为当我将布尔值从 true 更改为 false 或反之亦然时,我的听众不会被解雇。

经过大量阅读,重构,切换contexts/innerclasses/privates/static/等,我意识到我的(愚蠢的)错误:

onSharedPreferenceChanged在发生变化时被调用。仅有的。永远。

在我的测试过程中,我一直在点击同一个按钮,因此一直为首选项分配相同的布尔值,所以它永远不会改变。

希望这对某人有帮助!

【讨论】:

    【解决方案4】:

    避免该问题的另一种方法是将您的活动设置为侦听器类。由于只有一个具有独特名称的覆盖方法,您可以这样做:

    public class MainActivity extends AppCompatActivity implements SharedPreferences.OnSharedPreferenceChangeListener
    {
        @Override
        protected void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            sharedPreferences.registerOnSharedPreferenceChangeListener(this);
            ...
        }
    
        @Override
        public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key)
        {
            ...
        }
    } 
    

    【讨论】:

    • 这是我正在做的,但没有奏效。除其他外,我发现使用 OnResume() 和 OnPause() 分别注册和注销侦听器会导致侦听器无效,因为用户在使用 PreferenceActivity 时会离开 MainActivity(这在您考虑时很有意义) )。
    【解决方案5】:

    请注意,最初的问题是关于 MainActivity 监听 PreferenceActivity 中的设置更改。然后提问者添加了一个“EDIT1”并将问题更改为在 PreferenceActivity 本身中聆听。这比前者更容易,而且似乎是所有答案所假设的。但是,如果您仍然想要前一种情况怎么办?

    好吧,它也可以,但是不要使用 OnResume() 和 OnPause() 来注册和注销监听器。这样做会导致监听器无效,因为用户在使用 PreferenceActivity 时会离开 MainActivity(这在您考虑时是有道理的)。所以它会起作用,但是即使用户不使用它,您的 MainActivity 仍将在后台监听。有点浪费资源不是吗?因此,还有另一种似乎可行的解决方案,只需向 OnResume() 添加一个方法即可重新读取所有首选项。这样,当用户在 PreferenceActivity 中完成编辑首选项时,MainActivity 会在用户返回时拾取它们,而您根本不需要监听器

    如果有人发现这种方法有问题,请告诉我。

    【讨论】:

      【解决方案6】:

      您为什么不在偏好可能会改变的其他活动中添加onSharedPreferenceChanged

      【讨论】:

      • 你的意思是像在我的设置活动中定义 onSharedPreferenceChanged 吗?我做到了,但它没有开火。
      • 作为记录,问题是由垃圾收集器回收我的事件处理程序引起的。我必须创建一个对事件处理程序的全局成员引用。
      【解决方案7】:

      垃圾收集器会清除...您应该考虑改用应用程序上下文...或者只是在应用启动时添加代码...然后添加具有应用程序上下文的侦听器...

      【讨论】:

        【解决方案8】:

        考虑将 PreferencesChangeListener 保留在 Android App 类实例中。虽然它不是一个在 App 内存储引用的干净解决方案,但应该阻止 GC 垃圾收集您的侦听器,并且您仍然应该能够接收数据库更改更新。请记住,偏好管理器存储对侦听器的强引用! (WeakHashMap)

        /**
         * Main application class
         */
        class MyApp : Application(), KoinComponent {
        
            var preferenceManager: SharedPreferences? = null
            var prefChangeListener: MySharedPrefChangeListener? = null
        
            override fun onCreate() {
                super.onCreate()
        
                preferenceManager = PreferenceManager.getDefaultSharedPreferences(this)
                prefChangeListener = MySharedPrefChangeListener()
                preferenceManager?.registerOnSharedPreferenceChangeListener(prefChangeListener)
            }
        }
        

        class MySharedPrefChangeListener : SharedPreferences.OnSharedPreferenceChangeListener {
        
            /**
             * Called when a shared preference is changed, added, or removed.
             */
            override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
                if (sharedPreferences == null)
                    return
        
                if (sharedPreferences.contains(key)) {
                    // action to perform
                }
            }
        }
        

        【讨论】:

          【解决方案9】:

          在阅读第一个应用共享的Word可读数据时,我们应该

          替换

          getSharedPreferences("PREF_NAME", Context.MODE_PRIVATE);
          

          getSharedPreferences("PREF_NAME", Context.MODE_MULTI_PROCESS);
          

          在第二个应用中获取第二个应用中的更新值。

          【讨论】:

          • Android 不支持从多个进程访问SharedPreferences。这样做会导致并发问题,从而导致所有首选项丢失。此外,不再支持MODE_MULTI_PROCESS
          猜你喜欢
          • 2022-12-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-29
          • 1970-01-01
          • 2011-10-15
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多