OnSharedPreferenceChangeListener调用失败之坑 强引用 软引用 弱引用 虚引用之分

来源:互联网 发布:淘宝定金尾款是同价格 编辑:程序博客网 时间:2024/06/08 18:29

引用这东西,之前一直都没怎么了解,从来不都是new对象的么,直到用 SharedPreference 的时候,碰到 OnSharedPreferenceChangeListener ,被坑了一把,才开始了解引用的概念,然后把玩一番。初看觉得这东西好麻烦,仔细了解一下之后才发现,这东西在内存控制方面还是很重要,很实用的(-_-||不过目前做的项目对内存方面几乎没有克制的要求,哈哈)。

故事是这样的,代码里将一个值存到 SharedPreference 里面,如果其他的地方改变这个值,我需要将新的值给显示出来,一个很简单的逻辑,可做完之后发现运行的结果与实际情况有时候会有出入,改动并没有被显示出来。分析来分析去,都觉得没问题啊,逻辑正常啊(程序员遇到bug都是这么想的)一直没有怀疑到监听失效这上面来。
后来经过打 log 发现,居然是,额,监听有时候有效,有时候无效,OnSharedPreferenceChangeListener 没有被触发。
第一次碰到这种监听无效的情况,一脸懵逼。

内部的项目的代码就不分享了,用一个 demo 来说明一下。
点击 Button,计数加1,将点击数存到 SharedPreference 中,用 OnSharedPreferenceChangeListener 来监听 SharedPreference 中值的变化,变化的时候将计数打印出来,在页面之前换来换去,运行一段时间会发现,OnSharedPreferenceChangeListener 失效了。

import android.content.SharedPreferences;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;import android.widget.Button;import android.view.View;public class MainActivity extends AppCompatActivity {    private SharedPreferences spJ;    private SharedPreferences.Editor editor;    private Button buttonNum;    private int clickN;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        final int initTimes=0;        clickN=initTimes;        buttonNum=(Button)findViewById(R.id.Button_Num);//获取按键控件        spJ =getApplicationContext().getSharedPreferences("ClickN",MODE_PRIVATE);        editor= spJ.edit();        buttonNum.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                clickN++;                editor.putInt("times",clickN).commit();            }        });        spJ.registerOnSharedPreferenceChangeListener(new SharedPreferences.OnSharedPreferenceChangeListener() {            @Override            public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {                Log.d("MainActivity ", "onSharedPreferenceChanged:  " + spJ.getInt("times",initTimes));            }        });    }}

XML 就不贴了,多简单的一个 demo 啊,这么简单的代码,居然还会失效,额,也是醉了。

不过看了源码中关于 registerOnSharedPreferenceChangeListener 的描述后,也就不觉得奇怪了,居然,用的,是,弱引用,真的是够坑的。
这里写图片描述

OK了,下面区分一下强引用,软引用,弱引用,虚引用之间的区别吧

强引用

从名字也很好理解,就是很强的引用咯,那么她有多强呢?即使在内存很紧缺的情况下,即使程序报出 Out Of Memory 错误,它也不会被回收的。这种我们在编程中用的是最广了,比如说 new,基本上大部分的对象都是强引用了。

软引用

软引用,顾名思义,就是比较弱的一种引用,弱是相对于上一个来说的。这个还是比较讲义气的,当内存不够用的时候,它会牺牲自己,贡献出占用的内存,避免发生 Out Of Memory。当内存足够时,它是比较安全的,并不会被回收掉。

弱引用

弱引用,就是比较弱咯,弱到什么程度呢,如果一不小心,被垃圾回收机制检测到,就被回收,貌似跟此时内存够不够没有关系。反正跟垃圾回收机制的关系很不好,见面就是掐啊。

虚引用

这个,就比较奇葩了,因为比弱还弱,弱到什么程度呢。对对象的生命周期没有影响,而且,无法通过弱引用得到对象,弱的不要不要的。它基本只有一个作用,在对象被回收的时候,得到一个系统通知,这一点,也是它唯一可以被利用的一点,也是最关键的一点。
怎么用呢?在需要对内存十分克制的开发中,这一点很重要,可以通过这一点来准确的判断对象是否被回收,来决定是否申请一个新的对象来占用内存。准确是相对通常用的 finalize() 方法来说的。
finalize() 被调用的时候,只是准备好释放存储空间,只有在下一次垃圾收集的过程中,对象的内存才会被清除掉,并不准确。还有一点,存在 finalize() 重写不规范的情况,如果在该方法中又创建了一个强引用来指向要被销毁的对象时,这个对象就重新复活了,运行 finalize() 之后并不会被干掉。
而使用虚引用就不会存在这种问题,只有当对象占用的内存空间被清除的时候,才会返回系统通知,这个时候,被销毁的对象是不会被重新激活的。这个消息才是最精确的。

解决方法

知道原理,要改这前的那个 bug 就好办了,既然是安卓,那就结合安卓 activity 的生命周期,在 onPause( ) 的时候主动注销监听,在 onResume()的时候创建监听就可以了。其实解这个 bug 还有其他的方法,有兴趣的读者可以自己去试一下。

    @Override    public void onPause() {        spJ.unregisterOnSharedPreferenceChangeListener(profileChangedSPListener);        super.onPause();    }    @Override    public void onResume() {        spJ.registerOnSharedPreferenceChangeListener(profileChangedSPListener);        super.onResume();    }

好了,到处都是坑,这就对了,不然哪来那么多的 bug 解,哈哈哈。

阅读全文
0 0
原创粉丝点击