Android中常用的三种数据存储方法:
- 直接进行文件存储
- 使用SharedPreferences进行文件存储
- 数据库存储
1. 直接进行文件存储
1. 存储数据
存储数据使用的是Context类中提供的openFileOutput方法,这个方法有两个参数,第一个是文件名,第二个是存储模式,有两种模式:MODE_PRIVATE表示文件已存在时替换文件;MODE_APPEND表示文件已存在时将要存储的内容追加到文件末尾。
FileOutputStream out = null;
BufferedWriter writer = null;
try {
//实例化文件输出流对象out,设置保存文件名为data,输出模式为Private
out = openFileOutput("data", Context.MODE_PRIVATE);
//使用BufferedWriter来包裹OutputStreamWriter方法创建的writer对象用于提高效率
// 原理是避免频繁的转换器调用……(什么意思?)
writer = new BufferedWriter(new OutputStreamWriter(out));
//写入数据
writer.write(data);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//最后检查writer如果不为null则调用close方法关闭writer
if (writer != null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
2. 读取数据
FileInputStream in = null;
BufferedReader reader = null;
//实例化StringBuilder对象用于保存读取到的数据
StringBuilder content = new StringBuilder();
try {
in = openFileInput(fileName);
reader = new BufferedReader(new InputStreamReader(in));
String line = "";
//每次读取文件中的一行数据同时检查是否读取到文件末
while ((line = reader.readLine()) != null) {
//将读取到的数据放入实例化的对象content中
content.append(line);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (reader != null)
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
2. 使用SharedPreferences进行文件存储
和直接使用文件存储相比使用SharedPreferences进行文件存储的操作步骤就简单多了。不过使用SharePreferences存储需要获取SharePreferences对象,有三种办法可以获取:
- Context类中的getSharedPreferences方法,第一个参数为文件名,第二个参数为存储模式,这里只有MODE_PRIVATE一种模式可用
- Activity类中的getPreferences方法,参数只有一个存储模式参数,文件名会默认使用当前活动的类名
- PrefreencesManager类中的getDefaultSharedPreferences方法,这是一个静态方法,只接收一个Context参数,文件名使用当前程序的包名为前缀命名SharedPreferences文件
1. 存储数据
//使用getSharedPreferences().edit()方法实例化一个Editor对象
SharedPreferences.Editor editor = getSharedPreferences("data",MODE_PRIVATE).edit();
//设置数据
editor.putString("name","admin");
editor.putInt("mode",15);
//将数据存储到文件中
editor.apply();
2. 读取数据
//使用getSharedPreferences()方法实例化一个SharedPreferences对象preferences
SharedPreferences preferences = getSharedPreferences("data",MODE_PRIVATE);
//获取数据
String name = preferences.getString("name","");
int mode = preferences.getInt("mode",0);
3. 数据库存储
1. 使用Android内置的SQLite数据库进行数据库存储
使用SQLite数据库需要自定义类继承抽象类SQLiteOpenHelper,在其内部重写抽象方法onCreate和onUpgrade。
- onCreate用于创建并初始化数据库
- onUpgrade升级数据库方法
//SQLiteOpenHelper的构造方法有两种,一般使用这个参数较少的方法
//第一个参数是Context,第二个参数是数据库名称,第三个参数允许我们在查询数据时返回一个自定义的Cursor,一般传入null,第四个参数是当前数据库的版本号,用于进行升级操作
public MyDatabaseHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
重写onCreate方法
//使用execSQL方法执行编写好的SQL语句初始化数据库
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK);
}
重写onUpgrade方法,和重写onCreate的思路一样
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK_1);
}
- 创建SQLite数据库
SQLiteOpenHelper中有两个实例方法可以用于创建或者当数据库已存在时打开数据库,然后这两个方法会返回一个可对数据库进行读写操作的对象。但是当数据库不可写入时,这两个方法会有不同的结果:- getReadableDatabase:返回一个使用只读方式打开的数据库
- getWritableDatabase:出现异常
private MyDatabaseHelper dbHelper;
……
//实例化一个自定义MyDatabaseHelper对象dbHelper,设置参数数据库名为Test.db,版本号为1,然后使用getWritableDatabase创建或打开数据库
dbHelper = new MyDatabaseHelper(this,"BookStore.db",null,1);
dbHelper.getWritableDatabase();
- 升级数据库
使用比原来版本号大的版本重新打开数据库,系统会自动调用onUpgrade方法并执行内部的升级数据库逻辑处理方法升级数据库
dbHelper = new MyDatabaseHelper(this,"BookStore.db",null,2);
- 添加数据
//创建SQLiteDatabase对象获得getWritableDatabase方法返回的数据库操作对象
SQLiteDatabase db = dbHelper.getWritableDatabase();
//使用ContentValues封装数据
ContentValues values = new ContentValues();
values.put("name","The Da Vinci Code");
//插入数据
db.insert("Book",null,values);
- 更新数据
直接调用数据库操作对象的update方法,有四个参数,第一个表名,第二个更新的数据,剩余两个用于限制更新某一行或者某几行,不指定的话就是默认更新所有行
db.update("Book",values,"name = ?",new String[] {"The Da Vinci Code"});
- 删除数据
直接调用数据库操作对象的delete方法,参数类似于更新操作
db.delete("Book","pages>?",new String[]{"500"});
- 查询数据
调用数据库操作对象的query方法,不过这个方法有很多种参数,最少的一个重载方法也有七个参数:
//实例化一个Cursor对象存储查询到的数据
/**
* 查询方法的参数含义:
* 1. 表名
* 2. 查询那几列,不指定默认查询所有列
* 3和4. 查询某一行或者某几行
* 5. 指定需要去group by的列,不指定则不进行group by操作
* 6. 对group by后的数据进行过滤,不指定则不过滤
* 7. 指定查询结果的排序方式,不指定则使用默认排序方式
*/
Cursor cursor = db.query("book",null,null,null,null,null,null);
//将数据的指针移动到第一行
if(cursor.moveToFirst()){
do{
String name = cursor.getString(cursor.getColumnIndex("name"));
}
//读取下一行数据
while (cursor.moveToNext());
}
//关闭cursor对象
cursor.close();
2. 使用开源库LitePal进行数据库存储操作
LitePal开源库:https://github.com/LitePalFramework/LitePal
LitePal是一款开源的Android数据库框架,它采用了对象关系映射(ORM)的模式,并将我们平常开发最常用到的一些数据库功能进行了封装。到项目主页看更详细的开发文档!
使用LitePal:
在build.gradle中声明开源库的引用:
implementation 'org.litepal.android:java:3.0.0'
在AndroidManifest.xml的application中配置:
android:name="org.litepal.LitePalApplication"
在main目录下创建assets目录并在该目录下创建litepal.xml:
<?xml version="1.0" encoding="utf-8"?>
<litepal>
<!--
设置数据库的名称,如果不添加.db后缀的话系统会自动添加
-->
<dbname value="demo" />
<!--
设置数据库版本
-->
<version value="1" />
<!--
使用映射标签在映射模型列表中定义模型,LitePal将为每个映射类创建表。支持的字段模型中定义的内容将映射到列中。
比如:
<list>
<mapping class="com.test.model.Reader" />
<mapping class="com.test.model.Magazine" />
</list>
-->
<list>
</list>
</litepal>
- 创建和升级数据库
和SQLite不同的是,因为LitePal使用的是对象关系映射模式,我们可以使用面向对象的思维来操作数据库,而其中的映射则交给LitePal来处理。
创建一个Book表我们只需要定义一个Book类,继承LitePalSupport是用于在后续的数据处理时调用LitePalSupport中的方法。
public class Book extends LitePalSupport {
//这里表示Book表有几列,比如String name表示一个数据类型为text的name列
//值得一提的是即使类中不声明id,数据库也会自动添加一个id列并将其设置为自增长的主键。
// 而如果设置有id则会直接将id设置为自增长的主键。
private int id;
private String name;
private String author;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
}
然后在litepal.xml中的list标签中使用mapping标签配置新建的Book类。接下来只要进行任意一次数据库的操作,配置好的数据库就会建立出来。
而升级就更简单了,只需要更改Book类或者新建类配置到litepal.xml中,然后将版本号+1,再进行一次任意的数据库操作即可。
- 添加数据
//实例化一个Book表对应的Book对象并设置数据
//然后调用save方法就可以将设置好的数据添加到数据库book表中
Book book = new Book();
book.setName("The Dan Vinci Code");
book.setAuthor("Dan Brown");
book.save();
- 更新数据
更新数据的方法比较多,常用的方法:
//1. 通过find方法获取到已存储的数据然后重新设值
Book book = LitePal.find(Book.class, 1);
book.setName("Lin");
//调用save方法进行更新
book.save();
//2. 实例化一个对象并设值,然后使用update方法更新指定id的数据库对象
Book book = new Book();
book.setName("Lef Thor");
book.update(1);
//3. 使用updateAll方法更新符合指定条件的多条语句
Book book = new Book();
book.setName("Lef Thor");
book.updateAll("author = ?","Dan Brown");
这里需要注意的是,如果想要更新的值是对应数据的数据类型的默认值时,使用以上方法并不会更新数据,这个时候需要调用setToDefault
方法。比如如果想要将一个int类型的数据更新为0时并不能使用update(0)
方法,应当使用setToDefault()
将其更新为默认值即0
- 删除数据
第一种方法类似于更新操作的一种。直接查询到想删除的数据行,然后调用delete方法直接删除。
第二种方法是使用deleteAll方法删除多行数据:
//指定删除符合条件的数据行,或者不指定后缀条件则清空整个数据库的所有数据(删库跑路.gif 2333)
LitePal.deleteAll(Book.class,"pages>?","1000");
- 查询数据
相对的,LitePal的查询操作也是最复杂的,不过相比较SQLite的臃肿,LitePal还是很清爽的。- 通过find查询指定id的数据行,返回一个表对应的自定义类类型数据
- 通过findAll查询指定表的所有数据:
List<Book> bookList = LitePal.findAll(Book.class);
- 通过组合多个查询方法,常用的查询方法:
- select:指定查询那几列,对应SQL语句中的select关键字。比如:select(“name”,“author”);
- where:指定查询的约束条件,对应SQL语句中的where关键字
- order:指定查询结果的排序方式,对应SQL语句中的order by关键字
- limit:指定查询结果的数量
- offset:指定查询结果的偏移量