ContentProvider + SQLite的使用详解
ContentProvider 对象绑定一个Uri对象,通过getContentResolver获得一个 ContentResolver对象,通过这个对象可以对所有的ContentProvier进行操作,具体的操作的ContentProvider对象由Uri获得,例如我们想在ContentProvider对象中插入一条数据,首先获取这个对象的Uri,然后通过这个对象的Uri去访问这个ContentProvider中的数据。
可就是说我们访问ContentProvider中的数据都是由Uri指定的数据,而ContentProvider只提供接口,这些接口中都存在一个Uri的形式参数。
一般我们在使用ContentProvider的时候,是通过继承 ContentProvider对象,并重定义里边的几个关键函数:
Cursor query(Uri uri,String[] proection,String selection,String[] selectionArgs,String sortOrder);
String getType(Uri uri);
Uri insert(Uri uri,ContentValues values);
int delete(Uri uri,String where,String[] whereArgs);
int update(Uri uri,ContentValues values,String where,String[] whereArgs);
从函数的形参可见,每个函数都存在一个Uri类型的形参,也就是说Uri是指定ContentProvider的唯一标示。通过Uri,我们可以访问到这其中的所有数据。
那么Uri是如何与ContentProvider产生关联的呢???!!!
Uri查查有道词典,我们可以看到,Uri的本意就是统一资源标示符,全写为Uniform Resource Identifier,也就是说每一个Uri都是唯一的,先抛开Uri不提,ContentProvider到底是如何与Uri产生联系呢?
产生联系要分为三个步骤,
第一步:要定义一个Uri对象,Uri对象的产生是通过Uri.parse(String s);这个函数产生,其中字符串S是有一定规则的,也就是Uri的三部分。所以String 必须是 s = “content://” + 数据的路径/文件名 + 标识(ID),标识是可选的,数据路径类似于文件的路径,/string1/string2/string2/….,文件名我们可以将他看做是ContentProvier存储数据文件。
通常数据路径都定义成一个字符串长量,这个长量不能被改变。
例如 final String AUTHORITY = “com.android.MyProvider.PeopleInfo”
利用这个我们可以生成一个Uri uri = Uri.parse(“content://” + AUTHORITY + /文件名);
文件名我们可以随便起,定义一个Uri之后,接下来看第二步。
第二步,在继承ContentProvider的类中定义一个 UriMatcher对象,
UriMatcher matcher =new UriMatcher(UriMatcher.NO_MATCH);
这个类型的对象用来与指定的Uri匹配,如果匹配成功,说明指定的Uri与本对象的唯一标识,如果不匹配就不拒绝访问。
第三部,将Uri 对象加入匹配的对象中,即matcher.addURI(AUTHORITY , 文件名 ,Uri的键值);
当匹配的时候是调用。matcher.mathc(Uri uri),返回值就是 Uri的键值。
从绑定Uri到匹配Uri的伪代码如下:
从绑定Uri到匹配Uri的伪代码如下: Uri uri = Uri.Parse(“content://” + 文件路径/文件名); //这里还可以指定某一行的Uri,即加上Uri的第三部分,标识(ID), UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); Matcher.addURI(文件路径,文件名,Uri键值); 匹配时 If(Uir键值 != matcher.match(uri)) 退出程序,拒绝访问。
详情看代码
PPeople.java,为了和数据库一起使用,就是用数据库保存ContentProvider的数据,这个类中定义了,文件路径,即String AUTHORITH, 且定义了Uri对象,CONTENT_URI,可见Uri的三部分,其中第三不存在,即
public static final Uri CONTENT_URI = Uri.parse("content://"+ AUTHORITY +"/ppinf");
其中/ppinf就是文件名,文件名与文件路径都是抽象的说法,类似于content是一个虚拟存在的系统符号,文件路径+文件名就是这个系统中我们创建的ContentProvider数据。
为了数据库使用,在内部继承了 BaseColums类,这个类中存在一个唯一标示的ID,就是主键,同时我们可以自定义我们数据库中的每一列的字符。
除此之外为了方便Uir访问每一条数据,我们定义了两中Uri的类型,一个是包含全部数据,一个是只有一条数据即:
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.pdr.ppinf"; public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.pdr.ppinf";
好了废话不多说,看完整的PPeople.java
public class PPeople { public static final String AUTHORITY = "com.provider.PPeople"; private PPeople() { } public static final class PPles implements BaseColumns { private PPles(){} public static final Uri CONTENT_URI = Uri.parse("content://"+ AUTHORITY +"/ppinf"); public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.pdr.ppinf"; public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.pdr.ppinf"; public static final String DEFAULT_SORT_ORDER = "modified DESC"; public static final String NAME = "name"; public static final String SEX = "sex"; public static final String AGE = "age"; } }
接下来我们需要定义我们自己的ContentProvier对象,来看MyProvider.java
为了使用数据库存在我们的数据,需要声明一个数据库对象,并创建。同时创建数据表:
private static class DatabaseHelper extends SQLiteOpenHelper { public DatabaseHelper(Context context)//, String name,CursorFactory factory, int version) { super(context,DATABASE_NAME,null,DATABASE_VERSION); // TODO Auto-generated constructor stub } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_TABLE); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // TODO Auto-generated method stub db.execSQL("DROP TABLE IF EXISTS pinf"); onCreate(db); } }
在MyProvider类中,指定创建表的SQL语句,定义UriMathcher对象,并添加Uri的关联。
static { sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); sUriMatcher.addURI(PPeople.AUTHORITY,"ppinf",PPInf); sUriMatcher.addURI(PPeople.AUTHORITY,"ppinf/#",PPInf_ID); sPInfProjectionMap = new HashMap<String,String>(); sPInfProjectionMap.put(PPles._ID, PPles._ID); sPInfProjectionMap.put(PPles.AGE,PPles.AGE); sPInfProjectionMap.put(PPles.SEX,PPles.SEX); sPInfProjectionMap.put(PPles.NAME, PPles.NAME); }
关键的是
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); sUriMatcher.addURI(PPeople.AUTHORITY,"ppinf",PPInf); sUriMatcher.addURI(PPeople.AUTHORITY,"ppinf/#",PPInf_ID);
初始化UriMatcher的对象,并添加于Uri的关联。
其他还需要重定义
Cursor query(Uri uri,String[] proection,String selection,String[] selectionArgs,String sortOrder);
String getType(Uri uri);
Uri insert(Uri uri,ContentValues values);
int delete(Uri uri,String where,String[] whereArgs);
int update(Uri uri,ContentValues values,String where,String[] whereArgs);
这5个函数,目前我只重写了 insert与query的函数,这样可以简单的添加并测试是否成功。
完整的MyProvider.java如下:
package com.example.com.android.provider; import java.util.HashMap; import com.example.com.android.provider.PPeople.PPles; import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.text.TextUtils; import android.util.Log; public class MyProvider extends ContentProvider { private static final String TAG = "MyProvider"; private static final String DATABASE_NAME = "Ppinf.db"; private static final int DATABASE_VERSION = 2; private static final String PP_TABLE_NAME = "pinf"; private static final HashMap<String,String> sPInfProjectionMap; private static final int PPInf = 1; private static final int PPInf_ID = 2; private static final UriMatcher sUriMatcher; private DatabaseHelper mOpenHelper; private static final String CREATE_TABLE = "CREATE TABLE " + PP_TABLE_NAME + " (" + PPles._ID + " INTEGER PRIMARY KEY," + PPles.NAME + " TEXT," + PPles.SEX + " TEXT," + PPles.AGE + " INTEGER" + " );"; static { sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); sUriMatcher.addURI(PPeople.AUTHORITY,"ppinf",PPInf); sUriMatcher.addURI(PPeople.AUTHORITY,"ppinf/#",PPInf_ID); sPInfProjectionMap = new HashMap<String,String>(); sPInfProjectionMap.put(PPles._ID, PPles._ID); sPInfProjectionMap.put(PPles.AGE,PPles.AGE); sPInfProjectionMap.put(PPles.SEX,PPles.SEX); sPInfProjectionMap.put(PPles.NAME, PPles.NAME); } private static class DatabaseHelper extends SQLiteOpenHelper { public DatabaseHelper(Context context)//, String name,CursorFactory factory, int version) { super(context,DATABASE_NAME,null,DATABASE_VERSION); // TODO Auto-generated constructor stub } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_TABLE); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // TODO Auto-generated method stub db.execSQL("DROP TABLE IF EXISTS pinf"); onCreate(db); } } @Override public boolean onCreate() { // TODO Auto-generated method stub mOpenHelper = new DatabaseHelper(getContext()); return true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { // TODO Auto-generated method stub SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); switch(sUriMatcher.match(uri)) { case PPInf: Log.d(TAG,"daaa1111"); qb.setTables(PP_TABLE_NAME); qb.setProjectionMap(sPInfProjectionMap); break; case PPInf_ID: Log.d(TAG,"daaa2222"); qb.setTables(PP_TABLE_NAME); qb.setProjectionMap(sPInfProjectionMap); qb.appendWhere(PPInf_ID + "=" + uri.getPathSegments().get(1)); break; default: throw new IllegalArgumentException("Unknown URI" + uri); } Log.d(TAG,"adfasfsfsfsf"); SQLiteDatabase db = mOpenHelper.getReadableDatabase(); Cursor c = qb.query(db, projection, selection, selectionArgs, null, null,sortOrder); c.setNotificationUri(getContext().getContentResolver(),uri); return c; } @Override public String getType(Uri uri) { // TODO Auto-generated method stub switch(sUriMatcher.match(uri)) { case PPInf: return PPles.CONTENT_TYPE; case PPInf_ID: return PPles.CONTENT_ITEM_TYPE; default: throw new IllegalArgumentException("Unknown URI" + uri); } } public Uri insert(Uri uri, ContentValues initialValues) { // TODO Auto-generated method stub if(sUriMatcher.match(uri) != PPInf) { throw new IllegalArgumentException("Unknown URI " + uri); } SQLiteDatabase db = mOpenHelper.getWritableDatabase(); long rowId = db.insert(PP_TABLE_NAME,PPles._ID,initialValues); Log.d(TAG,"daaa33333"); //rowId = db.insert(RuiXin.TNAME,RuiXin.TID,values); if(rowId>0) { Log.d(TAG,"daaa4444"); Uri noteUri=ContentUris.withAppendedId(PPles.CONTENT_URI, rowId); getContext().getContentResolver().notifyChange(noteUri, null); return noteUri; } throw new IllegalArgumentException("Unknown URI"+uri); } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { // TODO Auto-generated method stub return 0; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { // TODO Auto-generated method stub return 0; } }
最后我们来看看测试:
测试首先是测试 插入数据:
ContentValues values = new ContentValues(); values.put(PPles.NAME, "NIUBEN"); values.put(PPles.SEX,"MAN"); values.put(PPles.AGE, 24); getContentResolver().insert(PPeople.PPles.CONTENT_URI,values);
来看显示数据:
private void disp() { // TODO Auto-generated method stub String columns [] = new String[] {PPeople.PPles._ID, PPeople.PPles.NAME, PPeople.PPles.SEX, PPeople.PPles.AGE}; Cursor cur = getContentResolver().query(PPeople.PPles.CONTENT_URI, columns, null, null, null); if(cur.moveToFirst()) { String id = null; String name = null; String sex = null; String age = null; do { id = cur.getString(cur.getColumnIndex(PPeople.PPles._ID)); name = cur.getString(cur.getColumnIndex(PPeople.PPles.NAME)); sex = cur.getString(cur.getColumnIndex(PPeople.PPles.SEX)); age = cur.getString(cur.getColumnIndex(PPeople.PPles.AGE)); Toast toast = Toast.makeText(this, id +" name" + name + "sex " + sex +" age "+ age , Toast.LENGTH_LONG); toast.setGravity(Gravity.TOP|Gravity.CENTER, 0, 40); toast.show(); } while(cur.moveToNext()); } }
通过查询,获得数据,并根据每一条数据的列参数,获取对应的值。
测试时候不要忘记在AndroidMainfest.xml中注册,我们的Provider类。
<provider android:name="MyProvider" android:authorities="com.provider.PPeople"/> <activity android:name=".MainActivity" android:label="@string/title_activity_main" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <intent-filter> <data android:mimeType="vnd.android.cursor.dir/vnd.pdr.ppinf"/> </intent-filter> <intent-filter> <data android:mimeType="vnd.android.cursor.item/vnd.pdr.ppinf"/> </intent-filter>
欢迎大家讨论,不懂得可以相互交流,以上写的有筒靴觉得不对或者不好的地方,欢迎拍砖,谢谢观看