目录
1.键值对
键值对是Android四种数据存储方式之一,是一种轻量级的数据存储方式,常用于保存简单的配置信息、用户设置等。键值对有几种存储数据的方式,下面介绍主要的两种。
1.1共享参数SharedPreferences
SharedPreferences是Android的一个轻量级存储工具,它采用的存储结构为Key-Values的键值对方式。SharedPreferences的存储媒介是xml文件且以xml标记保存键值对。保存的文件位置一般为/data/data/应用包名/文件名.xml。
SharedPreferences对数据的操作类似于Map,有put方法存储数据以及get方法读取数据。调用getSharedPreferences方法获取SharedPreferences的实例,它有两个参数,第一个为文件名,第二个为操作模式,推荐取值为MODE_PRIVATE,下面是操作模式的取值表。
Android 6.0 (API 23) 之前
| 模式常量 | 值 | 描述 | 安全性 |
|---|---|---|---|
| MODE_PRIVATE | 0 | 默认模式,只有创建应用可以访问 | 安全 |
| MODE_WORLD_READABLE | 1 | 其他应用可读 | 不安全,已废弃 |
| MODE_WORLD_WRITEABLE | 2 | 其他应用可写 | 不安全,已废弃 |
| MODE_MULTI_PROCESS | 4 | 多进程支持(已废弃) | 已废弃 |
Android 7.0 (API 24) 及以后
当前唯一推荐的模式:MODE_PRIVATE
代码示例如下,在页面的布局文件中定义了两个编辑框以及一个单选按钮组,两个按钮,一个文本视图展示存储结果。
<?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=".Storage.KeyWithValueActivity"
android:orientation="vertical">
<EditText
android:id="@+id/editText_Name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:hint="Name"/>
<EditText
android:id="@+id/editText_Age"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number"
android:hint="Age"/>
<RadioGroup
android:id="@+id/radioGroup_gender"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<RadioButton
android:id="@+id/radio_male"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="男"/>
<RadioButton
android:id="@+id/radio_female"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="女"/>
<RadioButton
android:id="@+id/radio_other"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="其他"
android:checked="true"/>
</RadioGroup>
<Button
android:id="@+id/button_Write"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="提交" />
<Button
android:id="@+id/button_Read"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="读取" />
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="读取结果:\n" />
</LinearLayout>
部分Java代码,实现存储与读取操作。
public class KeyWithValueActivity extends AppCompatActivity implements View.OnClickListener, RadioGroup.OnCheckedChangeListener {
private EditText editText_Name, editText_Age;
private RadioGroup radioGroup_gender;
private Button button_Write, button_Read;
private TextView textView;
private SharedPreferences sharedPreferences;
private String gender = "其他";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_key_with_value);
editText_Name = findViewById(R.id.editText_Name);
editText_Age = findViewById(R.id.editText_Age);
radioGroup_gender = findViewById(R.id.radioGroup_gender);
button_Write = findViewById(R.id.button_Write);
button_Read = findViewById(R.id.button_Read);
textView = findViewById(R.id.textView);
button_Write.setOnClickListener(this);
button_Read.setOnClickListener(this);
radioGroup_gender.setOnCheckedChangeListener(this);
//文件名为info,若不存在该文件则自动创建。
sharedPreferences = getSharedPreferences("info", MODE_PRIVATE);
}
@Override
public void onClick(View view) {
if (view.getId() == R.id.button_Write) {
String Name = editText_Name.getText().toString();
String Age = editText_Age.getText().toString();
if(TextUtils.isEmpty(Name)){
editText_Name.setHint("姓名不可为空!");
editText_Name.requestFocus();
return;
}
if(TextUtils.isEmpty(Age)){
editText_Age.setHint("年龄不可为空!");
editText_Age.requestFocus();
return;
}
Calendar calendar = Calendar.getInstance();
String time = String.format("%d:%d:%d.%d",calendar.get(Calendar.HOUR_OF_DAY),calendar.get(Calendar.MINUTE),calendar.get(Calendar.SECOND),calendar.get(Calendar.MILLISECOND));
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("Name",Name);
editor.putInt("Age",Integer.parseInt(Age));
editor.putString("gender",gender);
editor.putString("time",time);
editor.apply();
textView.setText( "添加成功!\n" + textView.getText());
} else {
String Name = sharedPreferences.getString("Name","null");
int Age = sharedPreferences.getInt("Age",0);
String gender = sharedPreferences.getString("gender","null");
String time = sharedPreferences.getString("time","null");
textView.setText(String.format("%s\ntime:%s\nName:%s\nAge:%d\ngender:%s\n",textView.getText(),time,Name,Age,gender));
}
}
@Override
public void onCheckedChanged(RadioGroup radioGroup, int id) {
if (id == R.id.radio_male) {
gender = "男";
} else if (id == R.id.radio_female) {
gender = "女";
} else {
gender = "其他";
}
}
}
效果图如下,可以看到提交了两次但是时间不一致,这是因为每次提交都会覆盖原来的内容

1.2更安全的数据仓库DataStore
虽然共享参数SharedPreferences用起来会方便一些,但如果保存的数据过多的话当使用这些数据时会将整个文件加载进内存中,可能会导致主线程阻塞。对此Android官方推出了数据仓库DataStore并将其作为Jetpack库的基础组件。DataStore提供了两种实现方式,一种是Preferences DataStore用于存储键值对数据,一种是Proto DataStore用于自定义类型数据存储。第一种可以代替SharedPreferences。
由于DataStore并没有集成在SDK中,而是作为第三方框架提供,所以需要修改模块的build.gradle文件,在dependencies节点下添加两行配置,表示导入指定版本的DataStore库,代码如下。
implementation "androidx.datastore:datastore-preferences:1.0.0"
implementation "androidx.datastore:datastore-preferences-rxjava2:1.0.0"
代码示例如下,页面的布局文件的内容与上面类似,不做重复,主要看Java代码中的不同。
public class DataStoreActivity extends AppCompatActivity implements View.OnClickListener, RadioGroup.OnCheckedChangeListener {
private EditText editText_Name, editText_Age;
private RadioGroup radioGroup_gender;
private Button button_Write, button_Read;
private TextView textView;
private String gender = "其他";
private MyDataStore myDataStore;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_data_store);
editText_Name = findViewById(R.id.editText_Name);
editText_Age = findViewById(R.id.editText_Age);
radioGroup_gender = findViewById(R.id.radioGroup_gender);
button_Write = findViewById(R.id.button_Write);
button_Read = findViewById(R.id.button_Read);
textView = findViewById(R.id.textView);
button_Write.setOnClickListener(this);
button_Read.setOnClickListener(this);
radioGroup_gender.setOnCheckedChangeListener(this);
myDataStore = new MyDataStore(this,"dataStore_info");
}
@Override
public void onClick(View view) {
if (view.getId() == R.id.button_Write) {
String Name = editText_Name.getText().toString();
String Age = editText_Age.getText().toString();
if(TextUtils.isEmpty(Name)){
editText_Name.setHint("姓名不可为空!");
editText_Name.requestFocus();
return;
}
if(TextUtils.isEmpty(Age)){
editText_Age.setHint("年龄不可为空!");
editText_Age.requestFocus();
return;
}
Calendar calendar = Calendar.getInstance();
String time = String.format("%d:%d:%d.%d",calendar.get(Calendar.HOUR_OF_DAY),calendar.get(Calendar.MINUTE),calendar.get(Calendar.SECOND),calendar.get(Calendar.MILLISECOND));
myDataStore.setStringValue("Name",Name);
myDataStore.setIntValue("Age",Integer.parseInt(Age));
myDataStore.setStringValue("gender",gender);
myDataStore.setStringValue("time",time);
textView.setText( "添加成功!\n" + textView.getText());
} else {
String Name = myDataStore.getStringValue("Name");
int Age = myDataStore.getIntValue("Age");
String gender = myDataStore.getStringValue("gender");
String time = myDataStore.getStringValue("time");
textView.setText(String.format("%s\ntime:%s\nName:%s\nAge:%d\ngender:%s\n",textView.getText(),time,Name,Age,gender));
}
}
@Override
public void onCheckedChanged(RadioGroup radioGroup, int id) {
if (id == R.id.radio_male) {
gender = "男";
} else if (id == R.id.radio_female) {
gender = "女";
} else {
gender = "其他";
}
}
public class MyDataStore {
private RxDataStore<Preferences> dataStoreForPreferences;// 声明一个数据仓库实例
public MyDataStore(Context context, String dataStoreName) {
// 获取数据仓库工具的实例
dataStoreForPreferences = new RxPreferenceDataStoreBuilder(context.getApplicationContext(), dataStoreName).build();
}
// 设置指定名称的字符串值
public void setStringValue(String key, String value) {
//根据传入的key字符串创建一个Preferences.Key对象
//这个Key对象用于在DataStore中唯一标识这个字符串值
Preferences.Key<String> stringKey = PreferencesKeys.stringKey(key);
// 开始一个异步数据更新操作,返回一个Single<Preferences>对象
// updateDataAsync() 会启动一个事务性的更新操作
Single<Preferences> single = dataStoreForPreferences.updateDataAsync(preferences -> {
// 将当前的Preferences对象转换为可变的版本
// 这样才能修改其中的值
MutablePreferences mutablePreferences = preferences.toMutablePreferences();
// 在可变Preferences中设置键值对
// 这相当于SharedPreferences的putString操作
mutablePreferences.set(stringKey, value);
// 返回更新后的Preferences对象
// Single.just() 创建一个包含结果的Single对象
return Single.just(mutablePreferences);
});
}
// 获取指定名称的字符串值
public String getStringValue(String key) {
Preferences.Key<String> stringKey = PreferencesKeys.stringKey(key);
// 获取DataStore的数据流并转换为字符串流
// data() 返回一个Flowable<Preferences>,表示数据的变化流
// map() 操作将Preferences对象映射为具体的字符串值
Flowable<String> flowable = dataStoreForPreferences.data().map(preferences -> preferences.get(stringKey));
try {
// 从Preferences中获取指定key的值
// 如果key不存在则返回null
return flowable.blockingFirst();
} catch (Exception e) {
return "null";
}
}
// 设置指定名称的整型数
public void setIntValue(String key, int value) {
Preferences.Key<Integer> intKey = PreferencesKeys.intKey(key);
Single<Preferences> single = dataStoreForPreferences.updateDataAsync(preferences -> {
MutablePreferences mutablePreferences = preferences.toMutablePreferences();
mutablePreferences.set(intKey, value);
return Single.just(mutablePreferences);
});
}
// 获取指定名称的整型数
public int getIntValue(String key) {
Preferences.Key<Integer> intKey = PreferencesKeys.intKey(key);
Flowable<Integer> flowable = dataStoreForPreferences.data().map(preferences -> preferences.get(intKey));
try {
return flowable.blockingFirst();
} catch (Exception e) {
return 0;
}
}
}
}
效果图如下,通过数据仓库依旧可以进行读写文件。

经过上面两次代码的运行可以在对应的文件夹中看到新建的两个文件,一个在shared_preds目录下,一个在files/datastore目录下。两者新建的文件并不在同一个文件夹下且它们的后缀名不一样。

906

被折叠的 条评论
为什么被折叠?



