Android数据存储访问方式-SQLite,ContentProvider的应用

SQLite的简单介绍
SQLite是一个开源的,小型的嵌入式数据库,小而巧的特点特别适用于移动设备的开发中.
SQLite数据库的最大特点是"弱类型",即虽然在创建表的时候指定了列的数据类型,但在插入数据时不一定就使用指定了数据类型,当插入数据的数据类型与指定的不匹配,SQLite会尝试进行转换,如果不能转换则直接按本身类型存储.
另外,SQLite不支持一些标准SQL功能,如外键约束(foreign key),嵌套(transaction),右联接(right out join),全联接(full out join)


SQLiteOpenHelper
Android提供了SQLiteOpenHelper类,用于帮助管理数据库,例如创建或更新一个数据库(实际上是使用SQLiteDatabase的函数来执行sql语句).该类是抽象类,我们需要自己继承,重写onCreate()和onUpgrade()函数.一个简单的SQLiteOpenHelper继承类如下:
class DatabaseHelper extends SQLiteOpenHelper{
    DatabaseHelper(Context ctx){
            super(context, databasename, cursorFactory, databaseversion);
    }
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("create table ............");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("drop table is exists ...");
        onCreate(db);
    }
}

SQLiteOpenHelper中提供了两个函数来获得SQLiteDatabase对象,分别是getReadableDatabase()和getWritableDatabase()函数,这两个函数都是线程安全的,能返回可读/写的数据库对象,而它们的区别在于:
1.当存储空间(disk)满时,getReadableDatabase()会返回一个可读数据库对象,而getWritableDatabase()则会失败.知道存储空间满的问题被解决了.
2.假如现在已经有一个数据库对象存在并已经是打开状态,那getReadableDatabase()会返回这个已经存在的数据库对象,而对于getWritableDatabase()它还会判断该对象是read-only还是read/write的,如果是read/write的,则直接返回该数据库对象,如果是read-only的,则会把该数据库对象加锁,并尝试创建一个新的数据库对象,如果新的数据库对象创建成功,则返回新数据库对象,把旧数据库对象关闭、解锁、指向新数据库对象,如果创建失败则把旧数据库对象解锁.(详细看SQLiteOpenHelper类的源代码,这里只是初步看了下,并没验证,可能有错误或遗漏地方)


SQLiteDatabase
SQLiteDatabase提供了execSQL()函数用于执行原生sql语句,另外还提供了insert(),update(),delete(),query()等函数,通过重写实现我们的需求
后四种函数都把sql语句拆分作为参数传入,而在使用这些函数的过程中,我们又需要用到其他一些类,如Uri,UriMatcher,SQLiteQueryBuilder,ContentValues,ContentResolver,ContentUris,ContentProvider,Cursor等
Android的数据库操作形式类似于REST架构web的访问形式,Uri表示了我们要操作的目标
ContentProvider相当于封装类,把所有的事情都封装在其中,提供操作接口,这就使我们在更高一层来进行数据库操作了.
Cursor类似于JDBC中的ResultSet,表示返回的查询结果集(Cursor是一个接口,这里返回的应该是SQLiteCursor对象?)


Uri(android.net.Uri)
Android中使用了类似REST架构的形式来请求数据库(了解REST架构对于理解为什么Android用Uri很有帮助)
Uri是一个抽象类,我们使用它的静态方法parse()来创建一个Uri(StringUri)
Uri uri = Uri.parse("content://com.ksn.provider.BookProvider/books");
看下面两条uri的区别
content://com.ksn.provider.BookProvider/books/----------------代表了books表下所有记录
content://com.ksn.provider.BookProvider/books/13-------------代表了books表下id为13的记录


UriMatcher
UriMatcher用来解释Uri,识别Uri的类型,从而进行正确的操作.例如识别Uri是对一组数据还是一条数据进行操作.
为UriMatcher注册模式:
private static final UriMatcher sUriMatcher;
private static final int INCOME_BOOK_COLLECTION_URI_INDICATOR = 1;
private static final int INCOME_SINGLE_BOOK_URI_INDICATOR = 2;
static {
        sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        sUriMatcher.addURI(BookProviderMetaData.AUTHORITY, "books", INCOME_BOOK_COLLECTION_URI_INDICATOR);
        sUriMatcher.addURI(BookProviderMetaData.AUTHORITY, "books/#", INCOME_SINGLE_BOOK_URI_INDICATOR);
}
/*然后就可以使用match(uri)来识别改uri属于哪种模式了.*/
public String getType(Uri uri) {
        switch(sUriMatcher.match(uri)){
        case INCOME_BOOK_COLLECTION_URI_INDICATOR:
            return BookTableMetaData.CONTENT_TYPE;
        case INCOME_SINGLE_BOOK_URI_INDICATOR:
            return BookTableMetaData.CONTENT_ITEM_TYPE;
        default:
            throw new IllegalArgumentException("Unknown URI " + uri);
    }
}



Cursor
使用游标须注意:
-游标是一个行集合
-游标默认并不是指向第一条记录,所以在使用前先调用moveToFirst()
-需要知道列名
-需要知道列类型
-所有字段的访问都是基于列编号的,所以必须先讲列名转换为列编号
-游标可以随意移动
-游标可以用来获取行数


ContentProvider
作为上层的封装类,ContentProvider提供了基本CRUD函数,由我们来重写
-public abstract Cursor query(Uri uri, String[] projection,String selection, String[] selectionArgs, String sortOrder);
uri:要查询的目标
projection:返回的列(String[])
selection:where子句
selectionArgs:如果where子句存在?占位符,则这里需要提供参数
sortOrder:order自居
对于projection,如果不知道具体有哪些列,则会很难进行查询,所以一般应该提供一个对应表的Columns类,例如
People类对象一个PeopleColumns类,该类提供表的一些信息

-public abstract Uri insert(Uri uri, ContentValues values);
关键类android.content.ContentValues,android.content.ContentResolver
Android使用类ContentValues(键值对)来保存即将插入的单一记录的值.

-public abstract int update(Uri uri, ContentValues values, String selection,String[] selectionArgs);
uri:更新的目标
values:ContentValues对象
where:where子句
selectionArgs:传入where子句的参数
返回受影响行数.

-public abstract int delete(Uri uri, String selection, String[] selectionArgs);
与Update类似,返回受影响行数

每个函数我们都可以用SQLiteDatabase下的对应函数来实现,也可以用ContentProvider下的对象函数来实现(待研究)
其中query()函数还可以使用SQLiteQueryBuilder辅助类来实现
SQLiteQueryBuilder能更方便查询的设置,例如projection的映射,select的生成等(详细讲源代码)


ContentProvider配置
ContentProvider是Android四大基础组件之一,需要在AndroidManifest.xml中进行配置,代码如下
<provider android:name=".MyProvider"></provider>

猜你喜欢

转载自kenshenchan.iteye.com/blog/1684864