OnSharedPreferenceChangeListener详解及出现不触发解决办法
作者:技术小黑屋 发布时间:2021-10-25 19:06:36
之前使用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修饰符
除此之外,因为局部变量无法在其所在方法外部访问,这样就导致了我们只可以使用方法中使用局部变量就行注册,在合适的时机却无法使用局部变量进行注销。
猜你喜欢
- 这篇文章主要介绍了Spring配置文件如何使用${username},文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习
- 所谓泛型:就是允许在定义类、接口指定类型形参,这个类型形参在将在声明变量、创建对象时确定(即传入实际的类型参数,也可称为类型实参)泛型类或接
- 原始Spring开发Person.java准备Person.java类:package com.jektong.spring;public
- 自定义starterSpringBoot中的starter是一种非常重要的机制,能够抛弃以前繁杂的配置,将其统一集成进 starter,应用
- 本文实例讲述了C#用匿名方法定义委托的实现方法。分享给大家供大家参考。具体实现方法如下://用匿名方法定义委托 class Program
- 首先打开 Visual Studio Installer 可以看到vs2022 只支持安装4.6及以上的版本,如图所示。那么该如何安装4.6
- RTF文档即富文本格式(Rich Text Format)的文档。我们在处理文件时,遇到需要对文档格式进行转换时,可以将RTF转为其他格式,
- 以下是SpringBoot项目中的常用配置类、jar包坐标等通用配置pom文件<!-- --><!-- 自定义配置文件
- JDK12的五大重要新特性Java12在March 19, 2019发布了。在2017年发布Java 9之后,Java平台发布节奏已从每3年
- 本文为大家介绍了FTP上传下载队列窗口的实现方法,供大家参考,具体内容如下1、首先看一下队列窗口的界面2、看一下上传队列窗口的界面3、看一下
- 继承什么是继承呢?继承(Inheritance)是一种联结类与类的层次模型。指的是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接
- 目录1 Exchanger 介绍2 Exchanger 实例exchange等待超时3 实现原理1 Exchanger 介绍前面分别介绍了C
- 一、CORS概述跨源资源共享标准通过新增一系列 HTTP 头,让服务器能声明那些来源可以通过浏览器访问该服务器上的各类资源(包括CSS、图片
- 上标是指比同一行中其他文字稍高的文字,而下标是指比同一行中其他文字稍低的文字。在生活中,我们常见的平方米、立方米等符号以及化学中的各种元素符
- 静态变量静态变量位于栈上,它是一个全局变量,在编译期就已经生成。public class Cow{public static int cou
- 通过反射根据提供的表名、POJO类型、数据对象自动生成sql语句。如名为 User 的JavaBean与名为 user 的数据库表对应,可以
- 自己写的一个日历记事本效果图 具体步骤:1.添加控件SkinEngine。 1.右键“工具箱”。“添加选项卡”,取名“皮肤”。
- Note:这篇文章是基于Android Studio 3.01版本的,NDK是R16。step1:创建一个包含C++的项目其他默认就可以了。
- Maven工程pom定义jdk版本今天把之前做的项目导进eclipse,然后发现报错,一些类在1.6中不支持,需要将JDK版本设置为1.7,
- 引用类型包含值类型字段,引用类型初始化后,值类型默认会被初始化为0、Null。 CLR允许为值类型定义构造器,但是构造器的调用,就必须显式的