1.SharedPreferences
在Android中,通常需要存储一些数据,一些大型的数据如图片、JSON数据等,可以通过读写File的方式实现;一些大量级的关系型数据,可以通过数据库SQLite实现;而一些简单的、无安全风险的键值对数据,可以通过Android提供的SharedPreferences实现。
SharedPreferences是一个轻量级的存储类,特别适合用于保存软件配置参数。其背后是用xml文件存放数据,文件存放在/data/data//shared_prefs目录下。SharedPreferences所保存的数据会一直存在,除非被覆盖、移除、清空或文件被删除。(SharedPreferences保存的数据会随着应用的卸载而被删除)
SharedPreferences可以保存的数据类型有:int、boolean、float、long、String、StringSet。
SharedPreferences优点:
相对于文件存储来说比较方便,支持多种数据类型的存储。
SharedPreferences缺点:
①不安全,一般只用来存储配置信息
②对数据的操作单一
③存储相同的key值时,存入的数据会被覆盖
2.SharedPreferences使用
①获取到应用中的SharedPreferences
有三种方式。
1)getSharedPreferences(String,mode)
如果需要多个通过名称参数来区分的sharedpreference文件, 名称可以通过第一个参数来指定。可在app中通过任何一个Context 执行该方法。
SharedPreferences sp = context.getSharedPreferences("setting", Context.MODE_PRIVATE);
第一次访问名为"setting"的SharedPreferences文件时,系统会在应用数据目录下(/data/data/packageName/)的shared_prefs文件夹下,创建一个同名的xml文件。也就是说该文件不存在时,直接创建;如果已经存在,则直接使用。
mode指定为MODE_PRIVATE,则该配置文件只能被自己的应用程序访问。
2)getPreferences(mode)
这个方法默认使用当前类不带包名的类名作为文件的名称,配置文件仅可以被调用的Activity使用。当Activity只需要创建一个SharedPreferences对象的时候,可以使用该方法。
SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
3)PreferenceManager.getDefaultSharedPreferences(Context)
每个应用都有一个默认的配置文件preferences.xml,可以使用getDefaultSharedPreferences获取。
每个应用默认的配置文件的名字是:包名+_preferences,其文件读取类型为Context.MODE_PRIVATE。
看一下它的源码:
public static SharedPreferences getDefaultSharedPreferences(Context context) {
return context.getSharedPreferences( getDefaultSharedPreferencesName(context),getDefaultSharedPreferencesMode());
}
private static String getDefaultSharedPreferencesName(Context context) {
return context.getPackageName() + "_preferences";
}
private static int getDefaultSharedPreferencesMode() {
return Context.MODE_PRIVATE;
}
以上三种获取SharedPreferences的方法都提到了mode,来看一下关于mode的指定:
1)私有模式 Context.MODE_PRIVATE
只能被创建这个文件的当前应用访问。若文件不存在会创建文件;若创建的文件已存在则会覆盖掉原来的文件。
2)追加模式 Context.MODE_APPEND
只能被创建这个文件的当前应用访问。若文件不存在会创建文件;若文件存在则在文件的末尾进行追加内容。
3)可读模式 Context.MODE_WORLD_READABLE
创建出来的文件可以被其他应用所读取
4)可写模式 Context.MODE_WORLD_WRITEABLE
允许其他应用对其进行写入。
②读写SharedPreferences
1)写SharedPreferences
为了写sharedPreferences文件,需要通过执行edit()创建一个 SharedPreferences.Editor。通过putXXX()方法传递keys与values,最后通过commit() 或apply()提交改变。
SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString("number",number);
editor.putString("password",pwd);
editor.apply(); 或 editor.commit();
commit表示同步提交到SharedPreferences文件,获取是否同步成功的结果:boolean success = editor.commit();
apply表示异步提交到SharedPreferences文件:editor.apply();
2)读SharedPreferences
通过getXXX()方法从sharedPreferences中读取数据。在这些方法里面传入想要获取的value对应的key,并提供一个默认的value作为查找的key不存在时函数的返回值。如下:
SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
String number=sp.getString("number","");
String pasword=sp.getString("password","");
③移除数据
1)移除指定key的数据(由Editor对象调用)
abstract SharedPreferences.Editor remove(String key)
参数key:指定数据的key
2)清空数据(由Editor对象调用)
abstract SharedPreferences.Editor clear()
④系统默认的SharedPreferences
每个应用都有一个默认编好的preferences.xml文件,使用getDefaultSharedPreferences获取,其余操作是一样的。
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean("if_set_location", false);
editor.commit();
⑤访问其他应用的SharedPreferences
如果应用B要读写访问A应用中的Preference前提条件是,A应用中该preference创建时指定了Context.MODE_WORLD_READABLE或者Context.MODE_WORLD_WRITEABLE权限,代表其他的应用能访问读取或者写入。
具体步骤:
在B中创建一个指向A应用的Context:
Context otherAppsContext = createPackageContext("A应用的包名", Context.CONTEXT_IGNORE_SECURITY);
然后通过context获取到SharedPreferences实体:
SharedPreferences sharedPreferences = otherAppsContext.getSharedPreferences("SharedPreferences的文件名", Context.MODE_WORLD_READABLE);
String name = sharedPreferences.getString("key", "");
注:如果不通过创建Context访问其他应用的preference,也可以以读取xml文件方式直接访问其他应用preference对应的xml文件,如:
File xmlFile = new File(“/data/data/<package name>/shared_prefs/itcast.xml”);
//<package name>应替换成应用的包名。
3.SharedPreferences变化监听
这是当SharedPreferences改变时的回调,是SharedPreferences的一个接口。
通过registerOnSharedpreferenceListener方法设置监听:
SharedPreferences sp = getSharedPreferences( "testSP", Context.MODE_PRIVATE);
sp. registerOnSharedPreferenceChangeListener( new SharedPreferences. OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged( SharedPreferences sharedPreferences, String s) {
Log.i("spTest","sp changed, key is "+ s);
}
});
关于这个监听,官方文档是这样描述的:
Called when a shared preference is changed, added, or removed.
This may be called even if a preference is set to its existing value.
This callback will be run on your main thread.
使用这个监听时,如果你用匿名对象也就是下面这样,可能会被当作垃圾回收,导致会回调一次你的callback,达不到监听的效果。
//匿名回调
protected void onCreate(Bundle savedInstanceState) {
SharedPreferences sp = getSharedPreferences( "testSP", Context.MODE_PRIVATE);
sp.registerOnSharedPreferenceChangeListe ner(new OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged( SharedPreferences sharedPreferences, String key) {
Log.i(LOGTAG, "testOnSharedPreference ChangedWrong key =" + key);
}
});
}
这种写法看上去没有什么问题,而且很多时候开始几次onSharedPreferenceChanged方法也可以被调用。但是过一段时间(简单demo 不容易出现,但是使用DDMS中的gc会立刻导致接下来的问题),你会发现前面的方法突然不再被调用,进而影响到程序的处理。
原因剖析:
真正的原因就是注册的监听器被移除掉了。
首先先了解一下registerOnSharedPreferenceChangeListener注册的实现。
private final WeakHashMap<OnSharedPreferenc eChangeListener, Object> mListeners = new WeakHashMap<OnSharedPreferenceChangeListener, Object>();
registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
synchronized(this) {
mListeners.put(listener, mContent);
}
}
从上面的代码可以得知,一个OnSharedPreferenceChangeListener对象实际上是放到了一个WeakHashMap的容器中,执行完示例中的onCreate方法,这个监听器对象很快就会成为垃圾回收的目标,由于放在WeakHashMap中作为key不会阻止垃圾回收, 所以当监听器对象被回收之后,这个监听器也会从mListeners中移除。所以就造成了onSharedPreferenceChanged不会被调用。
解决办法:改为对象成员变量(推荐)
将监听器作为Activity的一个成员变量,在Activity的onResume进行注册,在onPause时进行注销。推荐在这两个Activity生命周期中进行处理,尤其是当SharedPreference值发生变化后,对Activity展示的UI进行处理操作的情况。这种方法是最推荐的解决方案。
private OnSharedPreferenceChangeListener mListener = new OnSharedPreferenceChangeL istener() {
@Override
public void onSharedPreferenceChanged( SharedPreferences sharedPreferences, String key) {
Log.i(LOGTAG, "instance variable key=" + key);
}
};
@Override
protected void onResume() {
sp.registerOnSharedPreferenceChangeListener(mListener);
super.onResume();
}
@Override
protected void onPause() {
sp.unregisterOnSharedPreferenceChangeList ener(mListener);
super.onPause();
}
总结一下:
①仅当添加或更改值时,监听器才会触发,设置相同的值将不会调用它;
②监听器需要保存在成员变量中,而不是匿名类,因为registerOnSharedPreferenceChangeListener使用弱引用进行存储,因此将被垃圾回收;
③除了使用成员变量,也可以由类直接实现,然后调用 registerOnSharedPreferenceChangeListener(this);
④当不再需要使用时