如果在单独的活动中发生更改,则不会触发 onSharedPreferenceChanged?

Posted

技术标签:

【中文标题】如果在单独的活动中发生更改,则不会触发 onSharedPreferenceChanged?【英文标题】:onSharedPreferenceChanged not fired if change occurs in separate activity? 【发布时间】: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
        
    );

【问题讨论】:

SharedPreferences.onSharedPreferenceChangeListener not being called consistently 的可能重复项 【参考方案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

以上是关于如果在单独的活动中发生更改,则不会触发 onSharedPreferenceChanged?的主要内容,如果未能解决你的问题,请参考以下文章

删除多个单元格时不会发生 Excel 工作表更改事件

约束布局更改后,不会触发UIButton操作

Mounted 中的类更改不会触发过渡 vuejs

发布值更改后不会触发 onReceive

按下后退时 EditText 不会触发更改

AnyLogic中的事件触发变量