SQLite 是一款轻量级的关系型数据库,它的运算速度非常快, 占用资源很少,通常只需要几百 K 的内存就足够了,因而特别适合在移动设备上使用。SQLite 不仅支持标准的 SQL 语法,还遵循了数据库的 ACID 事务,所以只要你以前使用过其他的 关系型数据库,就可以很快地上手 SQLite。而 SQLite 又比一般的数据库要简单得多,它甚 至不用设置用户名和密码就可以使用。Android 正是把这个功能极为强大的数据库嵌入到了 系统当中,使得本地持久化的功能有了一次质的飞跃。
前面我们所学的文件存储和 SharedPreferences 存储毕竟只适用于去保存一些简单的数据 和键值对,当需要存储大量复杂的关系型数据的时候,你就会发现以上两种存储方式很难应 付得了。比如我们手机的短信程序中可能会有很多个会话,每个会话中又包含了很多条信息 内容,并且大部分会话还可能各自对应了电话簿中的某个联系人。很难想象如何用文件或者 SharedPreferences 来存储这些数据量大、结构性复杂的数据吧?但是使用数据库就可以做得 到。那么我们就赶快来看一看,Android 中的 SQLite 数据库到底是如何使用的。
创建数据库
Android 为了让我们能够更加方便地管理数据库,专门提供了一个 SQLiteOpenHelper 帮 助类,借助这个类就可以非常简单地对数据库进行创建和升级。既然有好东西可以直接使用, 那我们自然要尝试一下了,下面我就将对 SQLiteOpenHelper 的基本用法进行介绍。
首先你要知道 SQLiteOpenHelper 是一个抽象类,这意味着如果我们想要使用它的话, 就需要创建一个自己的帮助类去继承它。SQLiteOpenHelper 中有两个抽象方法,分别是 onCreate()和 onUpgrade(),我们必须在自己的帮助类里面重写这两个方法,然后分别在这两 个方法中去实现创建、升级数据库的逻辑。
SQLiteOpenHelper 中 还 有 两 个 非 常 重 要 的 实 例 方 法 , getReadableDatabase() 和 getWritableDatabase()。这两个方法都可以创建或打开一个现有的数据库(如果数据库已存在 则直接打开,否则创建一个新的数据库),并返回一个可对数据库进行读写操作的对象。不 同的是,当数据库不可写入的时候(如磁盘空间已满)getReadableDatabase()方法返回的对 象将以只读的方式去打开数据库,而 getWritableDatabase()方法则将出现异常。
SQLiteOpenHelper 中有两个构造方法可供重写,一般使用参数少一点的那个构造方法即 可。这个构造方法中接收四个参数,第一个参数是 Context,这个没什么好说的,必须要有 它才能对数据库进行操作。第二个参数是数据库名,创建数据库时使用的就是这里指定的名 称。第三个参数允许我们在查询数据的时候返回一个自定义的 Cursor,一般都是传入 null。 第 四 个 参 表 示 当 前 数 据 库 的 版 本 号 , 可 用 于 对 数 据 库 进 行 升 级 操 作 。 构 建 出 SQLiteOpenHelper 的实例之后,再调用它的 getReadableDatabase()或 getWritableDatabase()方法就能够创建数据库了,数据库文件会存放在/data/data/<package name>/databases/目录下。
此时,重写的 onCreate()方法也会得到执行,所以通常会在这里去处理一些创建表的逻辑。
接下来还是让我们通过例子的方式来更加直观地体会 SQLiteOpenHelper 的用法吧,首先新建一个 DatabaseTest 项目。 这里我们希望创建一个名为 BookStore.db 的数据库,然后在这个数据库中新建一张 Book表,表中有 id(主键)、作者、价格、页数和书名等列。创建数据库表当然还是需要用建表 语句的,这里也是要考验一下你的 SQL 基本功了,Book 表的建表语句如下所示:
create table Book (
id integer primary key autoincrement, author text,
price real, pages integer, name text)
只要你对 SQL 方面的知识稍微有一些了解,上面的建表语句对你来说应该都不难吧。SQLite 不像其他的数据库拥有众多繁杂的数据类型,它的数据类型很简单,integer 表示整型,real 表示浮点型,text 表示文本类型,blob 表示二进制类型。另外,上述建表语句中我们还使用了 primary key 将 id 列设为主键,并用 autoincrement 关键字表示 id 列是自增长的。 然后需要在代码中去执行这条 SQL 语句,才能完成创建表的操作。新建 MyDatabaseHelper类继承自 SQLiteOpenHelper,代码如下所示:
public class MyDatabaseHelper extends SQLiteOpenHelper {
public static final String CREATE_BOOK = "create table book ("
+ "id integer primary key autoincrement, "
+ "author text, "
+ "price real, "
+ "pages integer, "
+ "name text)";
private Context mContext;
public MyDatabaseHelper(Context context, String name, 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) {
}
}
可以看到,我们把建表语句定义成了一个字符串常量,然后在 onCreate()方法中又调用 了 SQLiteDatabase 的 execSQL()方法去执行这条建表语句,并弹出一个 Toast 提示创建成功, 这样就可以保证在数据库创建完成的同时还能成功创建 Book 表。
现在修改 activity_main.xml 中的代码,如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="vertical" >
<Button android:id="@+id/create_database" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Create database"/>
</LinearLayout>
布局文件很简单,就是加入了一个按钮,用于创建数据库。最后修改 MainActivity 中的 代码,如下所示:
public class MainActivity extends Activity {
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 OnClickListener() {
@Override
public void onClick(View v) {
dbHelper.getWritableDatabase();
}
});
}
}
这里我们在 onCreate()方法中构建了一个 MyDatabaseHelper 对象,并且通过构造函数的 参数将数据库名指定为 BookStore.db,版本号指定为 1,然后在 Create database 按钮的点击 事件里调用了 getWritableDatabase()方法。这样当第一次点击 Create database 按钮时,就会检测 到当前程序中并没有 BookStore.db 这个数据库,于是会创建该数据库并调用 MyDatabaseHelper中的 onCreate()方法,这样 Book 表也就得到了创建,然后会弹出一个 Toast 提示创建成功。
再次点击 Create database 按钮时,会发现此时已经存在 BookStore.db 数据库了,因此不会再 创建一次。
现在就可以运行一下代码了,在程序主界面点击 Create database 按钮,结果如图 6.11 所示。
图 6.11
此时 BookStore.db 数据库和 Book 表应该都已经创建成功了,因为当你再次点击 Create database 按钮时不会再有 Toast 弹出。可是又回到了之前的那个老问题,怎样才能证实它们 的确是创建成功了?如果还是使用 File Explorer,那么最多你只能看到 databases 目录下出现 了一个 BookStore.db 文件,Book 表是无法通过 File Explorer 看到的。因此这次我们准备换一 种查看方式,使用 adb shell 来对数据库和表的创建情况进行检查。
adb 是 Android SDK 中自带的一个调试工具,使用这个工具可以直接对连接在电脑上的 手机或模拟器进行调试操作。它存放在 sdk 的 platform-tools 目录下,如果想要在命令行中使 用这个工具,就需要先把它的路径配置到环境变量里。
如果你使用的是 Windows 系统,可以右击我的电脑→属性→高级→环境变量,然后在 系统变量里找到 Path 并点击编辑,将 platform-tools 目录配置进去,如图 6.12 所示。
图 6.12
如果你使用的是 Linux 系统,可以在 home 路径下编辑.bash_profile 文件,将 platform-tools目录配置进去即可,如图 6.13 所示:
图 6.13
配置好了环境变量之后,就可以使用 adb 工具了。打开命令行界面,输入 adb shell,就 会进入到设备的控制台,如图 6.14 所示。
图 6.14
然后使用 cd 命令进行到/data/data/com.example.databasetest/databases/目录下,并使用 ls命令查看到该目录里的文件,如图 6.15 所示。
图 6.15
这个目录下出现了两个数据库文件,一个正是我们创建的 BookStore.db ,而另一个 BookStore.db-journal 则是为了让数据库能够支持事务而产生的临时日志文件,通常情况下这 个文件的大小都是 0 字节。
接下来我们就要借助 sqlite 命令来打开数据库了,只需要键入 sqlite3,后面加上数据库 名即可,如图 6.16 所示。
图 6.16
这时就已经打开了 BookStore.db 数据库,现在就可以对这个数据库中的表进行管理了。 首先来看一下目前数据库中有哪些表,键入.table 命令,如图 6.17 所示。
图 6.17
可以看到,此时数据库中有两张表,android_metadata 表是每个数据库中都会自动生成的,不用管它,而另外一张 Book 表就是我们在 MyDatabaseHelper 中创建的了。这里还可以通过.schema 命令来查看它们的建表语句,如图 6.18 所示。
图 6.18
由此证明,BookStore.db 数据库和 Book 表确实已经是创建成功了。之后键入.exit 或.quit命令可以退出数据库的编辑,再键入 exit 命令就可以退出设备控制台了。