任何一个应用程序,说白了都是在不停的和数据打交道,通过网络获取的数据,一般为瞬时数据,当我们关闭APP的时候,这些数据就会消失。但有时候我们却希望在APP关闭的时候,保存这些数据,以期待在下次APP启动的时候能用到这些数据。那么,这里我们就需要用到数据持久化技术了。

其实数据持久化已经被广泛应用于各种程序设计的领域了,后台就不说了,iOS有plist归档数据库等。当然,Android也有类似与iOS的存储方式:文件存储SharedPreference以及数据库等,对了,Android还有SD卡。

文件存储

写数据

文件存储是Android中最基本的存储方式,它不对存储的内容进行格式化处理,所有的数据原封不动地保存到文件中,因而它适合存储一些简单的文本数据或者二进制数据。其实问价存储是最简单的,你只需要了解java流的使用就可以了。Android提供了一个openFileOutput()方法,可以将数据存储到指定的文件中。这个方法接收两个参数,第一个参数是文件名,注意这里的文件不需要包含路径,因为所有的文件都是默认存储到/data/data/<packagename>/files目录下的,第二个参数是文件的操作模式,主要有两种,一种是MODE_PTIVATE,它也是默认的模式,表示当我们写入的数据会覆盖之前的数据。另一种是MODE_APPEND,表示会追加数据。而且openFileOutput()方法会返回一个FileOutputStream对象,然后就可以以java流的方式写数据了。

public void saveData(String data) {
//直接使用文件存储
FileOutputStream fileOutputStream = null;
BufferedWriter bufferedWriter = null;
try {
fileOutputStream = openFileOutput("str.txt",MODE_APPEND);
bufferedWriter = new BufferedWriter(new OutputStreamWriter(fileOutputStream));
bufferedWriter.write(data);
}catch (IOException e){
e.printStackTrace();
}finally {
try {
if (bufferedWriter != null){
bufferedWriter.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
读数据

有写就有读,Context类还提供了一个openFileInput()方法,用于读取数据,这个方法只接收一个参数文件名,系统会自动到/data/data/<packagename>/files目录下读取这个文件的数据,并返回一个FileInputStream对象,然后通过java流的方式读取数据。

public String read() {
FileInputStream inputStream = null;
BufferedReader bufferedReader = null;
StringBuffer stringBuffer = new StringBuffer();
try {
inputStream = openFileInput("str.txt");
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String line = "";
while ((line = bufferedReader.readLine()) != null){
stringBuffer.append(line);
}
}catch (IOException e){
e.printStackTrace();
}finally {
try {
if (bufferedReader != null){
bufferedReader.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
return bufferedReader.toString();
}

SharedPreferences

说起这个,我想到了iOS的偏好设置,它也是一种轻量级的数据存储方式,和iOS不同的是,SharedPreferences却可以创建任意的偏好设置。iOS中的偏好设置本质是plist,Android中的偏好设置本质是XML。

写数据

要想写数据,首先需要获取SharedPreferences对象,Android提供了三种方式获取。

Context类中的getSharedPreferences()方法
此方法接收两个参数,第一个参数用于指定SharedPreferences文件的名称,如果指定的文件不存在则会创建,SharedPreferences文件都是存放在/data/data/<package name>/shared目录下的。第二个参数用于指定操作模式,目前只有MODE_PRIVATE这一种模式可选,表示只有本应用程序才可以对这个SharedPreferences文件进行读写。

Activity类中的getPreferences()方法
这个方法直接收一个操作参数,因为使用这个方法时会自动将当前活动的类名作为文件名。

PreferenceManager类中的getDefaultSharedPreferences()方法
这是一个静态方法,它接收一个Context参数,并自动使用当前应用程序的包名作为前缀来命名SharedPreferences文件,得到SharedPreferences对象后,就可以实现数据的存储了,主要有3步:

  • 调用SharedPreferences对象的edit()方法获取一个SharedPreferences.Editor对象
  • SharedPreferences.Editor对象中添加数据
  • 调用apply()方法将数据提交
//使用SharedPreferences 存储数据
public void saveData(String data) {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
//sharedPreferences = getSharedPreferences("share",MODE_PRIVATE);
//sharedPreferences = getPreferences(MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(data,"aaa");//以key-value的形式存储
editor.apply();
}
读数据

使用SharedPreferences读数据更加的简单,SharedPreferences有一系列的get方法来读取不同类型的数据。get方法的第一个参数是key,第二个参数是如果取不到这个key对应的值,返回的默认值。

public String read(String key) {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
//sharedPreferences = getSharedPreferences("share",MODE_PRIVATE);
//sharedPreferences = getPreferences(MODE_PRIVATE);
String string = sharedPreferences.getString(key, "default");
Toast.makeText(this,string,Toast.LENGTH_SHORT).show();
}

数据库存储

说到数据库存储,不得不了解的就是SQL语句,其实我的数据库了解的很少,SQL语句也只会一些简单的,如果想存储一些有复杂依赖关系的数据是需要数据库功底的。

无论Android还是iOS,系统都内置了名为sqlite数据库,我们可以使用标准的SQL语句来操作它。

创建数据库

Android专门提供一个叫SQLiteOpenHelper的抽象类,它有两个抽象方法onCreateonUpgrade,顾名思义,一个创建,一个更新。

SQLiteOpenHelper中还有两个方法,getReadableDatabase()getWritableDatabase()。这两个方法都可以创建或打开一个数据库,并返回一个数据库操作对象。不同的是当数据库不可写入时(如磁盘空间已满),getReadableDatabase()方法返回的对象将以只读的方式去打开数据库,而getWritableDatabase()方法则抛异常。

SQLiteOpenHelper中有两个构造方法可供重写,一般使用参数少的,第一个参数是Context,第二个参数是数据库名,第三个参数允许我们在查询数据的时候返回一个自定义的Cursor,一般都传null,第四个参数表示当前数据库的版本号,可用于对数据库升级操作。数据库存放的位置在/data/data/<package name>/database/目录下。

public class MyDataBaseHelper extends SQLiteOpenHelper {
private static final String CREATE_BOOK = "create table Book ("
+"id integer primary key autoincrement,"
+"author text,"
+"price real,"
+"pages integer,"
+"name text)";
private static final String CREATE_CATEGORY = "create table Category ("
+"id integer primary key autoincrement,"
+"author text,"
+"price real,"
+"pages integer,"
+"name text)";
private Context mContext;
public MyDataBaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
mContext = context;
}
@Override
public void onCreate(SQLiteDatabase db) {
//在此方法中创建数据库
db.execSQL(CREATE_BOOK);
Toast.makeText(mContext,"创建数据库",Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//如果版本号升级 则在此方法中对数据库进行升级
Toast.makeText(mContext,"更新数据库",Toast.LENGTH_SHORT).show();
if (newVersion == 2){
db.execSQL(CREATE_CATEGORY);
}
}
}
public class DataBaseActivity extends AppCompatActivity {
private MyDataBaseHelper mMyDataBaseHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_data_base);
//等到下次需要升级数据库时,需要修改这里的版本号
mMyDataBaseHelper = new MyDataBaseHelper(this,"BookStore.db",null,1);
Button button = (Button) findViewById(R.id.button2);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//会创建数据库
mMyDataBaseHelper.getWritableDatabase();
}
});
}
}

有数据库的地方就有增删改查

SQLiteOpenHelper提供了一个insert()方法,它接收三个参数,第一个是表名,第二个参数用于在未指定添加数据的情况下给某些可为空的列自动赋值为NULL,一般我们直接传null,第三个是ContentValues对象,它提供了一系列的put()方法,用于添加数据。

public void add() {
Button addButton = (Button) findViewById(R.id.addButton);
addButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase database = mMyDataBaseHelper.getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put("name","android");
contentValues.put("author","125");
contentValues.put("pages",40);
contentValues.put("price",16.50);
database.insert("Book",null,contentValues);
}
});
}

SQLiteOpenHelper提供一个delete()方法,第一个参数表名,第二,三个参用于约束删除某一行或某几行的数据,不指定的话默认删除所有行。

public void delete() {
Button deleteButton = (Button) findViewById(R.id.deleteButton);
deleteButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase database = mMyDataBaseHelper.getWritableDatabase();
database.delete("Book","name = ?",new String[]{"android"});
}
});
}

SQLiteOpenHelper提供了一个update()方法,用于对数据的更新。这个方法接收4个参数,第一个为表名,第二个为ContentValues对象,把要更新数据在这里组装,第三,四个用于约束更新某一行或某几行中的数据,不指定的话更新所有行。

public void update() {
Button updateButton = (Button) findViewById(R.id.updateButton);
updateButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase database = mMyDataBaseHelper.getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put("price",4545.9);
database.update("Book",contentValues,"name = ?",new String[]{"android"});
}
});
}

通常情况下,数据库中的查是最复杂的。SQLiteOpenHelper提供了一系列query()方法,最短的方法也有7个参数。第一个参数是表名。第二个参数用于指定去查询哪几行,如果不指定默认查询所有行。第三,四个用于约束查询某一行或某几行的数据,不指定则默认查询所有行。第五个参数参数表示指定需要group by的列,不指定则表示不对查询结果进行group by操作。第六个参数用于对group by的进一步过滤。第七个参数用于对查询的结果排序。

pubic void query() {
Button checkButton = (Button) findViewById(R.id.checkButton);
checkButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase database = mMyDataBaseHelper.getWritableDatabase();
Cursor cursor = database.query("Book",null,null,null,null,null,null);
if (cursor.moveToFirst()){
do {
String name = cursor.getString(cursor.getColumnIndex("name"));
double price = cursor.getDouble(cursor.getColumnIndex("price"))
Toast.makeText(DataBaseActivity.this,name+price,Toast.LENGTH_SHORT).show();
}
cursor.close();
}
});
}

这里我们看到,调用query()方法会返回一个Cursor对象,调用moveToFirst()可将查询指针指向第一行的位置,调用moveToNext()可将查询指针向下一行移动,如果没有下一行返回false,结束查询。然后我们就可以循环遍历查询数据。通过CursorgetColumnIndex()方法获取到某一列在表中对应的位置索引,通过这个索引就可以得到索引对应的数据,

当然,关于数据库的操作你完全可以不适用以上方法,如果你对SQL语句足够数量,你可以直接使用SQL语句进行操作,添加、更新、删除等操作都可以直接使用SQLiteDatabaseexecSQL()方法操作,第一个参数为SQL语句,第二个参数为值。查询操作可以使用SQLiteDatabaserawQuery()方法,第一个参数为SQL语句,第二个参数为值。

这里推荐一个库,LitePal,简化了数据库的操作,可以直接对JavaBean进行操作。