&&国之画&&&& &&&&&&
&& &&&&&&&&&&&&&&
鲁ICP备号-4
打开技术之扣,分享程序人生!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);
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table ............");
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 sUriM
private static final int INCOME_BOOK_COLLECTION_URI_INDICATOR = 1;
private static final int INCOME_SINGLE_BOOK_URI_INDICATOR = 2;
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;
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&
文章评论 以下网友留言只代表其个人观点,不代表本网站的观点和立场。Android_Storage(9)
ContentProvider何时创建?
1、ContentProvider所在的Process启动了,ContentProvider就会在ActivityThread中启动。
2、从数据共享的角度出发,ContentProvider应该是Android在系统启动时就创建了,否则就谈不上数据共享了。这就要求在AndroidManifest.XML中使用&provider&元素明确定义。&
SQLiteDatabase方法何时执行?
1、SQLiteOpenHelper会自动检测数据库文件是否存在。如果存在,会打开这个数据库,在这种情况下就不会调用onCreate()方法。如果数据库文件不存在,SQLiteOpenHelper首先会创建一个数据库文件,然后打开这个数据库,最后调用onCreate()方法。因此,onCreate()方法一般用来在新创建的数据库中建立表、视图等数据库组建。也就是说oncreate()方法在数据库文件第一次创建时调用。
2、如果数据库文件不存在,只有oncreate()被调用(该方法在创建数据库时被调用一次)。如果数据库文件存在,会调用onupdate()方法升级数据库,并更新版本号。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:131063次
积分:1880
积分:1880
排名:第17416名
转载:303篇
(61)(83)(85)(21)(47)(10)Android ContentProvider的介绍(很详细) -
- ITeye技术网站
博客分类:
一、ContentProvider的概念 ContentProvider:为存储和获取数据提供统一的接口。可以在不同的应用程序之间共享数据。Android已经为常见的一些数据提供了默认的ContentProvider 1、ContentProvider使用表的形式来组织数据 无论数据的来源是什么,ContentProvider都会认为是一种表,然后把数据组织成表格 2、ContentProvider提供的方法 query:查询 insert:插入 update:更新 delete:删除 getType:得到数据类型 onCreate:创建数据时调用的回调函数 3、每个ContentProvider都有一个公共的URI,这个URI用于表示这个ContentProvider所提供的数据。Android所提供的ContentProvider都存放在android.provider包当中 二、ContentProvider的内部原理 自定义一个ContentProvider,来实现内部原理 步骤: 1、定义一个CONTENT_URI常量(里面的字符串必须是唯一) Public static final Uri CONTENT_URI = Uri.parse("content://com.WangWeiDa.MyContentprovider"); 如果有子表,URI为: Public static final Uri CONTENT_URI = Uri.parse("content://com.WangWeiDa.MyContentProvider/users"); 2、定义一个类,继承ContentProvider Public class MyContentProvider extends ContentProvider 3、实现ContentProvider的所有方法(query、insert、update、delete、getType、onCreate) package com.WangWeiDa. import java.util.HashM import com.WangWeiDa.cp.MyContentProviderMetaData.UserTableMetaD import com.WangWeiDa.data.DatabaseH import android.content.ContentP import android.content.ContentU import android.content.ContentV import android.content.UriM import android.database.C import android.database.sqlite.SQLiteD import android.database.sqlite.SQLiteQueryB import android.net.U import android.text.TextU public class MyContentProvider extends ContentProvider { //访问表的所有列 public static final int INCOMING_USER_COLLECTION = 1; //访问单独的列 public static final int INCOMING_USER_SINGLE = 2; //操作URI的类 public static final UriMatcher uriM //为UriMatcher添加自定义的URI static{ uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(MyContentProviderMetaData.AUTHORITIES,"/user", INCOMING_USER_COLLECTION); uriMatcher.addURI(MyContentProviderMetaData.AUTHORITIES,"/user/#", INCOMING_USER_SINGLE); } private DatabaseH //为数据库表字段起别名 public static HashMap userProjectionM static { userProjectionMap = new HashMap(); userProjectionMap.put(UserTableMetaData._ID,UserTableMetaData._ID); userProjectionMap.put(UserTableMetaData.USER_NAME, UserTableMetaData.USER_NAME); } /** * 删除表数据 */ @Override public int delete(Uri uri, String selection, String[] selectionArgs) { System.out.println("delete"); //得到一个可写的数据库 SQLiteDatabase db = dh.getWritableDatabase(); //执行删除,得到删除的行数 int count = db.delete(UserTableMetaData.TABLE_NAME, selection, selectionArgs); } /** * 数据库访问类型 */ @Override public String getType(Uri uri) { System.out.println("getType"); //根据用户请求,得到数据类型 switch (uriMatcher.match(uri)) { case INCOMING_USER_COLLECTION: return MyContentProviderMetaData.UserTableMetaData.CONTENT_TYPE; case INCOMING_USER_SINGLE: return MyContentProviderMetaData.UserTableMetaData.CONTENT_TYPE_ITEM; default: throw new IllegalArgumentException("UnKnown URI"+uri); } } /** * 插入数据 */ @Override public Uri insert(Uri uri, ContentValues values) { //得到一个可写的数据库 SQLiteDatabase db = dh.getWritableDatabase(); //向指定的表插入数据,得到返回的Id long rowId = db.insert(UserTableMetaData.TABLE_NAME, null, values); if(rowId & 0){//判断插入是否执行成功 //如果添加成功,利用新添加的Id和 Uri insertedUserUri = ContentUris.withAppendedId(UserTableMetaData.CONTENT_URI, rowId); //通知监听器,数据已经改变 getContext().getContentResolver().notifyChange(insertedUserUri, null); return insertedUserU } } /** * 创建ContentProvider时调用的回调函数 */ @Override public boolean onCreate() { System.out.println("onCreate"); //得到数据库帮助类 dh = new DatabaseHelp(getContext(),MyContentProviderMetaData.DATABASE_NAME); } /** * 查询数据库 */ @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { //创建一个执行查询的Sqlite SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); //判断用户请求,查询所有还是单个 switch(uriMatcher.match(uri)){ case INCOMING_USER_COLLECTION: //设置要查询的表名 qb.setTables(UserTableMetaData.TABLE_NAME); //设置表字段的别名 qb.setProjectionMap(userProjectionMap); case INCOMING_USER_SINGLE: qb.setTables(UserTableMetaData.TABLE_NAME); qb.setProjectionMap(userProjectionMap); //追加条件,getPathSegments()得到用户请求的Uri地址截取的数组,get(1)得到去掉地址中/以后的第二个元素 qb.appendWhere(UserTableMetaData._ID + "=" + uri.getPathSegments().get(1)); } //设置排序 String orderBy; if(TextUtils.isEmpty(sortOrder)){ orderBy = UserTableMetaData.DEFAULT_SORT_ORDER; } else{ orderBy = sortO } //得到一个可读的数据库 SQLiteDatabase db = dh.getReadableDatabase(); //执行查询,把输入传入 Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy); //设置监听 c.setNotificationUri(getContext().getContentResolver(), uri); } /** * 更新数据库 */ @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { System.out.println("update"); //得到一个可写的数据库 SQLiteDatabase db = dh.getWritableDatabase(); //执行更新语句,得到更新的条数 int count = db.update(UserTableMetaData.TABLE_NAME, values, selection, selectionArgs); } } 4、在AndroidMinifest.xml中进行声明
android:name=".cp.MyContentProvider"
android:authorities="com.WangWeiDa.cp.MyContentProvider" /& **为ContentProvider提供一个常量类MyContentProviderMetaData.java package com.WangWeiDa. import android.net.U import android.provider.BaseC public class MyContentProviderMetaData { //URI的指定,此处的字符串必须和声明的authorities一致 public static final String AUTHORITIES = "com.wangweida.cp.MyContentProvider"; //数据库名称 public static final String DATABASE_NAME = "myContentProvider.db"; //数据库的版本 public static final int DATABASE_VERSION = 1; //表名 public static final String USERS_TABLE_NAME = "user"; public static final class UserTableMetaData implements BaseColumns{ //表名 public static final String TABLE_NAME = "user"; //访问该ContentProvider的URI public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITIES + "/user"); //该ContentProvider所返回的数据类型的定义 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.myprovider.user"; public static final String CONTENT_TYPE_ITEM = "vnd.android.cursor.item/vnd.myprovider.user"; //列名 public static final String USER_NAME = "name"; //默认的排序方法 public static final String DEFAULT_SORT_ORDER = "_id desc"; } }
浏览 107912
xiechengfa
浏览: 397421 次
来自: 上海
困惑了很久,终于找到答案了
pixels = dps * (density / 160). ...
楼主给力,解决了我遇到的问题
你好,我转载了您的此篇博客,如果有版权问题,请告诉我,谢谢!转 ...
请教您个问题,如何将drawable对象转换为id类型?}