OnSharedPreferenceChangeListener不被调用原理及解决方案

问题再现

OnSharedPreferenceChangeListener是Android中SharedPreference文件发生变化的监听器。通常我们想要进行监听,会实现如下的代码。

protected void onCreate(Bundle savedInstanceState) {
  PreferenceManager.getDefaultSharedPreferences(getApplicationContext())
      .registerOnSharedPreferenceChangeListener(new OnSharedPreferenceChangeListener() {
      @Override
      public void onSharedPreferenceChanged(
          SharedPreferences sharedPreferences, String key) {
          Log.i(LOGTAG, "testOnSharedPreferenceChangedWrong key =" + key);
      }
  });
}


这种写法看上去没有什么问题,而且很多时候开始几次onSharedPreferenceChanged方法也可以被调用。但是过一段时间(简单demo 不容易出现,但是使用DDMS中的gc会立刻导致接下来的问题),你会发现前面的方法突然不再被调用,进而影响到程序的处理。

原因剖析

简而言之,就是你注册的监听器被移除掉了。首先我们先了解一下registerOnSharedPreferenceChangeListener注册的实现。

private final WeakHashMap<OnSharedPreferenceChangeListener, Object> mListeners =
            new WeakHashMap<OnSharedPreferenceChangeListener, Object>();
//some code goes here
public void More ...registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
    synchronized(this) {
      mListeners.put(listener, mContent);
    }
}

从上面的代码可以得知,一个OnSharedPreferenceChangeListener对象实际上是放到了一个WeakHashMap的容 器中,执行完示例中的onCreate方法,这个监听器对象很快就会成为垃圾回收的目标,由于放在WeakHashMap中作为key不会阻止垃圾回收, 所以当监听器对象被回收之后,这个监听器也会从mListeners中移除。所以就造成了onSharedPreferenceChanged不会被调 用。

关于WeakHashMap相关,请阅读 译文:理解Java中的弱引用 进而更多了解。

如何解决

改为对象成员变量(推荐)

将监听器作为Activity的一个成员变量,在Activity的onResume进行注册,在onPause时进行注销。推荐在这两个 Activity生命周期中进行处理,尤其是当SharedPreference值发生变化后,对Activity展示的UI进行处理操作的情况。这种方 法是最推荐的解决方案。

private OnSharedPreferenceChangeListener mListener = new OnSharedPreferenceChangeListener() {

  @Override
  public void onSharedPreferenceChanged(
      SharedPreferences sharedPreferences, String key) {
      Log.i(LOGTAG, "instance variable key=" + key);
  }
};

@Override
protected void onResume() {
  PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).registerOnSharedPreferenceChangeListener(mListener);
  super.onResume();
}

@Override
protected void onPause() {
  PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).unregisterOnSharedPreferenceChangeListener(mListener);
  super.onPause();
}(后略)
时间: 2024-08-29 22:18:47

OnSharedPreferenceChangeListener不被调用原理及解决方案的相关文章

函数的调用原理——栈桢

函数调用过程------栈桢 例:剖析"比较两个数之间的大小关系,并把较大数返回"的调用原理: int Max(int x, int y) {                  int z = 0;                  if (x > y)                                 z = x;                  else                                 z = y;               

Delphi按名字调用方法高级解决方案

Delphi按名字调用方法高级解决方案 博客分类: 编程基础 DelphiJ#ASPDOS数据结构 按名字调用方法似乎一直以来都是大家比较关注的技术,在论坛上有一个经典的答复: type    TProcedure = procedure(Test: string) of object; procedure ExecuteRoutine(Obj: TObject; Name, Param: string);  var    PMethod: TMethod;    AProcedure: TPr

ajaxFileUpload异步上传资源,onchange多次调用问题的解决方案

一.上传文件的做法 1 前端代码 <input id="myfiles" name="myfiles" type="file" onchange="upload();"/> <input type="button" name="btn_abc" onclick="document.getElementById('myfiles').click();"

Spring异步调用原理及SpringAop拦截器链原理

一.Spring异步调用底层原理 开启异步调用只需一个注解@EnableAsync @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AsyncConfigurationSelector.class) public @interface EnableAsync { /** * Indicate the 'async' annotation type to be detected at

JAVA的List接口的remove重载方法调用原理

前言 说真的,平常看源码都是自己看完自己懂,很少有写出来的冲动. 但是在写算法的时候,经常用到java中各种集合,其中也比较常用到remove方法. remove有重载函数,分别传入参数是索引index或者数据Object(指定泛型后自动转换),如果指定泛型是其他数据类型还好,但是指定的是Integer或者是int的话,或者就有点懵了. 这曾经也困惑过我,所以我就唯有用实践解惑了. 测试类设计 测试类一 public class Text { public void remove(int ind

前端筑基篇(一)-&gt;ajax跨域原理以及解决方案

说明 跨域主要是由于浏览器的“同源策略”引起,分为多种类型,本文主要探讨Ajax请求跨域问题 前言 参考来源 什么是跨域 ajax跨域的表现 跨域的原理 如何解决跨域问题 JSONP方式解决跨域问题 CROS解决跨域问题 CROS请求原理 PHP后台配置 JAVA后台配置 .NET后台配置 FAQ multi value '*,*' 的问题 前言 强烈推荐阅读参考来源中的文章,能够快速帮助了解跨域的原理 参考来源 本文参考了以下来源 浏览器同源政策及其规避方法(阮一峰) 跨域资源共享 CORS

在设置了android:parentActivityName后,点击子Activity返回键,父Activity总会调用OnDestroy()的解决方案

最近查了很久这个事情,分享给大家, 原理很简单,一个Activity在manifet里声明了android:parentActivityName:这时候通过Activity左上角的返回按钮点击返回, 启动声明的父Activity,总会先调用父Activity的OnDestroy方法,具体如下面所说: <activity android:name="com.example.helloworld.DisplayMessageActivity" android:label="

ajax跨域原理以及解决方案

说明 跨域主要是由于浏览器的“同源策略”引起,分为多种类型,本文主要探讨Ajax请求跨域问题 前言 强烈推荐阅读参考来源中的文章,能够快速帮助了解跨域的原理 参考来源 本文参考了以下来源 浏览器同源政策及其规避方法(阮一峰) 跨域资源共享 CORS 详解(阮一峰) 什么是跨域 为了更了解跨域的原理,可以阅读参考来源中的文章,里面对跨域的原理讲解很详细到位 ajax跨域的表现 ajax请求时,如果存在跨域现象,并且没有进行解决,会有如下表现 第一种现象:No 'Access-Control-All

详细解释如何通过Android自带的方式来实现图片的裁剪——原理分析+解决方案

我们很多时候需要进行图片的裁剪,其实这个功能在android系统中已经有一套解决方案了,虽然界面和效果并不是很优秀但功能毫无疑问是完美实现了.至于,不用自带的方案怎么做自定义,这个就是后话了.本篇主要讲解的是裁剪的原理和流程,外带分析了大图裁剪和小图裁剪的不同之处,同时给出具体的实现方案. 一.原理+流程 andorid提供了一个action,com.android.camera.action.CROP, 是Intent intent = new Intent("com.android.came