文章目录
- 数据存储介绍
- SharedPreferences:偏好设置
- I/O存储/文件存储
数据存储介绍
数据存储也称之为数据持久化。表现为将程序处理过程中需要保存的数据存储到硬盘的某个文件中。在 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
<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;
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
<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;
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 版本代码。????传送门