ContentProvider的作用:
1、为存储和获取数据提供了一个统一的接口;
2、使用ContentProvider可以在不同的应用程序之间共享数据;
3、Android为常见的一些数据提供了ContentProvider(包括音频、视频、图片和通讯录等等);
ContentProvider使用表的结构来存储数据;每个ContentProvider都提供了一个公共的URI,这个URL用于表示这个ContentProvider所提供的数据;
下面列举一些常用的接口:
1、query(Uri uri,String[] projection,String selection,String[] selectionArgs,String sortOrder):通过Uri进行查询,返回一个Cursor.
2、insert(Uri uri,ContentValues values):将一组数据插入到Uri指定的地方。
3、update(Uri uri,ContentValues values,String where,String[] selectionArgs):更新Uri指定位置的数据。
4、delete(Uri uri,String where,String[] selectionArgs):删除指定Uri并且符合一定条件的数据。
5、getType():得到数据类型;
6、OnCreate():创建时的回调函数
实现一个ContentProvider的过程:
1 定义个CONTENT_URI常量;
2 定义一个类,继承ContentProvider;
3 实现一些方法;
4 在AndroidMainfest.xml中进行声明;
外界程序通过ContentResolver接口可以访问ContentProvider提供的数据,在Activity当中通过getContentResolver()可以得到当前应用ContentResolver实例。其提供的接口与ContentProvider提供的接口对应:
1、query(Uri uri,String[] projection,String selection,String[] selectionArgs,String sortOrder):通过Uri进行查询,返回一个Cursor.
2、insert(Uri uri,ContentValues values):将一组数据插入到Uri指定的地方。
3、update(Uri uri,ContentValues values,String where,String[] selectionArgs):更新Uri指定位置的数据。
4、delete(Uri uri,String where,String[] selectionArgs):删除指定Uri并且符合一定条件的数据。
以下代码来自:http://blog.csdn.net/linshutao/article/details/5779558
【1】要为当前应用程序的私有数据定义URI,就需要专门定义个继承自ContentProvider的类,然后实现各个不同的操作所调用的方法。
首先在该应用程序的某个类中定义所有与数据库操作有关的静态字段,以便打包成jar文件供其他应用程序调用
此例子没有新定义一个专门的类来存放这些字段,而是在继承了SQLiteOpenHelper的类StudentData中定义:
- package com.shutao.testsqlite2;
- public class StudentData extends SQLiteOpenHelper {
- /*
- * 分别定义了数据库和表的名称、表中各个字段的名称、数据库的版本号
- */
- public final static String DB_NAME = "student";
- public final static String TABLE_NAME = "hero";
- public final static String SNAME = "name";
- //Be sure to include an integer column named "_id" (with the constant _ID) for the IDs of the records
- public final static String SID = "_id";
- public final static int DB_VERSION = 1;
- /*
- * AUTHORITY:定义了标识ContentProvider的字符串 ;
- * ITEM和ITEM_ID分别用于UriMatcher(资源标识符匹配器)中对路径item和item/id的匹配号码
- * CONTENT_TYPE和CONTENT_ITEM_TYPE定义了数据的MIME类型。需要注意的是: 单一数据的MIME 类型字符串应该以
- * vnd.android.cursor.item/开头,数据集的MIME类型字符串应该以vnd.android.cursor.dir开头
- * CONTENT_URI定义的是查询当前表数据的content://样式URI
- */
- //类名的路径名
- public static final String AUTHORITY = "com.shutao.testsqlite2.provider.studentdata";
- public static final int ITEM = 1;
- public static final int ITEM_ID = 2;
- public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.shutao.testsqlite2.studentdata";
- public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.shutao.testsqlite2.studentdata";
- public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY
- + "/item");
- public StudentData(Context context) {
- super(context, DB_NAME, null, DB_VERSION);
- }
- @Override
- public void onCreate(SQLiteDatabase db) {
- db.execSQL("CREATE TABLE " + TABLE_NAME + "(" + SID
- + " VARCHAR PRIMARY KEY," + SNAME + " VARCHAR NOT NULL);");
- }
- @Override
- public void onUpgrade(SQLiteDatabase db, int arg1, int arg2) {
- db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
- }
- }
package com.shutao.testsqlite2; public class StudentData extends SQLiteOpenHelper { /* * 分别定义了数据库和表的名称、表中各个字段的名称、数据库的版本号 */ public final static String DB_NAME = "student"; public final static String TABLE_NAME = "hero"; public final static String SNAME = "name"; //Be sure to include an integer column named "_id" (with the constant _ID) for the IDs of the records public final static String SID = "_id"; public final static int DB_VERSION = 1; /* * AUTHORITY:定义了标识ContentProvider的字符串 ; * ITEM和ITEM_ID分别用于UriMatcher(资源标识符匹配器)中对路径item和item/id的匹配号码 * CONTENT_TYPE和CONTENT_ITEM_TYPE定义了数据的MIME类型。需要注意的是: 单一数据的MIME 类型字符串应该以 * vnd.android.cursor.item/开头,数据集的MIME类型字符串应该以vnd.android.cursor.dir开头 * CONTENT_URI定义的是查询当前表数据的content://样式URI */ //类名的路径名 public static final String AUTHORITY = "com.shutao.testsqlite2.provider.studentdata"; public static final int ITEM = 1; public static final int ITEM_ID = 2; public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.shutao.testsqlite2.studentdata"; public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.shutao.testsqlite2.studentdata"; public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/item"); public StudentData(Context context) { super(context, DB_NAME, null, DB_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE " + TABLE_NAME + "(" + SID + " VARCHAR PRIMARY KEY," + SNAME + " VARCHAR NOT NULL);"); } @Override public void onUpgrade(SQLiteDatabase db, int arg1, int arg2) { db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); } }【2】接下来定义一个继承自ContentProvider的类:MyProvider.java,来实现对数据进行操作的各个方法。这里将用到StudentData来辅助得到SQLiteDatabase对象:
- package com.shutao.testsqlite2;
- public class MyProvider extends ContentProvider {
- StudentData sd;
- private static final UriMatcher sMatcher;
- static {
- // 传入匹配码如果大于0表示匹配根路径或传入-1,即常量UriMatcher.NO_MATCH表示不匹配根路径
- // addURI()方法是用来增加其他URI匹配路径的:
- // 第一个参数代表传入标识ContentProvider的AUTHORITY字符串
- // 第二个参数是要匹配的路径,#代表任意数字,另外还可以用*来匹配任意文本
- // 第三个参数必须传入一个大于零的匹配码,用于match()方法对相匹配的URI返回相对应的匹配码
- sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
- sMatcher.addURI(StudentData.AUTHORITY, "item", StudentData.ITEM);
- sMatcher.addURI(StudentData.AUTHORITY, "item/#", StudentData.ITEM_ID);
- }
- /*
- * 每当ContentProvider启动时都会回调onCreate()方法。此方法主要执行一些ContentProvider初始化
- * 的工作,返回true表示初始化成功,返回false则初始化失败。
- */
- @Override
- public boolean onCreate() {
- sd = new StudentData(this.getContext());
- return true;
- }
- // getType()是用来返回数据的MIME类型的方法。使用sMatcher对URI进行匹配,并返回相应的MIME类型字符串
- @Override
- public String getType(Uri uri) {
- switch (sMatcher.match(uri)) {
- case StudentData.ITEM:
- return StudentData.CONTENT_TYPE;
- case StudentData.ITEM_ID:
- return StudentData.CONTENT_ITEM_TYPE;
- default:
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
- }
- /*
- * 插入数据,返回新插入数据的URI,只接受数据集的URI,即指向表的URI
- */
- @Override
- public Uri insert(Uri uri, ContentValues values) {
- SQLiteDatabase sdb = sd.getWritableDatabase();
- long rowId;
- if (sMatcher.match(uri) != StudentData.ITEM) {
- throw new IllegalArgumentException("Unknow URI " + uri);
- }
- rowId = sdb.insert(StudentData.TABLE_NAME, StudentData.SID, values);
- if (rowId > 0) {
- Uri noteUri = ContentUris.withAppendedId(StudentData.CONTENT_URI,
- rowId);
- //通知监听器,数据已经改变
- this.getContext().getContentResolver().notifyChange(noteUri, null);
- return noteUri;
- }
- throw new SQLException("Failed to insert row into " + uri);
- }
- /*
- * 用于数据的删除,返回的是所影响数据的数目,首先利用数据库辅助对象获取一个SQLiteDatabase对象
- * 然后根据传入Uri用sMatcher进行匹配,对单个数据或数据集进行删除或修改。notifyChange()方法
- * 用来通知注册在次URI上的观察者(observer)数据发生了改变。
- */
- @Override
- public int delete(Uri uri, String selection, String[] selectionArgs) {
- SQLiteDatabase sdb = sd.getWritableDatabase();
- int count;
- switch (sMatcher.match(uri)) {
- case StudentData.ITEM:
- count = sdb.delete(StudentData.DB_NAME, selection, selectionArgs);
- break;
- case StudentData.ITEM_ID:
- String id = uri.getPathSegments().get(1);
- count = sdb.delete(StudentData.DB_NAME, StudentData.SID
- + "="
- + id
- + (!TextUtils.isEmpty(selection) ? " AND (" + selection
- + ")" : ""), selectionArgs);
- break;
- default:
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
- this.getContext().getContentResolver().notifyChange(uri, null);
- return count;
- }
- /*
- * 查询数据,将数据装入一个Cursor对象并返回
- */
- @Override
- public Cursor query(Uri uri, String[] projection, String selection,
- String[] selectionArgs, String sortOrder) {
- SQLiteDatabase sdb = sd.getReadableDatabase();
- Cursor c;
- switch (sMatcher.match(uri)) {
- case StudentData.ITEM:
- c = sdb.query(StudentData.TABLE_NAME, projection, selection,
- selectionArgs, null, null, sortOrder);
- break;
- case StudentData.ITEM_ID:
- String id = uri.getPathSegments().get(1);
- c = sdb.query(StudentData.TABLE_NAME, projection, StudentData.SID
- + "="
- + id
- + (!TextUtils.isEmpty(selection) ? " AND (" + selection
- + ")" : ""), selectionArgs, null, null, sortOrder);
- break;
- default:
- Log.i("sMatcher.match(uri)", String.valueOf(sMatcher.match(uri)));
- throw new IllegalArgumentException("Query with unknown URI: " + uri);
- }
- c.setNotificationUri(getContext().getContentResolver(), uri);
- return c;
- }
- // 更新,与删除类方法类似
- @Override
- public int update(Uri uri, ContentValues values, String selection,
- String[] selectionArgs) {
- SQLiteDatabase sdb = sd.getWritableDatabase();
- int count;
- switch (sMatcher.match(uri)) {
- case StudentData.ITEM:
- count = sdb.update(StudentData.TABLE_NAME, values, selection,
- selectionArgs);
- break;
- case StudentData.ITEM_ID:
- String id = uri.getPathSegments().get(1);
- count = sdb.update(StudentData.DB_NAME, values, StudentData.SID
- + "="
- + id
- + (!TextUtils.isEmpty(selection) ? " AND (" + selection
- + ")" : ""), selectionArgs);
- break;
- default:
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
- this.getContext().getContentResolver().notifyChange(uri, null);
- return count;
- }
- }
package com.shutao.testsqlite2; public class MyProvider extends ContentProvider { StudentData sd; private static final UriMatcher sMatcher; static { // 传入匹配码如果大于0表示匹配根路径或传入-1,即常量UriMatcher.NO_MATCH表示不匹配根路径 // addURI()方法是用来增加其他URI匹配路径的: // 第一个参数代表传入标识ContentProvider的AUTHORITY字符串 // 第二个参数是要匹配的路径,#代表任意数字,另外还可以用*来匹配任意文本 // 第三个参数必须传入一个大于零的匹配码,用于match()方法对相匹配的URI返回相对应的匹配码 sMatcher = new UriMatcher(UriMatcher.NO_MATCH); sMatcher.addURI(StudentData.AUTHORITY, "item", StudentData.ITEM); sMatcher.addURI(StudentData.AUTHORITY, "item/#", StudentData.ITEM_ID); } /* * 每当ContentProvider启动时都会回调onCreate()方法。此方法主要执行一些ContentProvider初始化 * 的工作,返回true表示初始化成功,返回false则初始化失败。 */ @Override public boolean onCreate() { sd = new StudentData(this.getContext()); return true; } // getType()是用来返回数据的MIME类型的方法。使用sMatcher对URI进行匹配,并返回相应的MIME类型字符串 @Override public String getType(Uri uri) { switch (sMatcher.match(uri)) { case StudentData.ITEM: return StudentData.CONTENT_TYPE; case StudentData.ITEM_ID: return StudentData.CONTENT_ITEM_TYPE; default: throw new IllegalArgumentException("Unknown URI " + uri); } } /* * 插入数据,返回新插入数据的URI,只接受数据集的URI,即指向表的URI */ @Override public Uri insert(Uri uri, ContentValues values) { SQLiteDatabase sdb = sd.getWritableDatabase(); long rowId; if (sMatcher.match(uri) != StudentData.ITEM) { throw new IllegalArgumentException("Unknow URI " + uri); } rowId = sdb.insert(StudentData.TABLE_NAME, StudentData.SID, values); if (rowId > 0) { Uri noteUri = ContentUris.withAppendedId(StudentData.CONTENT_URI, rowId); //通知监听器,数据已经改变 this.getContext().getContentResolver().notifyChange(noteUri, null); return noteUri; } throw new SQLException("Failed to insert row into " + uri); } /* * 用于数据的删除,返回的是所影响数据的数目,首先利用数据库辅助对象获取一个SQLiteDatabase对象 * 然后根据传入Uri用sMatcher进行匹配,对单个数据或数据集进行删除或修改。notifyChange()方法 * 用来通知注册在次URI上的观察者(observer)数据发生了改变。 */ @Override public int delete(Uri uri, String selection, String[] selectionArgs) { SQLiteDatabase sdb = sd.getWritableDatabase(); int count; switch (sMatcher.match(uri)) { case StudentData.ITEM: count = sdb.delete(StudentData.DB_NAME, selection, selectionArgs); break; case StudentData.ITEM_ID: String id = uri.getPathSegments().get(1); count = sdb.delete(StudentData.DB_NAME, StudentData.SID + "=" + id + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ")" : ""), selectionArgs); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } this.getContext().getContentResolver().notifyChange(uri, null); return count; } /* * 查询数据,将数据装入一个Cursor对象并返回 */ @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteDatabase sdb = sd.getReadableDatabase(); Cursor c; switch (sMatcher.match(uri)) { case StudentData.ITEM: c = sdb.query(StudentData.TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder); break; case StudentData.ITEM_ID: String id = uri.getPathSegments().get(1); c = sdb.query(StudentData.TABLE_NAME, projection, StudentData.SID + "=" + id + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ")" : ""), selectionArgs, null, null, sortOrder); break; default: Log.i("sMatcher.match(uri)", String.valueOf(sMatcher.match(uri))); throw new IllegalArgumentException("Query with unknown URI: " + uri); } c.setNotificationUri(getContext().getContentResolver(), uri); return c; } // 更新,与删除类方法类似 @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { SQLiteDatabase sdb = sd.getWritableDatabase(); int count; switch (sMatcher.match(uri)) { case StudentData.ITEM: count = sdb.update(StudentData.TABLE_NAME, values, selection, selectionArgs); break; case StudentData.ITEM_ID: String id = uri.getPathSegments().get(1); count = sdb.update(StudentData.DB_NAME, values, StudentData.SID + "=" + id + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ")" : ""), selectionArgs); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } this.getContext().getContentResolver().notifyChange(uri, null); return count; } }
【3】完成以上方法以后,还要AndroidManifest.xml中对这个ContentProvider声明:
- <provider
- android:name="MyProvider"
- android:authorities="com.shutao.testsqlite2.provider.studentdata"/>
- <!-- 其中 android:name必须跟定义的ContentProvider的类名一样
- android:authorities则指定了content://样式的URI中标识这个ContentProvider的字符串-->
<provider android:name="MyProvider" android:authorities="com.shutao.testsqlite2.provider.studentdata"/> <!-- 其中 android:name必须跟定义的ContentProvider的类名一样 android:authorities则指定了content://样式的URI中标识这个ContentProvider的字符串-->
【4】接下来在另一个程序中使用ContentResolver来操作数据:
- package com.shutao.contentprovider;
- public class TestContentProvider extends Activity {
- private EditText stu_name;
- private EditText stu_sid;
- Button commit;
- ListView studList;
- ContentResolver resolver;
- // 由于在AlertDialog中用到,所以定义为全局变量。
- String sid = "";
- private final int DIALOG_IN_USED = 1;
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- stu_name = (EditText) this.findViewById(R.id.edit_name);
- stu_sid = (EditText) this.findViewById(R.id.edit_sid);
- commit = (Button) this.findViewById(R.id.butt_add);
- studList = (ListView) this.findViewById(R.id.show_stud);
- // 【重要】得到ContentResolver()
- resolver = this.getContentResolver();
- String[] projection = { StudentData.SNAME, StudentData.SID };
- Cursor c = resolver.query(StudentData.CONTENT_URI, null, null, null,
- StudentData.SID);
- CursorAdapter adapter = new SimpleCursorAdapter(this,
- android.R.layout.simple_list_item_2, c, projection, new int[] {
- android.R.id.text1, android.R.id.text2 });
- studList.setAdapter(adapter);
- commit.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (!(stu_name.getText().length() > 0)) {
- Toast.makeText(TestContentProvider.this,
- "Please input the student's name!",
- Toast.LENGTH_LONG).show();
- } else if (!(stu_sid.getText().length() > 0)) {
- Toast
- .makeText(TestContentProvider.this,
- "Please input the student's ID!",
- Toast.LENGTH_LONG).show();
- } else {
- String name = stu_name.getText().toString();
- sid = stu_sid.getText().toString();
- Uri quri = Uri.parse(StudentData.CONTENT_URI + "/" + sid);
- Cursor c = resolver.query(quri, null, null, null, null);
- if (c.moveToFirst()) {
- // 采用Toast方式提醒
- /*
- * Toast.makeText(TestContentProvider.this,
- * "Fail to add!This ID is already in used!",
- * Toast.LENGTH_LONG).show();
- */
- // 采用AlertDialog方式提醒
- TestContentProvider.this.showDialog(DIALOG_IN_USED);
- } else {
- ContentValues values = new ContentValues();
- values.put(StudentData.SNAME, name);
- values.put(StudentData.SID, sid);
- resolver.insert(StudentData.CONTENT_URI, values);
- }
- }
- }
- });
- }
- // 如果插入的Id已经存在则用提示框进行提示
- @Override
- protected Dialog onCreateDialog(int id, Bundle args) {
- // // TODO Auto-generated method stub
- // return super.onCreateDialog(id, args);
- switch (id) {
- case DIALOG_IN_USED:
- return new AlertDialog.Builder(this).setTitle("Fail to add!")
- .setIcon(R.drawable.fail).setMessage(
- "This ID:" + sid + " is already in used!")
- .setPositiveButton("确定",
- // 特别注意:必须为:new DialogInterface.OnClickListener()
- // 不能为new
- // OnClickListener(),否则会跟View.OnClickListener冲突。
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
- // TODO Auto-generated method stub
- }
- }).create();
- default:
- return super.onCreateDialog(id, args);
- }
- }
- }
package com.shutao.contentprovider; public class TestContentProvider extends Activity { private EditText stu_name; private EditText stu_sid; Button commit; ListView studList; ContentResolver resolver; // 由于在AlertDialog中用到,所以定义为全局变量。 String sid = ""; private final int DIALOG_IN_USED = 1; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); stu_name = (EditText) this.findViewById(R.id.edit_name); stu_sid = (EditText) this.findViewById(R.id.edit_sid); commit = (Button) this.findViewById(R.id.butt_add); studList = (ListView) this.findViewById(R.id.show_stud); // 【重要】得到ContentResolver() resolver = this.getContentResolver(); String[] projection = { StudentData.SNAME, StudentData.SID }; Cursor c = resolver.query(StudentData.CONTENT_URI, null, null, null, StudentData.SID); CursorAdapter adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2, c, projection, new int[] { android.R.id.text1, android.R.id.text2 }); studList.setAdapter(adapter); commit.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (!(stu_name.getText().length() > 0)) { Toast.makeText(TestContentProvider.this, "Please input the student's name!", Toast.LENGTH_LONG).show(); } else if (!(stu_sid.getText().length() > 0)) { Toast .makeText(TestContentProvider.this, "Please input the student's ID!", Toast.LENGTH_LONG).show(); } else { String name = stu_name.getText().toString(); sid = stu_sid.getText().toString(); Uri quri = Uri.parse(StudentData.CONTENT_URI + "/" + sid); Cursor c = resolver.query(quri, null, null, null, null); if (c.moveToFirst()) { // 采用Toast方式提醒 /* * Toast.makeText(TestContentProvider.this, * "Fail to add!This ID is already in used!", * Toast.LENGTH_LONG).show(); */ // 采用AlertDialog方式提醒 TestContentProvider.this.showDialog(DIALOG_IN_USED); } else { ContentValues values = new ContentValues(); values.put(StudentData.SNAME, name); values.put(StudentData.SID, sid); resolver.insert(StudentData.CONTENT_URI, values); } } } }); } // 如果插入的Id已经存在则用提示框进行提示 @Override protected Dialog onCreateDialog(int id, Bundle args) { // // TODO Auto-generated method stub // return super.onCreateDialog(id, args); switch (id) { case DIALOG_IN_USED: return new AlertDialog.Builder(this).setTitle("Fail to add!") .setIcon(R.drawable.fail).setMessage( "This ID:" + sid + " is already in used!") .setPositiveButton("确定", // 特别注意:必须为:new DialogInterface.OnClickListener() // 不能为new // OnClickListener(),否则会跟View.OnClickListener冲突。 new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // TODO Auto-generated method stub } }).create(); default: return super.onCreateDialog(id, args); } } }
【注】:在ContentProvider与ContentResolver当中用到了Uri的形式通常有两种,一种是指定全部数据,另一种是指定某个ID的数据,如:
1、content://contacts/people/:这个Uri指定的就是全部联系人的数据。
2、content://contacts/people/1:这个Uri指定的就是ID为1的联系人的数据。
Uri一般由3部分组成:
1、"content://"
2、要获得数据的一个字符串片段
3、最后就是ID,如果没有ID则返回全部数据。
但因为URI通常比较长,容易出错,所以在Android当中定义了一些辅助类,并定义了一些常量来代替这些这些字符串的使用,如:
Contacts.People.CONTENT_URI:联系人的URI.