SharedPreferences存储
# 1. SharedPreferences存储简介
通常情兄下会发生这样的问题:我们在编辑短信的同时有电话打进来,那么接电话肯定是要启动另一个Activity,邢么当前编辑短信的Activity所编辑的信息我们想暂时保存下来,等接完电话后回到该Activity时,可以继续编辑短信。该功能需要如何去实现呢?
SharedPreferences使用xm格式为 Android应用提供一种永久的数据存储方式。Android提供了相关的API来处理这些数椐而不需要程序员直接操作这些文件或者考虑数椐同步问题.
- SP存储专门用来存储一些单一的小数据,存储数据的类型:boolean、float、int、long、String
- 数据保存的路径:
/data/data/<your app_ package _name>/shared_prefs/yyy.xml
,可以被处同一个应用中的所有 Activity访问。 - 可以设置数据只能是当前应用读取,而别的应用不可以;应用卸载时会删除此数据
# 1.1 Context中相关API
SharedPreferences对应SP文件的接口
context.getSharedPreferences(String name, int mode)
: 得到SP对象
public abstract SharedPreferences getSharedPreferences (String name, int mode)
- name: 文件名(不带.xml)
- mode: 生成的文件模式; 默认
MODE_PRIVATE
是否是私有的,即只有当前APP可以访问,其他几个值均已被废弃 - Editor sp.edit(): 得到Editor对象
- Xxx sp.getXxx(name,defaultValue):根据name得到对应的数据
MODE_PRIVATE(只能被自己的应用程序访问)
MODE_WORLD_READABLE(除了自己访问外还可以被其它应该程序读取)
MODE_WORLD_WRITEABLE(除了自己访问外还可以被其它应该程序读取和写入)
Editor:能更新sp文件的接口
- Editor put(name,value): 保存一个键值对,没有真正保存到文件中
- Editor remove(name)
- commit(): 提交,数据真正保存到文件中了
【使用】
// 创建SP, 第一个参数为xml保存的文件名,第二个为文件权限(私有)
SharedPreferences sharedPreferences = getApplicationContext().getSharedPreferences("shared_test", Context.MODE_PRIVATE);
// 获取SP编辑对象
SharedPreferences.Editor edit = sharedPreferences.edit();
// 存入数据
edit.putString(SPPHONRKEY, phoneNumber);
// 写到磁盘里
edit.commit();
// 从SP中读取数据,与put方法相对应的get方法
sharedPreferences.getString(key, defaultValue); // defaultValue为获取不到值时返回的默认值
2
3
4
5
6
7
8
9
10
11
edit.commit();
同步 直接写入磁盘(速度快)edit.apply();
非同步 写入内存,合适时机再持久化到磁盘(速度慢)
# 1.2 Activity类中的getPreferences
这个方法和Context中的getSharedPreferences
类似,不过只接受一个参数,因为使用getPreferences
时会将当前Activity的类名作为SharedPreferences的文件名
# 1.3 方法的使用
写入数据,由Editor对象调用
//写入boolean类型的数据
abstract SharedPreferences.Editor putBoolean(String key, boolean value)
//写入float类型的数据
abstract SharedPreferences.Editor putFloat(String key, float value)
//写入int类型的数据
abstract SharedPreferences.Editor putInt(String key, int value)
//写入long类型的数据
abstract SharedPreferences.Editor putLong(String key, long value)
//写入String类型的数据
abstract SharedPreferences.Editor putString(String key, String value)
//写入Set<String>类型的数据
abstract SharedPreferences.Editor putStringSet(String key, Set<String> values)
2
3
4
5
6
7
8
9
10
11
12
- key:指定数据对应的key
- value:指定的值
读取数据,由SharedPreferences对象调用
//读取所有数据
abstract Map<String, ?> getAll()
//读取的数据为boolean类型
abstract boolean getBoolean(String key, boolean defValue)
//读取的数据为float类型
abstract float getFloat(String key, float defValue)
//读取的数据为int类型
abstract int getInt(String key, int defValue)
//读取的数据为long类型
abstract long getLong(String key, long defValue)
//读取的数据为String类型
abstract String getString(String key, String defValue)
//读取的数据为Set<String>类型
abstract Set<String> getStringSet(String key, Set<String> defValues)
2
3
4
5
6
7
8
9
10
11
12
13
14
当读取不到指定的数据时,使用的默认值defValue
移除指定key的数据,由Editor对象调用
abstract SharedPreferences.Editor remove(String key)
清空数据,由Editor对象调用
abstract SharedPreferences.Editor clear()
提交数据,由Editor对象调用
abstract boolean commit()
# 1.4 SharedPreference的监听
api中还注册和注销SharedPreferences被编辑时的监听
SharedPreferences.OnSharedPreferenceChangeListener changeListener = new OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
//preferences被 编辑的SharedPreferences实例
//该SharedPreferences中被编辑的条目所对应的key
}
};
// userInfo注册监听事件
userInfo.registerOnSharedPreferenceChangeListener(changeListener);
// userInfo注销监听事件
userInfo.unregisterOnSharedPreferenceChangeListener(changeListener);
2
3
4
5
6
7
8
9
10
11
# 1.5 性能
- ShredPreferences是单例对象,第一次打开后,之后获取都无需创建,速度很快。
- 当第一次获取数据后,数据会被加载到一个缓存的Map中,之后的读取都会非常快。
- 当由于是XML<->Map的存储方式,所以,数据越大,操作越慢,get、commit、apply、remove、clear都会受影响,所以尽量把数据按功能拆分成若干份。
# 1.6 注意点
提示一
这里的用到的:
getSharedPreferences(String name, int mode);
getPreferences(int mode); // MODE_PRIVATE
2
3
都是在Acitivty中执行的。若不在Activity中,请使用
context.getSharedPreferences(String name, int mode);
context.getPreferences(int mode); // Conetxt.MODE_PRIVATE
2
3
提示二
所保存的SharedPreferences数据将一直存在,除非被覆盖、移除、清空或文件被删除。
SharedPreferences保存的数据会随着应用的卸载而被删除)
提示三
同时执行这两句代码的时候,第一行代码所写的内容会被第二行代码取代。
value的值被覆盖:
editor.putInt("age", 20);
//覆盖key为age的数据,得到的结果:age = 32
editor.putInt("age", 32);
2
3
value的值类型被覆盖:
editor.putString("age", "20");
//覆盖key为age的数据,得到的结果:age = 32 (int类型)
editor.putInt("age", 32);
2
3
提示四
执行以下代码会出现异常,指定key所保存的类型和读取时的类型不同
editor.putInt("age", 32);//保存为int类型
String age = userInfo.getString("age", "null");//读取时为String类型,出现异常
2
提示五
在这些动作之后,记得commit
editor.putInt("age", 20);//写入操作
editor.remove("age"); //移除操作
editor.clear(); //清空操作
editor.commit(); //记得commit
2
3
4
# 2. SP使用Demo
在文本框输入内容,切换到后台(模拟打电话的操作),则当前Activity处于onStop()
的状态;再次切换回来,处于onResume()
,从SP中读取数据
public class MainActivity extends AppCompatActivity {
private EditText mEditTextNote, mEditTextPhone;
private final static String SPNOTEKEY = "spnotekey";
private final static String SPPHONRKEY = "spphonekey";
private SharedPreferences sharedPreferences = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
// 实例化SharedPreferences对象
sharedPreferences = getApplicationContext().getSharedPreferences("shared_test", Context.MODE_PRIVATE);
}
private void initView() {
mEditTextNote = findViewById(R.id.edit_text_notes);
mEditTextPhone = findViewById(R.id.edit_text_phone);
}
// 还原数据
@Override
protected void onResume() {
super.onResume();
// 从SP中读取数据
mEditTextNote.setText(sharedPreferences.getString(SPNOTEKEY, "null"));
mEditTextPhone.setText(sharedPreferences.getString(SPPHONRKEY, "00000000000"));
}
// 存储数据
@Override
protected void onPause() {
super.onPause();
Log.i("TAG", "onCheckedChanged: 点击状态发生改变");
// 实例化编辑者对象
SharedPreferences.Editor edit = sharedPreferences.edit();
// 存储数据
String note = mEditTextNote.getText().toString();
String phoneNumber = mEditTextPhone.getText().toString();
if (TextUtils.isEmpty(note) && TextUtils.isEmpty(phoneNumber)) {
return;
}
edit.putString(SPNOTEKEY, note); // MD5加密
edit.putString(SPPHONRKEY, phoneNumber);
edit.commit();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# 综合使用
public class MainActivity extends Activity {
private static final String TAG = "SharedPreferencesTest";
// 保存数据SharedPreferences文件的名字
private final String PREFS_NAME = "MyPrefsFile";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
saveUserInfo();
getUserInfo();
removeUserInfo();
getUserInfo();
clearUserInfo();
getUserInfo();
}
// 保存用户信息
private void saveUserInfo() {
SharedPreferences userInfo = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
SharedPreferences.OnSharedPreferenceChangeListener changeListener = new OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
//preferences被 编辑的SharedPreferences实例
//该SharedPreferences中被编辑的条目所对应的key
}
};
//userInfo注册监听事件
userInfo.registerOnSharedPreferenceChangeListener(changeListener);
SharedPreferences.Editor editor = userInfo.edit();//获取Editor
//得到Editor后,写入需要保存的数据
editor.putString("username", "一只猫的涵养");
editor.putInt("age", 20);
editor.commit();//提交修改
Log.i(TAG, "保存用户信息成功");
}
// 读取用户信息
private void getUserInfo(){
SharedPreferences userInfo = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
String username = userInfo.getString("username", null);//读取username
int age = userInfo.getInt("age", 0);//读取age
Log.i(TAG, "读取用户信息");
Log.i(TAG, "username:" + username + ", age:" + age);
}
// 移除年龄信数据
private void removeUserInfo(){
SharedPreferences userInfo = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
SharedPreferences.Editor editor = userInfo.edit();//获取Editor
editor.remove("age");
editor.commit();
Log.i(TAG, "移除年龄数据");
}
// 清空数据
private void clearUserInfo(){
SharedPreferences userInfo = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
SharedPreferences.Editor editor = userInfo.edit();//获取Editor
editor.clear();
editor.commit();
Log.i(TAG, "清空数据");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# 3. SP传参注意内存泄漏
在Activity中实例化Data类时,往往需要通过构造传入Contect,默认传的就是this
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 可能会导致内存泄漏
MyData data = new MyData(this);
}
2
3
4
5
6
7
8
this比表示当前Activity的引用,如果当屏幕旋转时Activity可能被销毁来重新创建,将this
当前页面的引用传入到MyData中,因为MyData持有当前Activity的引用,所以导致当前Activity不能被及时销毁,可能导致内存泄露。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 正确使用
MyData data = new MyData(getApplicationContext());
}
2
3
4
5
6
7
8
getApplicationContext()
是当前APP全局的唯一的引用,可以理解为APP的顶级引用,它是一个单例的对象,只要APP在运行它就存在。
在推荐一篇不错的博文🔗http://gityuan.com/2017/06/18/SharedPreferences/
【文章参考】
[1] 带心情去旅行. 简书. https://www.jianshu.com/p/59b266c644f3