一 、数据库的创建
事实上,android的SQLite技术主要就是两个东西:
一个是 用create table语句创建出来的那张(些)表 ,里边存着我们需要的数据(先不管它到底是怎样存储数据的),另一个就是 封装了对这张(些)表进行添加(Create)、查询(Retrieve)、更新(Update)和删除(Delete)(这些操作简称为CRUD)等操作的方法的SQLiteDatabase类 ,
因为创建表的语句也是由SQLiteDatabase类的实例来执行的,所以我们先来关注SQLiteDatabase类及其实例的创建.
Android提供了一个名为SQLiteDatabase的类,该类封装了一些操作数据库的API,使用该类可以对数据进行添加(Create)、查询(Retrieve)、更新(Update)和删除(Delete)操作(这些操作简称为CRUD)。
首先看一下SQLiteDatabase类的概要:
-
-
-
-
-
-
-
-
-
-
-
-
-
- public final class SQLiteDatabase extends SQLiteClosable { }
在SQLiteDatabase.java中,我们可以看到构造函数是私有的:
- private SQLiteDatabase(String path, int openFlags, CursorFactory cursorFactory,
- DatabaseErrorHandler errorHandler) {
- mCursorFactory = cursorFactory;
- mErrorHandler = errorHandler != null ? errorHandler : new DefaultDatabaseErrorHandler();
- mConfigurationLocked = new SQLiteDatabaseConfiguration(path, openFlags);
- }
这意味着我们不能直接使用new的方式来创建SQLiteDatabase对象,那么在SQLiteDatabase类中就应该有一个供我们获取它的对象的方法,该方法就是
public static SQLiteDatabase openDatabase(
String pat
h, ... ...)
该方法的注释如下:
-
-
-
-
-
- public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags,
- DatabaseErrorHandler errorHandler) {
- SQLiteDatabase db = new SQLiteDatabase(path, flags, factory, errorHandler);
- db.open();
- return db;
- }
在该方法中通过new SQLiteDatabase获得一个SQLiteDatabase对象并返回,并且在SQLiteDatabase.java中我们可以看到,
public static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory)
public static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory,DatabaseErrorHandler errorHandler)
public static SQLiteDatabase create(CursorFactory factory)
public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags)
这些方法最终都是在调用
public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags,DatabaseErrorHandler errorHandler)方法来创建SQLiteDatabase实例.
而我们使用数据库的常见写法为:
- public class MySQLiteHelper extends SQLiteOpenHelper{
- ... ...
- @Override
- public void onCreate(SQLiteDatabase db) {
- db.execSQL("create table... ...}
-
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- ... ...
- }
- }
- ... ...
- public class myActivity extends Activity {
- ... ...
- myHelper = new MySQLiteHelper(this, "my.db", null, 1);
- SQLiteDatabase db = myHelper.getWritableDatabase();
- db.execSQL("insert ... ...
- }
于是问题就来了,既然我们最终会通过调用openDatabase()方法来获得一个SQLiteDatabase 实例,那么,
1、getWritableDatabase和getReadableDatabase方法最终是不是也会去调用openDatabase方法呢?
2、SQLiteOpenHelper类存在的目的是什么?
直接来看
SQLiteOpenHelper中的getWritableDatabase和getReadableDatabase方法:
- public SQLiteDatabase getWritableDatabase() {
- synchronized (this) {
- return getDatabaseLocked(true);
- }
- }
-
- public SQLiteDatabase getReadableDatabase() {
- synchronized (this) {
- return getDatabaseLocked(false);
- }
- }
两个方法里边都调用了getDatabaseLocked方法,只是传入的参数不同,一个为true,一个为false,该方法大致逻辑为:
- private SQLiteDatabase getDatabaseLocked(boolean writable) {
-
- if (mDatabase != null) {
-
- if (!mDatabase.isOpen()) {
-
-
- mDatabase = null;
- } else if (!writable || !mDatabase.isReadOnly()) {
-
-
- return mDatabase;
- }
- }
-
-
-
- if (mIsInitializing) {
-
- throw new IllegalStateException("getDatabase called recursively");
- }
-
-
- SQLiteDatabase db = mDatabase;
- try {
-
- mIsInitializing = true;
- if (db != null) {
-
- if (writable && db.isReadOnly()) {
-
- db.reopenReadWrite();
- }
- } else if (mName == null) {
-
-
-
-
- db = SQLiteDatabase.create(null);
- } else {
-
- try {
- if (DEBUG_STRICT_READONLY && !writable) {
-
-
- final String path = mContext.getDatabasePath(mName).getPath();
- db = SQLiteDatabase.openDatabase(path, mFactory,
- SQLiteDatabase.OPEN_READONLY, mErrorHandler);
- } else {
-
-
- db = mContext.openOrCreateDatabase(mName, mEnableWriteAheadLogging ?
- Context.MODE_ENABLE_WRITE_AHEAD_LOGGING : 0,
- mFactory, mErrorHandler);
- }
- } catch (SQLiteException ex) {
- if (writable) {
- throw ex;
- }
- Log.e(TAG, "Couldn't open " + mName
- + " for writing (will try read-only):", ex);
- final String path = mContext.getDatabasePath(mName).getPath();
- db = SQLiteDatabase.openDatabase(path, mFactory,
- SQLiteDatabase.OPEN_READONLY, mErrorHandler);
- }
- }
-
-
- onConfigure(db);
-
-
-
-
- final int version = db.getVersion();
- if (version != mNewVersion) {
-
-
-
- if (db.isReadOnly()) {
-
- throw new SQLiteException("Can't upgrade read-only database from version " +
- db.getVersion() + " to " + mNewVersion + ": " + mName);
- }
-
- db.beginTransaction();
- try {
- if (version == 0) {
-
-
-
- onCreate(db);
-
-
- } else {
-
- if (version > mNewVersion) {
-
- onDowngrade(db, version, mNewVersion);
- } else {
-
- onUpgrade(db, version, mNewVersion);
- }
- }
-
- db.setVersion(mNewVersion);
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- }
- }
-
- onOpen(db);
-
- if (db.isReadOnly()) {
- Log.w(TAG, "Opened " + mName + " in read-only mode");
- }
-
-
-
- mDatabase = db;
-
- return db;
- } finally {
-
- mIsInitializing = false;
- if (db != null && db != mDatabase) {
- db.close();
- }
- }
- }
上边第 91 行代码onCreate()方法的定义及注释
:
-
-
-
-
-
-
-
- public abstract void onCreate(SQLiteDatabase db);
在分析了SQLiteOpenHelper中的getWritableDatabase和getReadableDatabase方法的大致逻辑之后.我们在上文中提出的两个问题也就好回答了.
1、SQLiteOpenHelper类中的getWritableDatabase和getReadableDatabase方法最终会调用SQLiteDatabase类中的openDatabase方法
2、SQLiteOpenHelper类存在的目的主要是以下两个方面:
A、对SQLiteDatabase类中的openDatabase方法进行封装,优化SQLiteDatabase的实例的创建
现在来看一下SQLiteOpenHelper中的getWritableDatabase和getReadableDatabase方法的注释吧:
getWritableDatabase():
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- public SQLiteDatabase getWritableDatabase() {
- synchronized (this) {
- return getDatabaseLocked(true);
- }
- }
getReadableDatabase():
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- public SQLiteDatabase getReadableDatabase() {
- synchronized (this) {
- return getDatabaseLocked(false);
- }
- }
结合它们的注释和上边对getDatabaseLocked方法逻辑的分析,将会更有利于我们理解SQLiteOpenHelper对SQLiteDatabase的实例创建的优化
B、方便对数据库进行升级和降级
在开发中,数据库升级和降级是比较常见的操作,如果没有SQLiteOpenHelper,我们应该也会采用和SQLiteOpenHelper类似的方法用一个标示来判断是否要对数据库进行升级或降级,而SQLiteOpenHelper已经为我们做了这个工作.(参考getDatabaseLocked方法中的分析),让我们能够更加容易的管理数据库的版本。
再来看一下SQLiteOpenHelper类的注释:
-
-
-
-
-
-
-
-
-
-
-
- public abstract class SQLiteOpenHelper { }
到此为止,SQLiteOpenHelper类的注释中的 "A helper class to manage database creation and version management."这句话也就好理解了
还需要关注一下SQLiteOpenHelper的构造函数:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) {
- this(context, name, factory, version, null);
- }
这个构造函数内部调用了下边的构造函数:
-
-
-
-
-
-
- public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version,
- DatabaseErrorHandler errorHandler) {
- if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version);
-
- mContext = context;
- mName = name;
- mFactory = factory;
- mNewVersion = version;
- mErrorHandler = errorHandler;
- }
这两个构造函数的注释再结合上边对getDatabaseLocked方法逻辑的分析,我们就更容易理解在创建SQLiteOpenHelper的子类对象时传入的name和version参数了.
以上我们对
SQLiteOpenHelper在 SQLiteDatabase的实例创建上的优化
和 对数据库表的升级和降级功能的封装 两方面做了详细的分析,下边我们来看SQLiteDatabase类定义的方法的使用,即数据库的操作.
二、数据库的操作
关于数据库的使用,有不少好文章可以参考,暂不详细写了
ContentValues类的注释:
-
-
-
-
-
- public final class ContentValues implements Parcelable { }
查看ContentValues.java文件,可以发现,该类就一个主要的成员变量:private HashMap<String, Object> mValues;在ContentValues的各个构造函数中,主要的操作就是根据不同的需求对mValues进行初始化,并且在该类中定义了诸如put(String key, String value)和getAsString(String key)等方法:
put类方法,以put(String key, String value)为例:
-
-
-
-
-
-
- public void put(String key, String value) {
- mValues.put(key, value);
- }
get类方法,以getAsString(String key)为例:
-
-
-
-
-
-
- public String getAsString(String key) {
- Object value = mValues.get(key);
- return value != null ? value.toString() : null;
- }
另外,还有public int size()、public boolean containsKey(String key)、public void clear()、public Set<String> keySet()等,可以认为ContentValues类就是对HashMap的一个封装。
三、数据库的升级
未完待续 ... ...