第六章:数据存储全方案——详解持久化技术
前面操作的数据都是瞬时数据,瞬时数据就是存储在内存当中,有可能会因为程序关闭或其他原因导致内存被回收而丢失的数据。
6.1 持久化技术简介
数据持久化就是指将那些内存中的瞬时数据保存到存储设备中,保证即使在手机或电脑关机的情况下,这些数据仍然不会丢失。
持久化技术提供一种机制可以让数据在瞬时状态和持久状态之间进行转换。
Android系统中主要提供了3种方式用于简单地实现数据持久化功能,即文件存储、SharedPreferences存储以及数据库存储。除了这三种外,还可以存储在SD卡上。
6.2 文件存储
最基本的一种数据存储方式,它不对存储的内容进行任何的格式化处理,所有数据都是原封不动地保存到文件当中的,因而它比较适合用于存储一些简单的文本数据或是二进制。
6.2.1 将数据存储到文件中
Context类中提供了一个openFileOutput()方法,可以用于将数据存储到指定的文件中。这个方法接收两个参数:
第一个参数是文件名,在文件创建的时候使用的就是这个名称。
注意:这里的文件名不包含路径,所有文件默认存储到/data/data/<package name>/files/下
第二个参数是文件的操作模式,主要有两种模式可选:
MODE_PRIVATE(系统默认:当指定同样文件名的时候,所写入的内容会覆盖原文件中的内容)
MODE_APPEND(表示如果改文件已存在,就在文件里面追加内容,不存在就创建新文件)
//代码展示,如何将一段文本内容保存到文件中
public void save() {
String data = "Data to save";
FileOutputStream out = null;
BufferedWriter writer = null;
try {
//通过openFileOutput方法能够得到一个FileOutputStream对象
out = openFileOutput("data",Context.MODE_PRIVATE);
//借助FileOutputStream对象构建出一个OutputStreamWriter对象
//再借助OutputStreamWriter对象构建出一个BufferedWriter对象
writer = new BufferedWriter(new OutputStreamWriter(out));
writer.write(data);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (write != null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//学习如何在Android项目中使用文件存储技术
//创建一个FilePersistenceTest项目,并修改activity_main.xml中的文件
//布局中加入了一个文本框
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android
android:orientation="vertical"
android:layout_weight="match_parent"
android:layout_height="match_parent" >
<EditText
android:id="@+id/edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Type something here"
/>
</LinearLayout>
//在上面文本框中输入的数据被回收前,将它存储到文件中
//MainActivity
public class MainActivity extends AppCompatActivity {
private EditText edit;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.actvity_main);
edit = (EditText) findViewById(R.id.edit);
}
@Override
protected void onDestroy() {
super.onDestroy();
String inputText = edit.getText().toString();
save(inputText);
}
public void save(String inputText) {
FileOutputStream out = null;
BufferedWriter writer = null;
try {
//通过openFileOutput方法能够得到一个FileOutputStream对象
out = openFileOutput("data",Context.MODE_PRIVATE);
//借助FileOutputStream对象构建出一个OutputStreamWriter对象
//再借助OutputStreamWriter对象构建出一个BufferedWriter对象
writer = new BufferedWriter(new OutputStreamWriter(out));
writer.write(data);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (write != null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
6.2.2从文件中读取数据
Context类中还提供了一个openFileInput()方法,用于从文件中读取数据。这个方法要比openFileOutput()更简单一些,它只接收一个参数,即要读取的文件名,然后系统会自动到文件目录下去找,并返回一个FileInputStream对象,这个对象之后在通过Java流的方式就可以将数据读取出来了。
//代码展示如何从文件中读取文本数据
public String load() {
FileInputStream in = null;
BufferedReader reader = null;
StringBuilder content = new StringBuilder();
try {
//通过openFileInput方法获取到一个FileInputStream对象
in = openFileInput("data");
//通过FileInputStream对象构建出一个InputStreamReader对象,再使用InputStreamReader对象获取BufferedReader对象
reader = new BufferedReader(new InputStreamReader(in));
String line="";
while((line = reader.readLine()) != null) { //readLine方法是读取一行
content.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch {
e.printStackTrace();
}
}
}
return content.toString();
}
//将文本框中才输入的数据持久化存储,并能接着继续输入
//MainActivity
public class MainActivity extends AppCompatActivity {
private EditText edit;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.actvity_main);
edit = (EditText) findViewById(R.id.edit);
String inputText = load();
if(!TextUtils.isEmpty(inputText)) { //TextUtils.isEmpty方法,一次性进行null和空字符串这两种判断
edit.setText(inputText); //setText方法是用于将内容填充到EditText里
edit.setSelection(inputText.length()); //setSelection方法将输入光标移动到文本的末尾位置以便继续输入,然后弹出一句还原成功的提示
Toast.makeText(this,"Restoring succeeded",Toast.LENGTH_SHORT).show();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
String inputText = edit.getText().toString();
save(inputText);
}
public void save(String inputText) {
FileOutputStream out = null;
BufferedWriter writer = null;
try {
//通过openFileOutput方法能够得到一个FileOutputStream对象
out = openFileOutput("data",Context.MODE_PRIVATE);
//借助FileOutputStream对象构建出一个OutputStreamWriter对象
//再借助OutputStreamWriter对象构建出一个BufferedWriter对象
writer = new BufferedWriter(new OutputStreamWriter(out));
writer.write(data);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (write != null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public String load() {
FileInputStream in = null;
BufferedReader reader = null;
StringBuilder content = new StringBuilder();
try {
//通过openFileInput方法获取到一个FileInputStream对象
in = openFileInput("data");
//通过FileInputStream对象构建出一个InputStreamReader对象,再使用InputStreamReader对象获取BufferedReader对象
reader = new BufferedReader(new InputStreamReader(in));
String line="";
while((line = reader.readLine()) != null) { //readLine方法是读取一行
content.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch {
e.printStackTrace();
}
}
}
return content.toString();
}
}
6.3 SharedPreferences 存储
SharedPreferences是使用键值对的方式来存储数据的,也就是说,当保存一条数据的时候,需要给这条数据提供一个对应的键,这样读取这个数据的时候,就可以通过这个键把响应的值取出来。它还支持存储多种不同的数据类型。
6.3.1 将数据存储到SharedPreferences中
需要用它存储数据,首先需要获取到SharedPreferences对象。
Android中主要提供了3种方式用于得到SharedPreferences对象:
1)Context类中的getSharedPreferences(参数1,参数2)方法
参数1:文件名称,不存在则创建一个
参数2:MODE_PRICATE,和传入0的效果相同,表示只有当前应用程序才能对文件读写
2)Activity类中的getPreferences(参数1)方法
参数1:MODE_PRICATE,和传入0的效果相同
注意:这个方法自动将当前活动的类名作为SharedPreferences的文件名
3)PreferenceManager类中的getDefaultSharedPreferences()
这是一个静态方法,它接受一个Context参数,并自动使用当前应用程序的包名作为前缀来命名SharedPreferences文件。得到SharedPreferences对象后就开始向它中存储数据了,主要分为以下三步:
1> 调用SharedPreferences对象的edit方法获取一个SharedPreferences.Editor对象
2> 向SharedPreferences.Editor对象中添加数据,比如布尔类型putBoolean方法...
3> 调用apply方法将添加的数据提交,从而完成数据存储的操作
//新建一个SharedPreferencesTest项目,修改activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android
android:orientation="vertical"
android:layout_weight="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/save_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Save data"
/>
</LinearLayout>
//放置一个按钮,用于将一些数据存储到SharedPreferences文件当中
//修改MainActivity中的代码
public class MainActivity extends AppCompatActivity {
@Override
protected void OnCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button saveData = (Button) findViewById(R.id.save_data);
saveData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SharedPreferences.Editor editor = getSharedPreferences("data",MODE_PRIVATE).edit();
editor.putString("name","Tom");
editor.putInt("age",28);
editor.putBoolean("married",false);
editor.apply();
}
});
}
}
6.3.2 从SharedPreferences中读取数据
SharedPreferences对象中提供了一系列的get方法用于对存储的数据进行读取。每种get方法都对应了SharedPreferences.Editor中的一种out方法。
eg:getBoolean(参数1,参数2)
参数1:键
参数2:当前传入的键找不到对应的值时会以什么样的默认值进行返回。
//修改SharedPreferencesTest项目,修改activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android
android:orientation="vertical"
android:layout_weight="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/save_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Save data"
/>
<Button
android:id="@+id/restore_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Restore data"
/>
</LinearLayout>
//上面增加了一个还原按钮,希望通过点击这个按钮来从SharedPreferences文件中来读取数据
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
Button restoreData = (Button) findViewById(R.id.restore_data);
restoreData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SharedPreferences pref = getSharedPreferences("data",MODE_PRIVATE);
String name = pre.getString("name","");
int age = pre.getInt("age",0);
boolean married = pre.getBoolean("married",false);
Log.d("MainActivity","name is"+name);
Log.d("MainActivity","age is"+age);
Log.d("MainActivity","married is"+married);
}
});
}
}
6.3.3 实现记住密码的功能
//实现记录功能需要在登录界面完成,打开Broadcast-BestPractice项目
//修改activity_log.xml
<LinearLayout xmln:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="60pd" >
<TextView
android:layout_width="90dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textSize="18sp"
android:text="Account:"/>
<EditView
android:id="@+id/account"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="60pd" >
<TextView
android:layout_width="90dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textSize="18sp"
android:text="Password:"/>
<EditView
android:id="@+id/account"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<CheckBox //一个复选框控件,可以通过点击的方式来进行选中和取消
android:id="@+id/remember_pass"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:text="Remember Password:"/>
</LinearLayout>
<Button
android:id="@+id/login"
android:layout_width="match+parent"
android:layout_height="60dp"
android:text="Login"/ >
</LinearLayout>
//然后修改LoginActivity
public class LoginActivity extens BaseActivity {
private SharedPreferences pref;
private SharedPreferences.Editor editor;
private EditText accountEdit;
private EditText passwordEdit;
private Button login;
private CheckBox rememberPass;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
pre = PreferenceManager.getDafaultSharedPreferences(this);
accounyEdit = (EditText) findViewById(R.id.account);
passwordEdit = (EditText) findViewById(R.id.password);
rememberPass = (CheckBox) findViewById(R.id.login);
login = (Button) findViewById(R.id.login);
Boolean isRemember = pre.getBoolean("remember_password",false);
if(isRemember) {
//将账号和密码都设置到文本框中
String account = pre.getString("account","");
String account = pre.getString("password","");
accountEdit.setText(account);
passwordEdit.setText(password);
rememberPass.setChecked(true);
}
login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String account = accountEdit.getText().toString();
String password = passwordEdit.getText().toString();
//如果账号是a,密码是1,就认为登录成功
if(account.equals("a") && password.equals("1")) {
editor = pre.edit();
if (rememberPass.isChecked()) { //检查复选框是否被选中
editor.putBoolean("remember_password",true);
editor.putString("account"a);
editor.putString("password",1);
} else {
editor.clear();
}
editor.apply();
Intent intent = new Intent(LoginActivity.this,MainActivity.class);
startActivity(intent);
finish(); //销毁当前活动
} else {
Toast.makeText(LoginActivity.this,"hhh",Toast.LENFTH_SHORT).show();
}
}
});
}
}