数据存储-持久化技术

使用文件存储数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void save(String inputText){

FileOutputStream out = null;
BufferedWriter writer = null;

try {
out = openFileOutput("data",Context.MODE_PRIVATE);
writer = new BufferedWriter(new OutputStreamWriter(out));
writer.write(inputText);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(writer != null){
writer.close();
}
} catch (IOException e){
e.printStackTrace();
}
}
}

使用openFileOutput/openFileInput搭配相应的java输入输出流使用文件来存储数据。另外,记得writer.close()。

读取文件就将FileOutputStream->FileInputStreamBufferedWriter->BufferedReader,wirter.write->reader.redline()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public String load(){

FileInputStream inputStream = null;
BufferedReader reader = null;
StringBuilder content = new StringBuilder();

try {
inputStream = openFileInput("data");
reader = new BufferedReader(new InputStreamReader(inputStream));
String line = "";
while ((line = reader.readLine()) != null){
content.append(line);
}
} catch (IOException e){
e.printStackTrace();
} finally {
if (reader != null){
try {
reader.close();
} catch (IOException e){
e.printStackTrace();
}
}
}
return content.toString();
}

 

使用SharedPreferences存储数据

存放数据

1
2
3
4
5
SharedPreferences.Editor editor = getSharedPreferences("data",MODE_PRIVATE).edit();
editor.putString("name","Gaby");
editor.putBoolean("married",false);
editor.putInt("age",19);
editor.commit();

 

数据以xml的形式存在

取数据

1
2
3
4
5
6
7
SharedPreferences pref = getSharedPreferences("data",MODE_PRIVATE);//输入相对应的文件名
String name = pref.getString("name", "");
int age = pref.getInt("age",29);
Boolean married = pref.getBoolean("married",false);
Log.d("MainActivity","name is" + name);
Log.d("MainActivity","age is" + age);
Log.d("MainActivity","married is " + married);

 

加深理解:

1
2
3
4
5
6
7
8
9
10
11
12
SharedPreferences pref;
SharedPreferences.Editor editor;

pref = PreferenceManager.getDefaultSharedPreferences(this);

editor = pref.edit();

editor.putBoolean("remember_password", true); editor.putString("account", account);
editor.putString("password", password);
editor.commit();

editor.clear();//将SharedPreference中的数据全部清除掉t

 

使用SQLite(DBMS)创建数据库存储数据

细节理解:

1
2
3
dbHelper = new MyDatabaseHelpoer(MainActivity.this,"BookStore.db",null,1);//构建一个MydatabaseHelper对象

dbHelper.getWritableDatabase();//没找到BookStore.db这个数据库,即调用onCreate()创建该数据库

创建MyDatabaseHelper继承自SQLiteOpenHelper,覆写onCreate(),onCreate()中只需要包含数据库的操作就可以了(该数据库的名字即为构造函数中传入的名字)

一个MyDatabaseHelper的对象创建一个数据库,在onCreate()函数中执行对这个数据库的操作

创建好后,用ddms的explorer(顶上Tools->Android->Android Device Monitor->File Exploer,FileExplorer里的文件目录即为模拟器的文件目录)不能看到改数据库创建的表,用adb shell(adb是一个对连接的usb设备或者模拟器的调试器)看。要使用adb,需要先配置环境变量:增加C:\Users\Gaby\AppData\Local\Android\sdk\platform-tools到系统变量里面的Path,就可以在android studio 的terminal里使用

FileExplorer中/data/data/com.example.gaby.databasetest/databases/目录下有两个文件

  • xxx.db数据库
  • xxx.db-journal是为让数据库能够支持事务而产生的临时日志文件

建表语句:

1
2
3
4
5
6
public static final String CREATE_BOOK = "create table Book ("
+ "name text, "
+ "author text, "
+ "price real, "
+ "pages integer, "
+ "id integer primary key autoincrement)";

 

adb命令列举:

adb devices 列出当前设备
adb shell 进入设备的控制台
cd /data/data/com.example.gaby.databasetest/databases/进入模拟器该文件夹下
ls列出该目录下文件夹
sqlite3 BookStore.db 打开数据库

SQLite命令:

.table显示数据库中有哪些表
.schema查看建表语句
.exit/.quit 退出改数据库
select * from tableName;(有分号) 查询该表下有哪些纪录
接着exit退出设备控制台(adb)

注意execSQL(String s)传入的是字符串

CRUD

以下是按Android提供过的API来做的,实际上还可直接用SQL来操作数据库,见《第一行代码》page 260

向数据库中添加数据(db.insert()) (·)Create 添加
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
addData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
//di yi zu
values.put("name", "Game Of Thrones");
values.put("price",70.29);
values.put("author","George Martin");
values.put("pages",800);
db.insert("Book", null, values);
//di er zu
values.clear();
values.put("name","KaiFu's Saying");
values.put("price",50.34);
values.put("author","KaiFu");
values.put("pages",300);
db.insert("Book", null, values);
}
});

getWritableDatabase()和getReadableDatabase()除了升级和创建数据库,该方法本身会返回SQLiteDatabase对象。db.insert(“表名”, null, ContentValue对象)。

查询数据(db.query()) (·)Retrieve
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
queryData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
Cursor cursor = db.query("Book",null,null,null,null,null,null);
if(cursor.moveToFirst()){
do{
String name = cursor.getString(cursor.getColumnIndex("name"));
String author = cursor.getString(cursor.getColumnIndex("author"));
int pages = cursor.getInt(cursor.getColumnIndex("pages"));
double price = cursor.getDouble(cursor.getColumnIndex("price"));
Log.d("MainActivity","book name is" + name);
Log.d("MainActivity","book author is" + author);
Log.d("MainActivity","book pages is " + pages);
Log.d("MainActivity","book price is " + price);
}while(cursor.moveToNext());
}

}
});

db.query()返回Cursor对象

更新数据(db.update()) (·)Update 更新

注意:在MyDatabaseHelper中的onUpgrade()方法是用于升级数据库(如 多建/删除 一张表)。而更新数据,则是在特定表上修改数据

1
2
3
4
5
6
7
8
9
updataData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("price",12.33);
db.update("Book", values, "name = ?",new String [] {"Game Of Thrones"});
}
});

 

删除数据(db.delete) (·)Delete 删除
1
2
3
4
5
6
7
deleteData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.delete("Book","pages > ?", new String[] { "300" });
}
});

删除Book表中pages列值大于300的记录

数据库的事务(ACID要么全成功,要么全失败)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
replaceData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.beginTransaction();
try {
db.delete("Book",null,null);

ContentValues values = new ContentValues();
values.put("name","MySQL Crash Course");
values.put("price",12.93);
values.put("pages",400);
values.put("author","Ben Forta");
db.insert("Book",null,values);
db.setTransactionSuccessful();
} catch (Exception e){
e.printStackTrace();
} finally {
db.endTransaction();
}

}
});

解读:中途出现异常,db.setTransactionSuccessful()得不到执行,当结束事务db.endTransaction()时发现事务未成功执行,旧数据就不会被删除

升级数据库的最佳写法

需要注意以下几点:

  • 当用户下载的新版本覆盖安装旧版本时,实际上安装好的新版本应用程序具备所有新功能,只是数据库还残留在上一个版本,此时如果代码如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    private MyDatabaseHelpoer dbHelper;
    dbHelper = new MyDatabaseHelpoer(MainActivity.this,"BookStore.db",null,6);
    Button createDatabase = (Button) findViewById(R.id.createDatebase);
    createDatabase.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    dbHelper.getWritableDatabase();
    }
    });

则可视为手动更新数据库。如果将

1
2
3
private MyDatabaseHelpoer dbHelper;
dbHelper = new MyDatabaseHelpoer(MainActivity.this,"BookStore.db",null,6);
dbHelper.getWritableDatabase();

 

直接放置在主活动的onCreate(),而不放置在某个监听中,那么伴随活动的启动,数据库就被更新了。需要注意的是,dbHelper.getWritableDatabase()是在没有名为”BookStore.db”的数据库时就创建(执行MydatabaseHelperonCreate()函数)。如果本来就有,就会去执行onUpdate()函数,判断旧版本是哪一个,进而做出对数据库的更改

  • 新版本的不光要在onUpgrade()做出更改,还应以备旧版本更新。更要在onCreate()中做出完整的创建工作,以备用户直接下载新版本

  • onUpgrade() 中的 case 不加 break , 原因就是如果跨版本升级。比如旧版本的数据库停留在第二版,应用安装好了,且新版本的数据库是第8版,在更新数据库时就要从第二版更新到第八版,依次升级,升级结束后此时的数据库版本号为8

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    switch (oldVersion){
    case 4:
    db.execSQL(CREATE_MILK);

    case 5:
    db.execSQL("alter table Milk add column volume real");
    }
    }

个人想法

对于版本更新的问题,比如手机版的qq,不可能没更新一个版本就在,onUpgrade()里加一个型号,应该会要有个版本间隔(允许的最新和最旧的版本区间),不然200年以后,以腾讯的产品迭代速度,onUpgrade()里面的代码就太多了…

posted @ 2016-03-20 22:06  Gabyler  阅读(233)  评论(0编辑  收藏  举报