OnSharedPreferenceChangeListener详解及出现不触发解决办法

之前使用OnSharedPreferenceChangeListener,遇到了点小问题,就是有些时候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(); }

改为静态变量(不推荐)

如下,将一个指向匿名的内部类对象的变量sListener使用static修饰,这个内部类对象则不会持有外部类的引用。
但是这种做法并不推荐,因为一个静态变量和与外部实例不相关,我们很难和外部实例进行一些操作。

private static OnSharedPreferenceChangeListener sListener = new OnSharedPreferenceChangeListener() { @Override public void onSharedPreferenceChanged( SharedPreferences sharedPreferences, String key) { Log.i(LOGTAG, "static variable key=" + key); } };

为什么这样设计

可能会有人认为这是系统设计的猫腻或者bug,其实不然,这正是Android设计人员的高明之处。

正如我们示例的代码一样,将一个(隐式的)局部变量添加到监听器容器中,如果该容器只是一个普通的HashMap,这样会导致内存泄露,因为该容器还有局部变量指向的对象,该对象又隐式持有外部Activity的对象,导致Activity无法被销毁。关于非静态内部类持有隐式持有外部类引用,请参考细话Java:”失效”的private修饰符

除此之外,因为局部变量无法在其所在方法外部访问,这样就导致了我们只可以使用方法中使用局部变量就行注册,在合适的时机却无法使用局部变量进行注销。

以上就是对 Android OnSharedPreferenceChangeListener的介绍,以及出现问题解决办法,谢谢大家对本站的支持!

时间: 2024-09-28 23:24:03

OnSharedPreferenceChangeListener详解及出现不触发解决办法的相关文章

OnSharedPreferenceChangeListener详解及出现不触发解决办法_Android

之前使用OnSharedPreferenceChangeListener,遇到了点小问题,就是有些时候OnSharedPreferenceChangeListener没有被触发.最近花了点时间研究了一下,小做整理.本文将会介绍监听器不被触发的原因,解决方法,以及其中隐含的一些技术细节. 问题再现 OnSharedPreferenceChangeListener是Android中SharedPreference文件发生变化的监听器.通常我们想要进行监听,会实现如下的代码. protected vo

CSS透明属性详解及背景透明继承解决办法hack

透明往往能产生不错的网页视觉效果,先奉上兼容主流浏览器的CSS透明代码:  代码如下 复制代码 .transparent_class { filter:alpha(opacity=50); -moz-opacity:0.5; -khtml-opacity: 0.5; opacity: 0.5; } 上面的几个属性分别是: opacity: 0.5; 这是最重要的,因为它是CSS标准.该属性支持Firefox, Safari和 Opera. filter:alpha(opacity=50); 这个

详解mysql建立索引的使用办法及优缺点分析_Mysql

前言 索引(index)是帮助MySQL高效获取数据的数据结构. 它对于高性能非常关键,但人们通常会忘记或误解它. 索引在数据越大的时候越重要.规模小.负载轻的数据库即使没有索引,也能有好的性能, 但是当数据增加的时候,性能就会下降很快. 为什么要创建索引呢? 这是因为,创建索引可以大大提高系统的性能. 第一.通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性. 第二.可以大大加快数据的检索速度,这也是创建索引的最主要的原因. 第三.可以加速表和表之间的连接,特别是在实现数据的参考完整性方

为什么ResNet和DenseNet可以这么深?一文详解残差块为何有助于解决梯度弥散问题。

传统的"提拉米苏"式卷积神经网络模型,都以层叠卷积层的方式提高网络深度,从而提高识别精度.但层叠过多的卷积层会出现一个问题,就是梯度弥散(Vanishing),backprop无法有效地把梯度更新到前面的网络层,导致前面的层参数无法更新. 而BatchNormalization(BN).ResNet的skip connection就是为了解决这个问题,BN通过规范化输入数据改变数据分布,在前传过程中消除梯度弥散.而skip connection则能在后传过程中更好地把梯度传到更浅的层次

TCP连接状态详解及TIME_WAIT过多的解决方法

  TIME_WAIT状态原理 ---------------------------- 通信双方建立TCP连接后,主动关闭连接的一方就会进入TIME_WAIT状态. 客户端主动关闭连接时,会发送最后一个ack后,然后会进入TIME_WAIT状态,再停留2个MSL时间(后有MSL的解释),进入CLOSED状态. 下图是以客户端主动关闭连接为例,说明这一过程的.   TIME_WAIT状态存在的理由 ---------------------------- TCP/IP协议就是这样设计的,是不可避

发改委详解4万亿走向解决1.17亿农民饮水问题

本报讯 (记者 周宇) 昨天的报告中,张平对中央政府4万亿投资成效进行了解读.截至11月,运用4万亿新增投资先后解决了1.17亿农村人口饮水安全问题,新增农村电网线路115万公里,新建改建农村公路70万公里:京沪.哈大.石武.兰新等一批重大铁路项目进展顺利,建成高速公路1.27万公里,新建.改扩建28个中西部支线机场和西部干线机场:新增污水日处理能力约3200万吨:建成一大批基层医疗卫生教育文化服务设施.

JavaScript触发onScroll事件的函数节流详解_javascript技巧

问题描述 常见的网站布局,顶部一个导航栏,我们假设本页面共有四个栏目:分别为A.B.C.D,我们点击A,锚点跳转至A栏目,同时顶部的A按钮高亮:点击B,锚点跳转至B栏目,同时顶部的B按钮高亮:我们在Main组件里面滚动,滚动到B模块时,B按钮高亮.以上是我们经常会在开发中遇到的一个模型.如果是在以前,用jQuery作前端开发的话,实在是太熟悉不过了. 解决方案 主要想谈谈在React组件化开发中的性能优化方法. 我们的页面结构是这样的 <div className={style.main} id

百度地图API详解之事件机制,function“闭包”解决for循环和监听器冲突的问题:

原文:百度地图API详解之事件机制,function"闭包"解决for循环和监听器冲突的问题:  百度地图API详解之事件机制 2011年07月26日 星期二 下午 04:06 和DOM编程里的事件模型一样,百度地图API也提供了类似的事件机制.本文介绍了事件监听的添加和移除方法,this指针和事件参数的使用以及绑定事件监听函数中涉及的闭包问题,最后分享了一个用来增强地图API事件机制的开源项目. 事件添加和移除 我们最简单的事件开始,下面的代码示例给map对象添加了click事件的监

jQuery 跨域访问解决原理案例详解_jquery

浏览器端跨域访问一直是个问题,多数研发人员对待js的态度都是好了伤疤忘了疼,所以病发的时候,时不时地都要疼上一疼.记得很久以前使用iframe 加script domain 声明.yahoo js util 的方式解决二级域名跨域访问的问题. 时间过得好快,又被拉回js战场时, 跨域问题这个伤疤又开疼了.好在,有jQuery帮忙,跨域问题似乎没那么难缠了.这次也借此机会对跨域问题来给刨根问底,结合实际的开发项目,查阅了相关资料,算是解决了跨域问题...有必要记下来备忘, 跨域的安全限制都是指浏览