ContentProvider讲解
当应用A需要通过contentProvider共享数据时,应按如下顺序进行操作:
一. 定义一个MyProviderextends android.content.ContentProvider并重写里面的onCreate,getType,insert,delete,query,update六个方法。将定义好的MyProvider在manifest.xml中进行声明注册。
1. MyProvider中需要定义一个UriMatcher对象,并在其上注册Provider提供的Uri,返回对应的匹配码,若无匹配,则返回-1;具体如下:
public static UriMatcher uriMatcher; static{ uriMatcher= new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(AUTHORITY,"user", MATCH_DIR);// 添加需要匹配uri,如果匹配就会返回匹配码,没有匹配的,返回-1 uriMatcher.addURI(AUTHORITY,"user/#", MATCH_ITEM);// 如果match()方法匹配content://com.unj.myprovider/user/230路径,返回匹配码为2 }
2.对于重写的增删查改四个方法,sql语句对应的相应方法的参数参照如下:
/** * select id, name from user where age = ? order by name 对应下面的参数为 * projection = new String{"id","name"}; * selection = "age = ?"; * selectionArgs = new String{"23"}; * sortOrder = "name"; */ @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
switch (uriMatcher.match(uri)) { case MATCH_DIR: break; case MATCH_ITEM: long id = ContentUris.parseId(uri); if (selection != null && !"".equals(selection)) { selection += " and "; } selection += " id = " + id; break; default: throw new IllegalArgumentException("Unkonw URI" + uri); }
4. 当增删查改操作使数据发生变化时,可调用如下方法通知调用此ContentProvider的ContentResolver,数据发生了变化。
getContext().getContentResolver().notifyChange(uri, null);//通知,调用此uri的resolver,数据已经发生变化,调用端的ContentObserver会回调onChange方法。
5.如下是我自定义的操纵数据库的一个Provider:MyDBUserProvider.java
package com.androidstudydemo.contentprovider; import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; /** * 当应用需要通过ContentProvider对外共享数据时,第一步需要继承ContentProvider并重写下面六个方法 * 第二步需要在AndroidManifest.xml使用<provider>对该ContentProvider进行配置 * 详细参见:http://www.cnblogs.com/linjiqin/archive/2011/05/28/2061396.html * * @author zkx016 * */ public class MyDBUserProvider extends ContentProvider { public static final String AUTHORITY = "com.unj.myprovider"; public static final String TABLE_NAME = "user"; public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/user"); public static final int MATCH_DIR = 1; public static final int MATCH_ITEM = 2; MyDBHelper myDBHelper; /** * 该ContentProvider所返回的数据类型定义 */ public static final String CONTENT_TYPE_DIR = "vnd.android.cursor.dir/user";// 返回集合类型的数据 public static final String CONTENT_TYPE_ITEM = "vnd.android.cursor.item/user";// 返回非集合类型的数据 /** * UriMatcher类用于匹配Uri,首先第一步把你需要匹配Uri路径全部给注册上, */ public static UriMatcher uriMatcher; static { uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(AUTHORITY, "user", MATCH_DIR);// 添加需要匹配uri,如果匹配就会返回匹配码 uriMatcher.addURI(AUTHORITY, "user/#", MATCH_ITEM);// 如果match()方法匹配content://com.unj.myprovider/person/230路径,返回匹配码为2 } /** * 该方法在ContentProvider创建后就会被调用,Android开机后,ContentProvider在其它应用第一次访问它时才会被创建。 */ @Override public boolean onCreate() { myDBHelper = new MyDBHelper(getContext());// 这里的实现,常见前篇关于Sqlite的文章。 return true; } /** * 该方法用于返回当前Url所代表数据的MIME类型。 * 如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头, * 例如:要得到所有person记录的Uri为content://com.ljq.provider.personprovider/person, * 如果操作的数据不属于集合类型那么返回的MIME类型字符串应该为:"vnd.android.cursor.dir/person"。 * 如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头, */ @Override public String getType(Uri uri) { switch (uriMatcher.match(uri)) {// 进行匹配 case MATCH_DIR: return CONTENT_TYPE_DIR; case MATCH_ITEM: return CONTENT_TYPE_ITEM; default: throw new IllegalArgumentException("Unknown URI" + uri); } } /** * 该方法用于供外部应用往ContentProvider添加数据。 */ @Override public Uri insert(Uri uri, ContentValues values) { int type = uriMatcher.match(uri); if (type != MATCH_DIR) { throw new IllegalArgumentException("Unknown URI:" + uri); } SQLiteDatabase db = myDBHelper.getWritableDatabase(); long rowId = db.insert(TABLE_NAME, null, values);// 返回的是受影响的那一行的行号,出错时返回-1; if (rowId > 0) { /** * withAppendedId(uri, id)用于为路径加上ID部分, * 生成后的Uri为:content://com.unj.myprovider/person/10 */ Uri noteUri = ContentUris.withAppendedId(CONTENT_URI, rowId); /** * 如果ContentProvider的访问者需要知道ContentProvider中的数据发生变化, * 可以在ContentProvider发生数据变化时调用getContentResolver().notifyChange(uri, * null)来通知注册在此URI上的访问者, */ getContext().getContentResolver().notifyChange(uri, null); db.close(); return noteUri; } else { db.close(); throw new IllegalArgumentException("Failed to insert row into" + uri); } } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { SQLiteDatabase db = myDBHelper.getWritableDatabase(); switch (uriMatcher.match(uri)) { case MATCH_DIR: break; case MATCH_ITEM: long id = ContentUris.parseId(uri); if (selection != null && !"".equals(selection)) { selection += " and "; } selection += " id = " + id; break; default: throw new IllegalArgumentException("Unkonw URI" + uri); } int rows = db.delete(TABLE_NAME, selection, selectionArgs); if (rows > 0) { getContext().getContentResolver().notifyChange(uri, null); db.close(); return rows; } else { db.close(); throw new IllegalArgumentException("Failed to delete row in" + uri); } } /** * select id, name from user where age = ? order by name 对应下面的参数为 projection * = new String{"id","name"}; selection = "age = ?"; selectionArgs = new * String{"23"}; sortOrder = "name"; */ @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { switch (uriMatcher.match(uri)) { case MATCH_DIR: // tableName = uri.getLastPathSegment(); break; case MATCH_ITEM: long id = ContentUris.parseId(uri); if (selection != null && !"".equals(selection)) { selection += " and "; } selection += "id = " + id; break; default: throw new IllegalArgumentException("Unkonw URI" + uri); } SQLiteDatabase db = myDBHelper.getReadableDatabase(); Cursor cursor = db.query(TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder); cursor.setNotificationUri(getContext().getContentResolver(), uri);// @1 return cursor; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { switch (uriMatcher.match(uri)) { case MATCH_DIR: break; case MATCH_ITEM: long id = ContentUris.parseId(uri); if (selection != null && !"".equals(selection)) { selection += " and "; } selection += "id = " + id; break; default: throw new IllegalArgumentException("Unkonw URI" + uri); } SQLiteDatabase db = myDBHelper.getWritableDatabase(); int rowsAffected = db.update(TABLE_NAME, values, selection, selectionArgs);//返回受影响的行数 db.close(); if (rowsAffected > 0) { getContext().getContentResolver().notifyChange(CONTENT_URI, null);// @2 return rowsAffected; } return 0; } }
6.MyProvider定义完成之后,需要在manifest.xml中进行Provider的注册,具体在<application></application>内部:
<!-- 自定义contentProvider--> <provider android:name="com.androidstudydemo.contentprovider.MyDBUserProvider" android:authorities="com.unj.myprovider" > </provider>
二. 在其他应用(B)中可以对应的通过Context获得ContentResolver,ContentResolver里面有和ContentProvider相对应的增删查改四个方法,在B应用中直接调用contentResolver.insert()等方法就会获得A应用中提供的数据,B在调用时必须保证A应用同时在运行。
1. 新建一个android project,声明自己需要数据的Uri和authority:
public static final String AUTHORITY = "com.unj.myprovider"; public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/user");
private ContentObserver contentObserver = new ContentObserver(handler) { public void onChange(boolean selfChange, Uri uri) { Toast.makeText(MainActivity.this, "数据发生改变, uri is:" + uri, Toast.LENGTH_SHORT).show(); queryClick(); }; };
3,在应用中通过getContext().getContentResolver() 获得ContentResolver,并注册ContentObserver
<span style="white-space:pre"> </span>resolver = getContentResolver(); /** * 如果ContentProvider的访问者需要得到数据变化通知,必须使用ContentObserver对数据(数据采用uri描述)进行监听 * ,当监听到数据变化通知时,系统就会调用ContentObserver的onChange()方法 */ resolver.registerContentObserver(CONTENT_URI, false, contentObserver);4. 如下是我自定义的Bproject,用来测试上方的Provider:
package com.example.providertest; import android.app.Activity; import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends Activity implements OnClickListener { public static final String AUTHORITY = "com.unj.myprovider"; public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/user"); private ContentResolver resolver; private TextView textView; private Button query; private int id = 0; Handler handler = new Handler(); private ContentObserver contentObserver = new ContentObserver(handler) { public void onChange(boolean selfChange, Uri uri) { Toast.makeText(MainActivity.this, "数据发生改变, uri is:" + uri, Toast.LENGTH_SHORT).show(); queryClick(); }; }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); query = (Button) this.findViewById(R.id.button2); resolver = getContentResolver(); /** * 如果ContentProvider的访问者需要得到数据变化通知,必须使用ContentObserver对数据(数据采用uri描述)进行监听 * ,当监听到数据变化通知时,系统就会调用ContentObserver的onChange()方法 */ resolver.registerContentObserver(CONTENT_URI, false, contentObserver); textView = (TextView) findViewById(R.id.textView1); } public Uri insertClick() { ContentValues values = new ContentValues(); values.put("id", id); values.put("name", "喻文杰" + id); Uri uri = resolver.insert(CONTENT_URI, values); id++; return uri; } public void deleteClick() { Cursor cursor = resolver.query(CONTENT_URI, null, null, null, null); int count = cursor.getColumnCount(); if (count > 0 && cursor.moveToLast()) { int id = resolver.delete(CONTENT_URI, "id = ?", new String[] { cursor.getInt(cursor.getColumnIndex("id")) + "" }); } else { Toast.makeText(MainActivity.this, "删除失败", Toast.LENGTH_SHORT).show(); } } public void updateClick() { Cursor cursor = resolver.query(CONTENT_URI, null, null, null, null); if (cursor.moveToFirst()) { ContentValues values = new ContentValues(); int id = cursor.getInt(cursor.getColumnIndex("id")); values.put("name", "胡锦涛" + id); Uri uri = ContentUris.withAppendedId(CONTENT_URI, id); int result = resolver.update(uri, values, "", null); } if (!cursor.isClosed()) { cursor.close(); } } public void queryClick() { textView.setText(""); Cursor cursor = resolver.query(CONTENT_URI, null, null, null, null); while (cursor.moveToNext()) { textView.append("id : " + cursor.getInt(cursor.getColumnIndex("id")) + " "); textView.append("name : " + cursor.getString(cursor.getColumnIndex("name")) + "\n"); } if (!cursor.isClosed()) { cursor.close(); } textView.append("getAuthority:" + CONTENT_URI.getAuthority() + " \n********\n getEncodedAuthority:" + CONTENT_URI.getEncodedAuthority() + " \n********\n getEncodedFragment:" + CONTENT_URI.getEncodedFragment() + " \n********\ngetEncodedPath: " + CONTENT_URI.getEncodedPath() + " \n********\ngetHost: " + CONTENT_URI.getHost() + " \n********\ngetLastPathSegment: " + CONTENT_URI.getLastPathSegment() + " \n********\ngetPath: " + CONTENT_URI.getPath() + " \n********\ngetPort: " + CONTENT_URI.getPort() + " \n********\ngetQuery: " + CONTENT_URI.getQuery() + " \n********\ngetScheme: " + CONTENT_URI.getScheme() + " \n********\ngetUserInfo: " + CONTENT_URI.getUserInfo() + "\n********\n"); } @Override public void onClick(View arg0) { switch (arg0.getId()) { case R.id.button1:// 增 insertClick(); break; case R.id.button3:// 删 deleteClick(); break; case R.id.button2:// 查 queryClick(); break; case R.id.button4:// 改 updateClick(); break; default: break; } } }