序
之前一直都是new对象的,压根就不知道什么引用之类的东西,直到用SharedPreference的时候,用到了OnSharedPreferenceChangeListener,被坑了一把,才了解到引用的概念,然后了解一番。初看觉得这东西好麻烦,仔细了解一下之后才发现,这东西在内存控制方面还是很重要,很实用的。
事情是这个样子,我将一个值存到SharedPreference里面,如果其他的地方改变这个值,我需要将新的值给显示出来,一个很简单的逻辑,可做完之后发现运行的结果与实际情况并不是每次都相符的,经过打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.view.View;
import android.widget.Button;
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 version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.uidq0205.jtodo.MainActivity">
<Button
android:id="@+id/Button_Num"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="00"/>
</LinearLayout>
这让人很奇怪啊,于是就往源码里看,还好,刚一看,就看出了问题,“not currently store a strong reference to the listener”。继续了解之后,发现,原来是引用惹的祸。
强引用
从名字也很好理解,就是很强的引用咯,那么有多强呢?即使在内存很不够用的时候,程序报出Out Of Memory错误,也不会回收强引用的对象的。这种我们在编程中用的是最广了,比如说new。
软引用
软引用,顾名思义,就是比较弱的一种引用,弱是相对于上一个来说的。这个还是比较讲义气的,当内存不够用的时候,他会牺牲自己,贡献出自己的内存,避免发生Out Of Memory。当内存足够时,他是比较安全的,并不会被回收掉。
弱引用
弱引用,就是比较弱咯,弱到什么程度呢,如果一不小心,被垃圾回收机制检测到,就有可能被回收,貌似跟此时内存够不够也没有关系。
虚引用
这个,就比较奇葩了,因为比弱还弱,弱到什么程度呢。对对象的生命周期没有影响,而且,无法通过弱引用得到对象,弱的不行不行的。它基本只有一个作用,在对象被回收的时候,得到一个系统通知,这一点,基本是它唯一可以被利用的一点。
这一点怎么用呢,在需要对内存十分克制的开发中,这一点很重要,可以通过这一点来准确的判断对象是否被回收,来决定是否申请一个新的对象来占用内存。准确是相对通常用的finalize()方法来说的。
finalize()被调用的时候,只是准备好释放存储空间,只有在下一次垃圾收集的过程中,对象的内存才会被清除掉,并不准确。还有一点,存在finalize()重写不规范的情况,如果在该方法中又创建了一个强引用来指向要被销毁的对象时,这个对象就重新复活了。
而使用虚引用就不会存在这种问题,只有当对象占用的内存空间被清除的时候,才会返回系统通知,这个时候,被销毁的对象是不会被重新激活的。
咋一看,会觉得区分起来蛮麻烦的,不过当你内存紧缺的时候就不会这么想了,哈哈。