这是我参与11月更文挑战的第15天,活动详情查看:2021最后一次更文挑战
文章目录
数据存储介绍
数据存储也称之为数据持久化。表现为将程序处理过程中需要保存的数据存储到硬盘的某个文件中。在 Android 中,数据存储主要区分为:
1、偏好设置
2、IO存储/文件存储
3、SQLite数据库存储
4、网络存储
SharedPreferences:偏好设置
偏好设置通常用于存储用户在使用软件时的个性化设置。存储的数据通常存在 K-V 关系,且数据量较小,数据是私有的(不需要共享给其他应用程序)。
偏好设置的本质是使用 XML 存储数据,存储的文件将存在 /data/data/应用程序包名/shared_prefs/ 文件夹下。
如果在设置-应用程序-当前应用-清除数据,会清楚所有偏好设置的设置项。
【保存数据】
1、通过 Context 对象的 getSharedPreferences(String name,int mode)方法获取 SharedPreferences 对象,其中,第 1 个参数标识存储偏好设置的文件的文件名,不包括扩展名;第 2 个参数表示文件的访问类型,取值为 Context.MODE_PREVATE
2、通过 SharedPreferences 对象的 edit()方法,获取文件的编辑工具(Editor 接口类型)的对象
3、调用 Editor 对象的 put???(String key, ?? value) 系列方法执行编辑,即编辑文件,将数据写入文件内部
4、调用 Editor 对象的 commit() 方法提交写入
【读取数据】
1、通过 Context 对象的 getSharedPreferences(String name, int mode)方法获取 SharedPreferences 对象,其中,第 1 个参数表示存储偏好设置的文件的文件名,不包括扩展名,第 2 个参数表示文件的访问类型,取值为 Context.MODE_PREVATE
2、通过 SharedPreferences 对象的 get???() 系列方法读取数据
举例:通过 SharedPreferences 保存数据
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="15dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="请输入用户名" />
<EditText
android:id="@+id/et_username"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="请选择性别" />
<RadioGroup
android:id="@+id/rg_male"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RadioButton
android:id="@+id/rb_male"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="男" />
<RadioButton
android:id="@+id/rb_female"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="女" />
</RadioGroup>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="请输入年龄" />
<EditText
android:id="@+id/et_age"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number" />
<Button
android:id="@+id/btn_submit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="提交" />
</LinearLayout>
MainActivity
public class MainActivity extends AppCompatActivity {
private EditText etUserName;
private RadioButton rbGenderMale;
private RadioButton rbGenderFeMale;
private EditText etAge;
private Button btnSubmit;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
setListeners();
showData();
}
private void initView() {
etUserName = findViewById(R.id.et_username);
rbGenderMale = findViewById(R.id.rb_male);
rbGenderFeMale = findViewById(R.id.rb_female);
etAge = findViewById(R.id.et_age);
btnSubmit = findViewById(R.id.btn_submit);
}
private void setListeners() {
btnSubmit.setOnClickListener(view -> {
String username = etUserName.getText().toString().trim();
String gender = rbGenderMale.isChecked() ? "男" : "女";
int age = Integer.parseInt(etAge.getText().toString());
//执行保存
String name = "user-info";//保存偏好设置的文件的文件名,不需要指定扩展名
int mode = Context.MODE_PRIVATE;//文件的访问模式
SharedPreferences sharedPreferences = getSharedPreferences(name, mode);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("_username", username);
editor.putString("_gender", gender);
editor.putInt("_age", age);
editor.commit();
Toast.makeText(MainActivity.this, "保存成功", Toast.LENGTH_SHORT).show();
});
}
private void showData() {
//显示偏好设置中的数据
String name = "user-info";
int mode = Context.MODE_PRIVATE;
SharedPreferences sharedPreferences = getSharedPreferences(name, mode);
String username = sharedPreferences.getString("_username", null);
String gender = sharedPreferences.getString("_gender", null);
int age = sharedPreferences.getInt("_age", 0);
etUserName.setText(username);
if ("女".equals(gender)) {
rbGenderFeMale.setChecked(true);
} else {
rbGenderMale.setChecked(true);
}
etAge.setText(age == 0 ? "" : (age + ""));
}
}
我们把程序运行到手机上
可以在 Android Studio 查看 Device File Explorer ,选择手机
查看 user-info.xml 的文件内容
I/O存储/文件存储
如果需要存储的数据表现为独立的文件,则需要使用 IO 方式存储,例如下载得到的图片、歌曲等,或软件运行过程中产生的日志文件等。
使用 IO 存储时,如果是纯问比文件(.txt 文件、.xml 文件、.log 文件)这些文件内部的数据一共是不被频繁读取和解析的。
【存储分类】
IO 存储根据存储的文件位置、是否需要共享给其他应用程序、数据的时效性,可以分为:
1、内部存储
2、外部存储
3、内部缓存存储
4、外部缓存存储
【内部存储】
可以通过 openFileInput(String name)方法获取 FileInputStream 对象,可以通过 openFileOoutput(String name, int mode) 方法获取 FileInputStream 对象。
内部存储的文件都是私有的,所以在设置访问模式时始终使用 MODE_PRIVATE。
内部存储的文件将保存在 /data/data/应用程序报名/files 文件夹。
内部存储的文件也可以通过系统的设置功能进行清除。
与内部存储相关的方法还有:
String[] fileList():获取文件列表
boolean deleteFile(String name):删除文件
File getDir(String name,int mode):获取某个自定义的文件夹的 File 对象,如果自定义文件夹,系统会默认添加 app_ 做位前缀,例如指定的文件夹名称是 logs 时,实际表现为 app_logs,不过开发者可以无视前缀,获取文件夹对象时始终使用 logs 即可。
【外部存储】
外部存储的义件是存储在 sdcard 下的,这些文件都是公有的。
外部存储是不可靠的,因为外部存储本身并不是始终可用的,并且,保存在外部存储中的文件可能被用户或其它 APP 删除或修改。
外部存储的访问应该允分使用 Enviroment 类来获取相关文件夹对象等。实现外部存储依然使用 Java 中的 IO。
【内部缓存存储】
缓存:暂时保存,用于存储有效性特征的数据或文件。
通过调用File getCacheDir()方法,可以获取内部缓存的文件夹的 File 对象,然后再创建出缓存文件的 File 对象,最终创建 FileInputstream / FileoutputStrearm实现文件的读写。
内部缓存将由 Android 系统进行管理和维护,会将该文件夹的容量控制在一定池围之内。
内部缓存也是应用程序私有的,即其它应用程序无法访问。
【外部缓存存储】
与内部缓存不同,外部缓存对应的存储目录是在 sdcard 下的。
通过调用File getExternalcacheDir()方法,可以获取外部缓存的文件夹的 File 对象,然后再创建出缓存文件的 File 对象,最终创建 FileInputstream/Fileoutputstream 实现文件的读写。
外部缓存是公有的。外部缓存与一般外部存储一样,都是不可靠的。
外部缓存不由 Android 系统进行管理和维护,但是,开发者应该制定一系列的逻辑进行管理,例如定期清除,或达到一定容量后清除等。
栗子
xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="请输入你的个性签名" />
<EditText
android:id="@+id/et_sign"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/btn_submit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="提交"/>
</LinearLayout>
MainActivity
public class MainActivity extends AppCompatActivity {
private EditText et_sign;
private Button btnSubmit;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
setListeners();
loadData();
}
private void loadData() {
FileInputStream fis = null;
InputStreamReader isr = null;
BufferedReader br = null;
StringBuffer content = new StringBuffer();
try {
fis = openFileInput("sign.txt");
isr = new InputStreamReader(fis);
br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
content.append(line);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
et_sign.setText(content);
}
private void setListeners() {
btnSubmit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String content = et_sign.getText().toString();
FileOutputStream fos = null;
try {
fos = openFileOutput("sign.txt", MODE_PRIVATE);
//getBytes():将一个字符串转化为一个字节数组byte[]的方法
byte[] bytes = content.getBytes();
fos.write(bytes);
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
fos = null;//让它被虚拟机视为垃圾
} catch (IOException e) {
e.printStackTrace();
}
}
}
Toast.makeText(MainActivity.this, "保存成功", Toast.LENGTH_SHORT).show();
}
});
}
private void initView() {
et_sign = findViewById(R.id.et_sign);
btnSubmit = findViewById(R.id.btn_submit);
}
}
栗子
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="请输入你的个性签名" />
<EditText
android:id="@+id/et_sign"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/btn_submit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="提交" />
</LinearLayout>
MainActivity
public class MainActivity extends AppCompatActivity {
private EditText et_sign;
private Button btnSubmit;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
setListeners();
loadData();
}
private void initView() {
et_sign = findViewById(R.id.et_sign);
btnSubmit = findViewById(R.id.btn_submit);
}
private void setListeners() {
btnSubmit.setOnClickListener(view -> {
String content = et_sign.getText().toString();
FileOutputStream fos = null;
try {
fos = openFileOutput("sign.txt", MODE_PRIVATE);
//getBytes():将一个字符串转化为一个字节数组byte[]的方法
byte[] bytes = content.getBytes();
fos.write(bytes);
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
fos = null;//让它被虚拟机视为垃圾
} catch (IOException e) {
e.printStackTrace();
}
}
}
Toast.makeText(MainActivity.this, "保存成功", Toast.LENGTH_SHORT).show();
});
}
private void loadData() {
FileInputStream fis = null;
InputStreamReader isr = null;
BufferedReader br = null;
StringBuffer content = new StringBuffer();
try {
fis = openFileInput("sign.txt");
isr = new InputStreamReader(fis);
br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
content.append(line);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
et_sign.setText(content);
}
}
运行程序到手机上
以上这些内容是上课时讲的,我的博客中还有一篇关于数据存储的文章,是根据《第一行代码 第三版》写的,对于数据存储写的更全面,还有 kotlin 版本代码。👉传送门