Android系统中主要提供三种方式用于简单地实现数据持久化功能——文件存储、SharedPreference存储、数据库存储。
作为最基本的存储方式,不对存储的内容进行任何的格式化处理,所有数据都是原封不动地保存到文件当中。
适用:简单的文本数据或二进制数据
默认文件存放目录:所有文件默认存储到/data/data/
openFileOutput()
方法:由Context类提供。将数据存储到指定文件。
两个参数:
/data/data//files/
目录下);返回:返回一个FileOutputStream对象,得到这个对象后就可以使用java流的方式将数据写入到文件中。
openFileInput()
方法:同样Context类提供。只接收一个参数:要读取的文件名。系统会自动在默认目录下去加载这个文件。
返回:返回一个FileInputStream对象,得到对象后可以通过Java流的方式将数据读取出来。
P202及之前几页
使用键值对的方式来存储数据。支持多种不同的数据类型存储。存储的数据是什么类型,读出仍然一样。显然更方便。
默认SharedPreferences文件存放目录:/data/data/
目录下
首先获取SharedPreferences对象。三种方法:
getSharedPreferences()
方法getPreferences()
方法getSharedPreferences()
方法相似,只不过只接收一个参数,为操作模式。会将当前活动的类名作为SharedPreferences的文件名。getDefaultSharedPreferences()
方法edit()
方法来获取一个SharedPreferences.Editor
对象;SharedPreferences.Editor
对象中添加数据。比如添加一个布尔型数据就用putBoolean()
方法,添加一个字符串则用putString()
方法,以此类推.apply()
方法将添加的数据提交,从而完成数据存储操作。对应Preferences.Editor
中每一个put方法,都有对应get方法进行读取。
这些get方法接收两个参数:参数一是键,传入存储数据时使用的键就可以得到相应的值了;第二个参数是默认值,表示当传入的键找不到时会以什么样的默认值进行返回,要与方法索取的数据类型一致。
Android系统内置。
SQLite作为一种轻量级的关系型数据库,运算速度快,占用资源少,适合移动设备使用。
Android提供了一系列的辅助行方法,可以使开发者去不编写SQL语句,也能完成增删改查操作。(CRUD:create添加,retrieve查询,update更新,delete删除)
适用:大量复杂的关系型数据。
默认存储目录:/data/data/
Android提供了一个SQLiteOpenHelper
帮助类用以帮助开发者更方便管理数据库。
SQLiteOpenHelper
类是一个抽象类,需要创建一个自己的类去继承它。SQLiteOpenHelper
类有两个抽象方法:onCreate()
和onUpgrade()
。必须在自己的帮助类中重写,实现创建、升级数据库的逻辑。SQLiteOpenHelper
类还有两个非常重要的实例方法:getReadableDatabase()
和getWritableDatabase()
。两个方法都可以创建(数据库不存在)或打开(数据库已存在)一个数据库,并返回一个可对数据库进行读写操作的对象。getReadableDatabase()
方法返回的对象将以只读的方式去打开数据库,而getWritableDatabase()
方法则抛出异常。SQLiteOpenHelper
类有两个构造方法可以重写,一般使用参数较少的那个。SQLiteOpenHelper
实例后再调用getReadableDatabase()
和getWritableDatabase()
方法就能够创建数据库了。此时重写的onCreate()
方法中创建表的逻辑也会得到执行。实践一下:
create table Book(
id integer primary key autoincrement,
author text,
price real,
pages integer,
name text)
integer表示整形;real表示浮点型;text表示文本类型;blob表示二进制类型。
primary key 将id列设为主键;autoincrement关键字表示id列是自增长的。
public class MyDatabaseHelper extends SQLiteOpenHelper {
private Context mContext;
public static final String CREATE_BOOK = "create table Book ("
+ "id integer primary key autoincrement,"
+ "author text,"
+ "price real,"
+ "pages integer,"
+ "name text)";
public MyDatabaseHelper(@Nullable Context context, @Nullable String name, @Nullable 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, "Create succeeded", Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
对类构造完成提取contex到私有变量中,传入Toast.makeText()
作为上下文。
调用db.execSQL(CREATE_BOOK)
直接执行赋值到CREATE_BOOK
常量的创建table的SQL语句字符串。
public class MainActivity extends AppCompatActivity {
private MyDatabaseHelper dbHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 1);
Button createDatabase = (Button) findViewById(R.id.create_database);
createDatabase.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dbHelper.getWritableDatabase();
}
});
}
}
创建成功显示Toast提示。
挖坑:调试工具P216
SQLiteOpenHelper
类中另一空方法onUpgrade()
方法:对数据库进行升级。
数据库已经创建完毕,table已经加入;由于数据库已经创建完成,再新增table时,要用onUpgrade()
方法去完成。
改写onUpgrade()
方法:
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//如果发现已经有表了就将其删除,再调用onCreate()方法重新创建
db.execSQL("drop table if exists Book");
db.execSQL("drop table if exists Category");
onCreate(db);
}
将活动中构造方法的版本号参数进行更新,则会执行onUpgrade()
方法。这里吧版本号1更换为2:
dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 2);
SQLiteDatabase类提供了一个insert()
方法,用于添加数据。
有三个参数:
put()
方法重载,用于向ContentValues中添加数据,只需要将表中的每个列名以及相应的待添加数据传入即可。添加数据样例(在活动中新增一个按钮用于完成添加数据的操作):
Button addData = (Button) findViewById(R.id.add_data);
addData.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
//开始组装第一条数据
values.put("name", "The Da Vinci Code");
values.put("author", "Dan Brown");
values.put("pages", 454);
values.put("price", 16.96);
db.insert("Book", null, values);//插入第一条数据
values.clear();
//开始组装第二条数据
values.put("name", "The Lost Symbol");
values.put("author", "Dan Brown");
values.put("pages", 510);
values.put("price", 19.95);
db.insert("Book", null, values);//插入第二条数据
}
});
修改表中已有数据:update()
方法,用于数据更新。
接收四个参数:
更新数据示例(在布局中添加一个按钮用于执行更新数据操作):
Button updateData = (Button)findViewById(R.id.update_data);
updateData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("price", 10.99);
db.update("Book", values, "name = ?", new String[]{"The Da Vinci Code"});
}
});
构建了一个ContentValues对象,并指定了一组数据。调用了update()
方法执行具体更新操作。第三、四个参数指定具体更新哪一行。第三个参数对应的是SQL语句的where部分,表示更新所有name等于?的行,而?是一个占位符,可以通过第四个参数提供的字符串数组为第三个参数中的每个占位符指定相应的内容。
删除数据:SQLiteDatabase类中的delete()
方法。
接收三个参数:
删除数据示例(在布局中添加一个按钮用于执行删除操作):
Button deleteButton = (Button)findViewById(R.id.delete_data);
deleteButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.delete("Book", "pages > ?", new String[] {"500"});
}
});
通过参数二参数三指定删除页数超过500页的书。指定行用法和update一致。
用于查询数据:query()
方法。
方法比较复杂,最短的一个方法也需要传入7个参数。
返回:返回一个Cursor对象,查询的所有数据都从这个对象中取出。
以下是参数介绍,各种重载都大同小异:
示例:
Button queryButton = (Button)findViewById(R.id.query_data);
queryButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
//查询Book表中所有的数据
Cursor cursor = db.query("Book", null, null, null, null,null,null);
if(cursor.moveToFirst()){
do{
//遍历Cursor对象,取出数据并打印
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());
}
cursor.close();
}
});
在Cursor类中使用了以下方法:
moveToFirst()
方法:将数据的指针移动到第一行的位置;getColumnIndex()
方法:获取到某一列在表中对应的位置索引,这个索引传入到相应的取值方法中,就可以得到从数据库中读取到的数据了。对于熟练使用SQL的开发者,同样有相应的方法可以通过使用SQL来操作数据库。CRUD操作如下:
可以看到,除了查询数据时调用的是SQLiteDatabase的rawQuery()
方法,其他的操作都是调用的execSQL()
方法。
LitePal是一款开源的Android数据库框架,采用了对象关系映射(ORM)的模式,并将我们平时开发最常用到的一些数据库功能进行了封装,使得不编写一行SQL就可以完成各种建表和增删改查的操作。
编辑app/build.gradle
文件,在dependencies闭包中添加如下内容:compile 'org.litepal.android:core:1.3.2'
;
在app/src/main目录中新建一个assets目录,在assets目录新建一个文件litepal.xml,编辑如下:
<litepal>
<dbname value="BookStore">dbname>
<version value="1">version>
<list>
list>
litepal>
标签指定数据库名,
标签指定数据库版本号,
标签指定所有的映射模型。
修改AndroidMainfest.xml,在application标签内添加android:name="org.litepal.LitePalApplication"
,如下:
<application
android:name="org.litepal.LitePalApplication"
...
android:theme="@style/AppTheme">
...
application>
至此配置完成。
LitePal采用的是对象关系映射(ORM)模式。也就是在面向对象语言和关系型数据库之间建立一种映射关系,使得可以利用面向对象的思维去操作数据库。
示例创建一张Book表。
package com.example.litepaltest;
public class Book {
private int id;
private String author;
private double price;
private int pages;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getPages() {
return pages;
}
public void setPages(int pages) {
this.pages = pages;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
这样就是使用面向对象的方法完成了一张表的构建。类中的每一个字段分别对应表中的一个列。(使用Alt+Insert快捷键可以直接生成相应的getter和setter方法)
将Book类添加到映射模型列表中,修改litepal.xml中的代码,将
标签添加到
标签中。
需要映射的模型都应当添加到
标签中。且类名必须使用完整的类名。
调用Connector.getDatabase()
方法就是一次简单的数据库操作,点击一下按钮,数据库就会完成自动创建。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button createDatabase = (Button) findViewById(R.id.create_database);
createDatabase.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Connector.getDatabase();
}
});
}
}
例如向Book表添加一个press字段,直接修改Book类中的代码即可:
...
private String press;
...
public String getPress() {
return press;
}
public void setPress(String press) {
this.press= press;
}
再添加一张Category表,只需要新建一个Category类:
在这里插入代码片
所有需要更新的内容修改完毕后,只需再修改litepal.xml中的代码,如下:
<litepal>
<dbname value="BookStore">dbname>
<version value="2">version>
<list>
<mapping class="com.example.litepaltest.Book">mapping>
<mapping class="com.example.litepaltest.Category">mapping>
list>
litepal>
如有新增表新增一下注册,再更新一下version即可。
按钮的onClick()
方法内创建一个对象,并通过set方法依次赋值,最后调用对象的save方法直接存入。
创建的表对象需要继承DataSupport类,这样可以继承save方法。
对象是否已经存储根据调用model.isSaved()
方法的结果来判断的,返回true表示已存储,返回false表示未存储。
返回true情况:1已经调用过model.save()
方法去添加数据;2model对象通过LitePal提供的查询API查出来的。