赞
踩
要记录高分或者想保存微博,应用就需要存储数据。
在Android世界里,通常可以把数据安全地存放在SQLite数据库中。在这一章中,我们会展示如何创建数据库、如何在数据库中增加表,以及如何预填充数据,所有这些都会在友好的SQLite帮助器的帮助下完成。接下来你会看到如何妥善地完成数据库结构的升级,以及如何在需要撤销修改时实现降级。
Starbuzz应用从一个Drink类获得它的饮品数据,这个类包含了Starbuzz提供的一些饮品。在构建这个应用的第一个版本时,这样做会比较容易,不过还有一种更好的方法存放和持久存储数据。
在接下来的两章中,我们将修改Starbuzz应用,让它从SQLite数据库获取数据。这一章中我们会了解如何创建这个数据库,下一章将介绍如何将活动与这个数据库连接。
所有应用都需要存储数据,Andorid世界中存储数据的主要方法就是使用SQLlite数据库。
1.它是轻量级的。
大多数数据库系统都需要一个特殊的数据库服务器进程才能工作。SQLite并不需要这样一个服务器进程,SQLite数据库实际上就是一个文件。如果没有用到数据库,就不会占用任何处理器时间。这对于移动设备尤其重要,因为我们肯定不希望太多地消耗电池电量。
2.针对单个用户优化。
只是我们的应用要与数据库交互,所以不必用用户名和口令来标识我们的身份。
3.它很稳定且速度很快。
SQLite数据库极其稳定,可以处理数据库事务,这说明如果你在更新多个数据,突然出现故障,SQLite可以回滚数据。另外,读写数据的代码是用优化的C语言编写的。所以不仅速度很快,而且还可以减少所需的处理器功率消耗。
Android会自动为各个应用创建一个文件夹来存储应用的数据库。为Starbuzz应用创建数据库时,它会存储在下面这个文件夹中:
应用可以在这个文件夹中存储多个数据库。每个数据库由两个文件组成。
第一个文件是数据库文件,与数据库同名,例如,这个文件就名为“starbuzz”。这是主SQLite数据库文件。所有数据都存放在这个文件中。
第二个文件是journal文件。它也与数据库同名,另外有一个“-journal”后缀,例如,这个文件可以是“starbuzz-journal”。这个journal文件包含对数据库的所有修改。如果出现问题,Android会使用这个journal文件撤销(或回滚)最近的修改。
Android使用了一组类,可以利用这些类来管理SQLite数据库。这个工作主要由3类对象来完成:
1.SQLite帮助器
可以扩展SQLIteOpenHelper类创建一个SQLite帮助器。利用这个帮助器可以创建和管理数据库
2.SQLite数据库
可以利用SQLiteDatabase类访问数据库。这就像JDBC中的SQLConnection。
3.游标
可以利用Cursor读写数据库。这就像JDBC中的ResultSet.
我们将使用这些对象来展示如何创建一个SQLite数据库,你的应用可以使用这个数据库持久存储数据,为此只需要把Drink类替换为一个SQLite数据库。
问:如果数据库没有用户名和密码,怎么能保证它安全呢?
答:存储应用数据库的目录只对这个应用可读。可以保证这个数据库在操作系统级是安全的。
问:数据库目录在应用的目录中吗?
答:不,这个数据库与应用代码放在不同的目录中。采用这种做法,可以重写应用得到一个更新的版本,而数据库中的数据仍是安全的,不会被破坏。
来回顾当前的Starbuzz的应用结构:
1.TopLevelAetivity包含一个选项列表,其中包括Drinks、Food和Stores。
2.用户单击Drinks选项时,会启动DrinkCategoryActivity。
这个活动会显示一个饮品列表,这些饮品是从Java Drink类得到的。
3.用户单击一个饮品时,将在DrinkAetivity中显示它的洋细信息。
DrinkActivity从Java Drink类得到这个饮品的详细信息。
这个应用目前从Drink类获取饮品数据。
我们将使用一个SQLite帮助器创建可以在Starbuzz应用中使用的一个sQLite数据库。下面把我们的Drink Java类替换为一个数据库,所以需要这个SQLite帮助器完成下面的工作:
1.创建数据库
在做其他工作之前,首先要让SQLite帮助器创建Starbuzz数据库的第一个版本(版本1)。
2.创建Drink表,并填充饮品信息。
有了数据库之后,可以在这个数据库中创建一个表。表结构需要反映当前Drink类的属性,所以应当能存储各个饮品的名字、描述和图像资源ID。然后再在这个表中增加3个饮品。
这个应用的结构与以前基本相同,只不过这里要把文件Drink.java替换为一个SQLite帮助器和一个SQLite Starbuzz数据库。SQLite帮助器将维护这个Starbuzz数据库,并允许其他活动访问这个数据库。我们将在下一章修改活动来使用数据库。
SQLiteOpenHelper类可以帮助创建并维护SQLite数据库。可以将它想成是一个个人助理,专门负责处理日常的数据库维护工作。
SQLiteHelper可以帮助完成的典型任务:
1.创建数据库
第一次安装一个应用时,数据库文件并不存在。SQLite帮助器会确保正确地创建这个数据库文件,要有正确的文件名,而且要安装正确的表结构。
2.访问数据库
我们的应用不需要了解数据库文件位置的所有详细信息,所以SQLite帮助器可以提供一个易于使用的数据库对象,这样我们需要时就可以利用这个数据库对象访问数据库,这可以是任何时间,不论白天黑夜。
3.保持数据库井然有序
数据库结构可能会随时间改变,可以依赖SQLite帮助器将一个老版本的数据库转换为一个闪亮的新版本,包含它需要的所有最新数据库结构。
可以写一个类扩展SQLiteOpenHelper类来创建SQLite帮助器。为此,必须覆盖onCreate()和onUpgrade ()方法。必须要有这两个方法。
第一次在设备上创建数据库时会调用onCreate()方法。这个方法应当包含创建应用所需数据库表的必要代码。
数据库需要升级时会调用onUpgrade()方法。举例来说,如果需要在发布数据库之后对数据库中的表做些修改,就要在这个方法中完成。
创建一个类StarbuzzDatabaseHelper来实现SQLite帮助器:
- public class StarbuzzDatabaseHelper extends SQLiteOpenHelper {
-
- public StarbuzzDatabaseHelper(@Nullable Context context,
- @Nullable String name,
- @Nullable SQLiteDatabase.CursorFactory factory,
- int version) {
- super(context, name, factory, version);
- }
-
- @Override
- public void onCreate(SQLiteDatabase db) {
-
- }
-
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-
- }
- }
为了让SQLite帮助器做些具体工作,我们要在它的方法中增加必要的代码。
首先要告诉SQLite帮助器需要创建什么数据库。
要创建数据库,SQLite帮助器需要两个信息。
首先,我们需要为数据库指定一个名字。通过为数据库指定一个名字,可以确保数据库关闭时它仍在设备上。如果没有指定数据库名,就只会在内存中创建这个数据库,所以一旦数据库关闭,它就会消失。
需要提供的第二个信息是数据库版本。数据库版本必须是一个整数值,从1开始。SQLite帮助器使用这个版本号来确定数据库是否需要升级。
指定数据库名和版本时,要把它们传递到sQLiteOpenHelper超类的构造函数。我们指定这个数据库名为“starbuzz”,因为这是这个数据库的第一个版本,所以版本号为1。下面给出我们需要的代码(用下面的代码更新你的StarbuzzDatabaseHelper.java) :
构造函数指定了数据库的详细信息,不过此时还没有创建数据库。SQLite帮助器会等到应用需要访问数据库时才会真正创建数据库。
一旦告诉SQLite帮助器要创建什么数据库,下面可以指定数据库表。
SQLite数据库中的数据都存储在数据库表中。表中包含多行,每一行分为多列。一列包含一个数据。
对于需要记录的数据,要为每一部分不同的数据创建一个表。
比如:
有些列可以识别为主键。主键可以唯一地标识一行。如果说某一列是一个主键,那么数据库中不允许存储这一列有重复的行(也就是说,不允许有重复的键)。
建议数据库表都有一个整数主键列_id。这是因为Android代码要求有一个数值_id列,所以不要因为有一个不合适的键以后带来问题。
这是Android的一个约定,通常会称主键列为_id。Android化码希望你的数据里有一个_id列。如果忽视这个约定,从数据库获取数据以及在用户界面中显示数据时可能会比较困难。
存储类和数据类型:
表中的每一列都设置为存储某个特定类型的数据。下面是SQLite中可以使用的主要数据类型,以及可以存储哪些数据:
与大多数数据库系统不同,SQLite中不需要指定列的大小。在底层,数据类型会转换为一个更宽的存储类。这说明,可以很宽泛地说你想要存储哪种类型的数据,而不要求你特别指出数据的大小。
使用结构化查询语言(SQL)创建表:
与SQLite交互的各个应用需要使用一个标准数据库语言,成为结构化查询语言(SQL).几乎每种类型的数据库都使用SQL。如果希望创建DRINK表,就要利用SQL完成。
下面是创建表的SQL命令:
CREATE TABLE 指令指出表中希望有哪些列,以及各列的数据类型。_id是这个表的主键,特殊关键字AUTOINCREMENT表示在表中存储一个新行时,SQLite会自动为它生成一个唯一的整数。
SQLite帮助器负责在第一次需要使用数据库时创建这个SQLite数据库。首先,在设备上创建一个空的数据库,然后调用SQLite帮助器的onCreate()方法。
向onCreate()方法传入一个SQLiteDatabase对象作为参数。可以使用这个参数用下面的方法运行SQL命令:
下面是SQLite帮助器的onCreate()代码:
- @Override
- public void onCreate(SQLiteDatabase db) {
- db.execSQL("CREATE TABLE DRINK ("
- +"_id INTEGER PRIMARY KEY AUTOINCREMENT,"
- +"NAME TEXT,"
- +"DESCRIPTION TEXT,"
- +"IMAGE_RESOURCE_ID INTEGET);"
- );
- }
如果需要在一个SQLite表中预填充数据,可以使用SQLiteDatabase insert()方法。这个方法允许你在数据库中插入数据,一旦插入完成,会返回所插入记录的ID。如果这个方法不能插入这个记录,则返回值-1。
要使用insert()方法,需要指定想要将数据插入哪个表,以及所插入的值。指定想要插入的值时,需要创建一个contentvalues对象。Contentvalues对象用来保存一个名/值数据对:
要用contentvalues对象的put()方法为这个对象增加名/值数据对。
我们想要用它在DRINK表中插入一个数据行,所以我们会利用DRINK表中各列的列名以及我们希望各个字段中存放的值来填充这个数据行:
最后,我们要用sQLiteDatabase insert()方法在DRINK表中插入值:
insert()方法的一般形式如下:
nullColumnHack String值是可选的,大多数情况下,你可能希望像上面的代码中一样,把它设置为null。之所以有这个值,是因为contentvalues对象可以为空,你可能希望在表中插入一个空行。SQLite不允许插入空行,除非至少指定了一个列的列名;利用nullColumnHack参数,就可以指定其中一个列。
可以使用SQLiteDatabase update()方法更新SQLite中现有的记录。
这个方法允许你更新数据库中的记录,并返回所更新的记录数。要使用update()方法,需要指定想要更新哪个表中的记录,希望更新的值,以及在什么条件下更新。这个方法如下所示:
例如,下面来看如何在饮料名为“Latte”时将DESCRIPTION列的值改为"Tasty":
第1个参数:想要更新的表名(在这里就是DRINK表)。
第2个参数:指定想要更新哪些值。就像insert()方法中一样,指定希望更新的值时,要创建一个contentvalues对象保存名/值数据对
第3个参数:指定更新记录的条件。在上面的例子中,"NAME=?"表示NAME列应当等于某个值。?符号是表示这个值的一个占位符。这个查询使用最后一个参数的内容来指定这个值是什么(在这里就是“Latte”) 。
多个条件:如果想要对查询应用多个条件,需要确保按指定值相同的顺序来指定条件。
例如,下面会展示如何在饮品名为“Latte”或者饮品描述是“Ourbest drip coffee”时更新DRINK表中的记录:
条件值必须是String,即使应用这个条件的列并不包含String。如果是这样,则需要将值转换为String。
例如,下面会在_id为1时返回DRINK记录:
SQLiteDatabase delete()方法与前面看到的update()方法工作类似。它有以下形式:
代码:
- public class StarbuzzDatabaseHelper extends SQLiteOpenHelper {
-
- private static final String DB_NAME="strabuzz";
- private static final int DB_VERSION=1;
-
- public StarbuzzDatabaseHelper(@Nullable Context context) {
- super(context, DB_NAME, null, DB_VERSION);
- }
-
- @Override
- public void onCreate(SQLiteDatabase db) {
- db.execSQL("CREATE TABLE DRINK ("
- +"_id INTEGER PRIMARY KEY AUTOINCREMENT,"
- +"NAME TEXT,"
- +"DESCRIPTION TEXT,"
- +"IMAGE_RESOURCE_ID INTEGET);"
- );
- //将各个饮品分别插入一个单独的表行。
- insertDrink(db, "Latte", "Espresso and steamed milk", R.drawable.latte);
- insertDrink(db, "Cappuccino", "Espresso, hot milk and steamed-milk foam",
- R.drawable.cappuccino);
- insertDrink(db, "Filter", "Our best drip coffee", R.drawable.filter);
-
- }
-
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-
- }
-
- /**
- * 需要插入多个音频,因此创建insertDrink来完成这个工作
- */
- private static void insertDrink(SQLiteDatabase db, String name,
- String description, int resourceId) {
- ContentValues drinkValues = new ContentValues();
- drinkValues.put("NAME", name);
- drinkValues.put("DESCRIPTION", description);
- drinkValues.put("IMAGE_RESOURCE_ID", resourceId);
- db.insert("DRINK", null, drinkValues);
- }
- }
1.用户安装应用并启动应用
应用需要访问数据库时,SQLite帮助器会查看数据库是否已经存在
2.如果数据库不存在,则会创建数据库
数据库的名字和版本号在SQLite帮助器中指定
3.创建数据库时,会调用SQLite帮助器中的onCreate()方法
这会为数据库增加一个DRINK表,并在这个表中填充记录。
需要对已有的数据库进行修改。
需要修改一个应用的数据库时,你要处理两个最重要的情况。
SQLite帮助器如何区分数据库是否过期?
SQLite数据库有一个版本号
SQLite帮助器通过查看版本号可以区分这个SQLite数据库是否需要更新。通过在SQLiteOpenHelper超类的构造函数中传入版本号,可以在SQLite帮助器中指定数据库的版本。
SQLite数据库支持版本号,SQLite帮助器可以使用这个版本号,另外还支持一个内部模式版本:对数据库模式做任何修改时,如表结构有变化,数据库就会将模式版本增1。你无法控制这个值,它只能由SQLite在内部使用。
创建数据库时,会在SQLite帮助器中设置它的版本号,并调用SQLite帮助器的onCreate()方法。
想要更新数据库时,可以改变SQLite帮助器代码中的版本号:
大多数情况下,你都希望对数据升级,所以会指定一个更大的数。这是因为,通常只是在需要撤销之前升级版本中所做的修改时才会对数据库降级。
用户在其设备上安装应用的最新版本时,如果应用第一次需要使用数据库,SQLite帮助器会检查它的版本号,与设备上的数据库版本比较。如果SQLite帮助器代码中的版本号高于当前数据库,则调用SQLite帮助器的onUpgrade()方法。如果SQLite帮助器代码中的版本号低于当前数据库,则调用onDowngrade()方法。
一旦调用这两个方法中的任意一个方法,就会改变数据库的版本号,使之与SQLite帮助器中的版本号一致。
在发布应用的一个新版本时,如果将SQLite帮助器版本号从1改为2,会发生以下情况:
1.用户安装了应用的新版本,并运行应用。
2.如果这是用户第一次安装这个应用,数据库还不存在,那么SQLite帮助器会创建数据库。SQLite帮助器为数据库提供SQLite帮助器代码中指定的数据库名和版本号。
3.创建数据库时,会调用SQLite帮助器中的onCreate()方法。这个onCreate()方法包含填充数据库的代码
4.如累用户安装过应用的前一个版本,并且访问过数据库,那么数据库已经存在。如果数据库已经存在,SQLite帮助器不会重建数据库。
5.SQLite帮助器检查数据库的版本号,并与SQLite帮助器代码中的版本号比较。如果SQLite帮助器版本号高于当前数据库版本,则调用onUpgrade()方法。如果SQLite帮助器版本号低于当前数据库版本,将调用onDowngrade()方法。然后修改数据库版本号,使之与SQLite帮助器代码中的版本号一致。
SQLite帮助器要根据数据库是否已经存在以及主数据库的版本号来决定如何做。
onUpgrade()方法有三个参数:
可以使用版本号应用连续的更新:
使用这个方法意味着可以确保用户的数据库得到所需的全部修改,而不论用户之前安装的是哪一个版本。
onDowngrade()方法没有onUpgrade()方法那么常用,因为这个方法用来将数据库回退到之前的一个版本。如果你发布了应用的一个版本,其中对数据库有些改变,不过后来发现其中存在bug,在这种情况下,这个方法就很有用。onDowngrade()方法允许你取消这些改变,将数据库设置回原来的版本。
onDowngrade()方法也有3个参数:
与onUpgrade()方法一样,可以使用版本号撤销某个特定版本的改变。例如,如果需要当数据库版本号为3时对数据库做些修改,可以使用以下代码:
1.修改数据库记录。
这一章前面,你已经看到如何使用sQLiteDatabase insert(),up-date()和delete()方法在数据库中插入、更新或删除记录。升级数据库时可以增加更多记录,或者修改或删除原有的记录。
2.修改数据库结构。
你已经看到如何在数据库中创建表。可能还希望为现有的表增加列、对数据库表重命名,或者完全删除表。
增加新列:
重命名表:
删除表:
可以使用 SQLiteDatabase execSQL()方法执行SQL指令:
假设需要升级数据库,为DRINK表增加一个新列。由于我们希望所有新用户和现有的用户都能得到这个改变,所以需要确保oncreate ()和onUpgrade()方法都包含这个改变。oncreate()方法会确保所有新用户得到这个新列,onUpgrade()方法则确保所有现有的用户也能得到这个新列。
并不是在onCreate()和onUpgrade()方法中放入类似的代码,我们会创建一个单独的updateMyDatabase()方法,这个方法将由onCreate()和onUpgrade()方法调用。我们把onCreate()方法中现有的代码移到这个新的updateMyDatabase()方法中,而且会增加额外的代码创建新增的列。使用这种方法意味着可以将你的数据库代码都放在一处,这样可以更容易地跟踪每次更新数据库时做了哪些改变:
- package com.hfad.starbuzz;
-
- import android.content.ContentValues;
- import android.content.Context;
- import android.database.sqlite.SQLiteDatabase;
- import android.database.sqlite.SQLiteOpenHelper;
-
- import androidx.annotation.Nullable;
-
- public class StarbuzzDatabaseHelper extends SQLiteOpenHelper {
-
- private static final String DB_NAME="strabuzz";
- private static final int DB_VERSION=2;
-
- public StarbuzzDatabaseHelper(@Nullable Context context) {
- super(context, DB_NAME, null, DB_VERSION);
- }
-
- @Override
- public void onCreate(SQLiteDatabase db) {
- updateMyDatabase(db, 0, DB_VERSION);
- }
-
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- updateMyDatabase(db, oldVersion, newVersion);
- }
-
- private void updateMyDatabase(SQLiteDatabase db, int oldVersion, int newVersion) {
- if (oldVersion < 1) {
- db.execSQL("CREATE TABLE DRINK (_id INTEGER PRIMARY KEY AUTOINCREMENT, "
- + "NAME TEXT, "
- + "DESCRIPTION TEXT, "
- + "IMAGE_RESOURCE_ID INTEGER);");
- insertDrink(db, "Latte", "Espresso and steamed milk", R.drawable.latte);
- insertDrink(db, "Cappuccino", "Espresso, hot milk and steamed-milk foam",
- R.drawable.cappuccino);
- }
- insertDrink(db, "Filter", "Our best drip coffee", R.drawable.filter);
- if (oldVersion < 2) {
- //为DRINK表这家一个数值FAVORTITE列
- db.execSQL("ALTER TABLE DRINK ADD COLUMN FAVORITE NUMERIC;");
- }
- }
-
- /**
- * 需要插入多个饮品,因此创建insertDrink来完成这个工作
- */
- private static void insertDrink(SQLiteDatabase db, String name,
- String description, int resourceId) {
- ContentValues drinkValues = new ContentValues();
- drinkValues.put("NAME", name);
- drinkValues.put("DESCRIPTION", description);
- drinkValues.put("IMAGE_RESOURCE_ID", resourceId);
- db.insert("DRINK", null, drinkValues);
- }
- }
1.第一次需要访问数据库时,SQLite帮助器会检查这个数据库是否已经存在。
2.如果这个数据库不存在,SQLite帮助器会创建数据库,并运行它的onCreate()方法。
我们的onCreate()方法代码会调用updateMyDatabase()方法。这会创建DRINK表(包括新增的列),并在表中填充记录。
3.如果这个数据库已经存在,SQLHte帮助器会检查数据库的版本号,并与SQLite帮助器代码中的版本号比较。
如果SQLite帮助器版本号高于数据库版本号,则会调用onUpgrade()方法。如果SQLite帮助器版本号低于数据库版本号,会调用onDowngrade()方法。我们的SQLite帮助器版本号高于数据库的版本号,所以会调用onUpgrade()方法。它会调用updateMyDatabase()方法,这将为DRINK表增加一个新的列FAVORITE。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。