ContentProvider:
在创建ContentProvider时,需要首先使用数据库、文件系统或网络实现底层存储功能,
然后在继承ContentProvider的类中实现基本数据操作的接口函数,包括添加、删除、查找和更新等功能。
调用者不能够直接调用ContentProvider的接口函数,而需要使用ContentResolver对象,
通过URI间接调用ContentProvider。下图是ContentProvider调用关系。
URI:
URI是通用资源标志符(Uniform Resource Identifier),用来定位任何远程或本地的可用资源
ContentProvider使用的URI语法结构如下
content://<authority>/<data_path>/<id>
content://是通用前缀,表示该URI用于ContentProvider定位资源,无需修改。
<authority>是授权者名称,用来确定具体由哪一个ContentProvider提供资源。因此,一般<authority>都由类的小写全称组成,以保证唯一性。
<data_path>是数据路径,用来确定请求的是哪个数据集。
例如:
content://edu.hrbeu.peopleprovider/people/3
可以省略id(/3)部分那么意味着整个数据。
UriMatcher:
在新构造的ContentProvider类中,通过构造一个UriMatcher,判断URI是单条数据还是多条数据。
public void addURI (String authority, String path, int code)
authority表示匹配的授权者名称
path表示数据路径
#可以代表任何数字 (content://<authority>/<data_path>/#)
code表示返回代码(uriMatcher.match(uri))的返回值)
注册ContentProvider :
<provider android:name = ".PeopleProvider" android:authorities = "edu.hrbeu.peopleprovider"/>
实例:
ContentProvider一般用于两个不同的进程之间的数据共享。
- 假设我们有一个新的工程(app),在此工程中创建一个people.db数据库,然后通过自定义了一个ContentProvider来共享数据库中的data。
- 我们可以通过注册provider时用到的authority在配合db path之类的来连接(content://<authority>/<data_path>/#)读取内容提供者(不同进程)的数据。
首先是步骤1:
*只要在AndroidManifast中注册provider就会执行对应的provider类。无需再MainActivity中调用或无需直接调用自定义的new provider。
即系统自动会加载调用PeopleProvider类。
<provider android:name = ".PeopleProvider" android:authorities = "edu.hrbeu.peopleprovider"/>
继承ContentProvider时代码如下:
import android.content.*; import android.database.Cursor; import android.net.Uri; public class PeopleProvider extends ContentProvider{ @Override public int delete(Uri uri, String selection, String[] selectionArgs) { // TODO Auto-generated method stub return 0; } @Override public String getType(Uri uri) { // TODO Auto-generated method stub return null; } @Override public Uri insert(Uri uri, ContentValues values) { // TODO Auto-generated method stub return null; } @Override public boolean onCreate() { // TODO Auto-generated method stub return false; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { // TODO Auto-generated method stub return null; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { // TODO Auto-generated method stub return 0; } }
类似数据库操作。可以这么理解,相对来说好理解。
开始贴代码。。。
package edu.hrbeu.contentproviderdemo; 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.SQLException; 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; public class PeopleProvider extends ContentProvider { private static final String DB_NAME = "people.db"; private static final String DB_TABLE = "peopleinfo"; private static final int DB_VERSION = 1; private SQLiteDatabase db; private DBOpenHelper dbOpenHelper; private static final int MULTIPLE_PEOPLE = 1; private static final int SINGLE_PEOPLE = 2; private static final UriMatcher uriMatcher; static { uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(People.AUTHORITY, People.PATH_MULTIPLE, MULTIPLE_PEOPLE); uriMatcher.addURI(People.AUTHORITY, People.PATH_SINGLE, SINGLE_PEOPLE); } @Override public boolean onCreate() { // TODO Auto-generated method stub Context context = getContext(); dbOpenHelper = new DBOpenHelper(context, DB_NAME, null, DB_VERSION); db = dbOpenHelper.getWritableDatabase(); if (db == null) return false; else 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(); qb.setTables(DB_TABLE); switch(uriMatcher.match(uri)){ case SINGLE_PEOPLE: qb.appendWhere(People.KEY_ID + "=" + uri.getPathSegments().get(1)); break; default: break; } Cursor cursor = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder); cursor.setNotificationUri(getContext().getContentResolver(), uri); return cursor; } @Override public String getType(Uri uri) { // TODO Auto-generated method stub switch(uriMatcher.match(uri)){ case MULTIPLE_PEOPLE: return People.MINE_TYPE_MULTIPLE; case SINGLE_PEOPLE: return People.MINE_TYPE_SINGLE; default: throw new IllegalArgumentException("Unkown uri:"+uri); } } @Override public Uri insert(Uri uri, ContentValues values) { // TODO Auto-generated method stub long id = db.insert(DB_TABLE, null, values); if ( id > 0 ){ Uri newUri = ContentUris.withAppendedId(People.CONTENT_URI, id); getContext().getContentResolver().notifyChange(newUri, null); return newUri; } throw new SQLException("Failed to insert row into " + uri); } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { // TODO Auto-generated method stub int count = 0; switch(uriMatcher.match(uri)){ case MULTIPLE_PEOPLE: count = db.delete(DB_TABLE, selection, selectionArgs); break; case SINGLE_PEOPLE: String segment = uri.getPathSegments().get(1); count = db.delete(DB_TABLE, People.KEY_ID + "=" + segment, selectionArgs); break; default: throw new IllegalArgumentException("Unsupported URI:" + uri); } getContext().getContentResolver().notifyChange(uri, null); return count; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { // TODO Auto-generated method stub int count; switch(uriMatcher.match(uri)){ case MULTIPLE_PEOPLE: count = db.update(DB_TABLE, values, selection, selectionArgs); break; case SINGLE_PEOPLE: String segment = uri.getPathSegments().get(1); count = db.update(DB_TABLE, values, People.KEY_ID+"="+segment, selectionArgs); break; default: throw new IllegalArgumentException("Unknow URI:" + uri); } getContext().getContentResolver().notifyChange(uri, null); return count; } private static class DBOpenHelper extends SQLiteOpenHelper { public DBOpenHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); } private static final String DB_CREATE = "create table " + DB_TABLE + " (" + People.KEY_ID + " integer primary key autoincrement, " + People.KEY_NAME+ " text not null, " + People.KEY_AGE+ " integer," + People.KEY_HEIGHT + " float);"; @Override public void onCreate(SQLiteDatabase _db) { // TODO Auto-generated method stub _db.execSQL(DB_CREATE); } @Override public void onUpgrade(SQLiteDatabase _db, int oldVersion, int newVersion) { // TODO Auto-generated method stub _db.execSQL("DROP TABLE IF EXISTS " + DB_TABLE); onCreate(_db); } } }
package edu.hrbeu.contentproviderdemo; import android.net.Uri; public class People{ public static final String MIME_DIR_PREFIX = "vnd.android.cursor.dir"; public static final String MIME_ITEM_PREFIX = "vnd.android.cursor.item"; public static final String MINE_ITEM = "vnd.hrbeu.people"; public static final String MINE_TYPE_SINGLE = MIME_ITEM_PREFIX + "/" + MINE_ITEM; public static final String MINE_TYPE_MULTIPLE = MIME_DIR_PREFIX + "/" + MINE_ITEM; public static final String AUTHORITY = "edu.hrbeu.peopleprovider"; public static final String PATH_SINGLE = "people/#"; public static final String PATH_MULTIPLE = "people"; public static final String CONTENT_URI_STRING = "content://" + AUTHORITY + "/" + PATH_MULTIPLE; public static final Uri CONTENT_URI = Uri.parse(CONTENT_URI_STRING); public static final String KEY_ID = "_id"; public static final String KEY_NAME = "name"; public static final String KEY_AGE = "age"; public static final String KEY_HEIGHT = "height"; }
MainActivity是空的。。。不贴出来了。
运行结果没有任何内容,因为仅是添加了一个provider。
看一下步骤2:
在另一个App中调用此ContentProvider。
无需再AndroidManifast中注册provider。
只需对应步骤1中的People类里的静态变量是完全匹配的就是可以了。
也就是说URI需要与我们自定义的ContentProvider保持一致。
package edu.hrbeu.contentresolverdemo; import android.net.Uri; public class People{ public static final String MIME_DIR_PREFIX = "vnd.android.cursor.dir"; public static final String MIME_ITEM_PREFIX = "vnd.android.cursor.item"; public static final String MINE_ITEM = "vnd.hrbeu.people"; public static final String MINE_TYPE_SINGLE = MIME_ITEM_PREFIX + "/" + MINE_ITEM; public static final String MINE_TYPE_MULTIPLE = MIME_DIR_PREFIX + "/" + MINE_ITEM; public static final String AUTHORITY = "edu.hrbeu.peopleprovider"; public static final String PATH_SINGLE = "people/#"; public static final String PATH_MULTIPLE = "people"; public static final String CONTENT_URI_STRING = "content://" + AUTHORITY + "/" + PATH_MULTIPLE; public static final Uri CONTENT_URI = Uri.parse(CONTENT_URI_STRING); public static final String KEY_ID = "_id"; public static final String KEY_NAME = "name"; public static final String KEY_AGE = "age"; public static final String KEY_HEIGHT = "height"; }
操作Contentpriver提供的数据我们需要用到ContentResolver:
package edu.hrbeu.contentresolverdemo; import android.app.Activity; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.content.ContentResolver; import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; public class ContentResolverDemo extends Activity { private EditText nameText; private EditText ageText; private EditText heightText; private EditText idEntry; private TextView labelView; private TextView displayView; private ContentResolver resolver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_content_resolver_demo); nameText = (EditText) findViewById(R.id.name); ageText = (EditText) findViewById(R.id.age); heightText = (EditText) findViewById(R.id.hight); idEntry = (EditText) findViewById(R.id.id); labelView = (TextView) findViewById(R.id.label); displayView = (TextView) findViewById(R.id.display); Button addButton = (Button) findViewById(R.id.button1); Button queryAllButton = (Button) findViewById(R.id.button2); Button clearButton = (Button) findViewById(R.id.button3); Button queryButton = (Button) findViewById(R.id.button6); Button deleteButton = (Button) findViewById(R.id.button5); Button updateButton = (Button) findViewById(R.id.button7); resolver = this.getContentResolver(); addButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub ContentValues values = new ContentValues(); values.put(People.KEY_NAME, nameText.getText().toString()); values.put(People.KEY_AGE, Integer.parseInt(ageText.getText().toString())); values.put(People.KEY_HEIGHT, Float.parseFloat(heightText.getText().toString())); Uri newUri = resolver.insert(People.CONTENT_URI, values); labelView.setText("添加成功,URI:" + newUri); } }); queryAllButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub Cursor cursor = resolver.query(People.CONTENT_URI, new String[] { People.KEY_ID, People.KEY_NAME, People.KEY_AGE, People.KEY_HEIGHT }, null, null, null); if (cursor == null) { labelView.setText("数据库中没有数据"); return; } labelView.setText("数据库:" + String.valueOf(cursor.getCount()) + "条记录"); String msg = ""; if (cursor.moveToFirst()) { do { msg += "ID:" + cursor.getInt(cursor .getColumnIndex(People.KEY_ID)) + ","; msg += "姓名:" + cursor.getString(cursor .getColumnIndex(People.KEY_NAME)) + ","; msg += "年龄:" + cursor.getInt(cursor .getColumnIndex(People.KEY_AGE)) + ", "; msg += "身高:" + cursor.getFloat(cursor .getColumnIndex(People.KEY_HEIGHT)) + " "; } while (cursor.moveToNext()); } displayView.setText(msg); } }); clearButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub displayView.setText(""); } }); queryButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub Uri uri = Uri.parse(People.CONTENT_URI_STRING + "/" + idEntry.getText().toString()); Cursor cursor = resolver.query(uri, new String[] { People.KEY_ID, People.KEY_NAME, People.KEY_AGE, People.KEY_HEIGHT }, null, null, null); if (cursor == null) { labelView.setText("数据库中没有数据"); return; } String msg = ""; if (cursor.moveToFirst()) { msg += "ID:" + cursor.getInt(cursor .getColumnIndex(People.KEY_ID)) + ","; msg += "姓名:" + cursor.getString(cursor .getColumnIndex(People.KEY_NAME)) + ","; msg += "年龄:" + cursor.getInt(cursor .getColumnIndex(People.KEY_AGE)) + ", "; msg += "身高:" + cursor.getFloat(cursor .getColumnIndex(People.KEY_HEIGHT)) + " "; } labelView.setText("数据库:"); displayView.setText(msg); } }); deleteButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub Uri uri = Uri.parse(People.CONTENT_URI_STRING + "/" + idEntry.getText().toString()); int result = resolver.delete(uri, null, null); String msg = "删除ID为" + idEntry.getText().toString() + "的数据" + (result > 0 ? "成功" : "失败"); labelView.setText(msg); } }); updateButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub ContentValues values = new ContentValues(); values.put(People.KEY_NAME, nameText.getText().toString()); values.put(People.KEY_AGE, Integer.parseInt(ageText.getText().toString())); values.put(People.KEY_HEIGHT, Float.parseFloat(heightText.getText().toString())); Uri uri = Uri.parse(People.CONTENT_URI_STRING + "/" + idEntry.getText().toString()); int result = resolver.update(uri, values, null, null); String msg = "更新ID为" + idEntry.getText().toString() + "的数据" + (result > 0 ? "成功" : "失败"); labelView.setText(msg); } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.content_resolver_demo, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } }
事成相识的布局文件也附上:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="edu.hrbeu.contentresolverdemo.ContentResolverDemo" > <TextView android:id="@+id/Username" android:layout_height="wrap_content" android:layout_width="fill_parent" android:text="用户名:"> </TextView> <EditText android:id="@+id/name" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_below="@id/Username" > </EditText> <TextView android:id="@+id/Userage" android:layout_height="wrap_content" android:layout_width="fill_parent" android:layout_below="@id/name" android:text="年龄:"> </TextView> <EditText android:id="@+id/age" android:layout_height="wrap_content" android:layout_width="fill_parent" android:layout_below="@id/Userage"> </EditText> <TextView android:id="@+id/Userhight" android:layout_height="wrap_content" android:layout_width="fill_parent" android:layout_below="@id/age" android:text="身高:"> </TextView> <EditText android:id="@+id/hight" android:layout_height="wrap_content" android:layout_width="fill_parent" android:layout_below="@id/Userhight"> </EditText> <Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBaseline="@+id/button1" android:layout_alignBottom="@+id/button1" android:layout_centerHorizontal="true" android:text="全部显示" /> <Button android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBaseline="@+id/button2" android:layout_alignBottom="@+id/button2" android:layout_alignRight="@+id/hight" android:text="清除显示" /> <TextView android:id="@+id/Userid" android:layout_height="wrap_content" android:layout_width="fill_parent" android:layout_below="@id/button1" android:text="ID:"> </TextView> <EditText android:id="@+id/id" android:layout_height="wrap_content" android:layout_width="fill_parent" android:layout_below="@id/Userid"> </EditText> <Button android:id="@+id/button6" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBaseline="@+id/button5" android:layout_alignBottom="@+id/button5" android:layout_alignLeft="@+id/button2" android:text="ID查询" /> <Button android:id="@+id/button7" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBaseline="@+id/button6" android:layout_alignBottom="@+id/button6" android:layout_alignLeft="@+id/button3" android:text="ID更新" /> <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/hight" android:text="添加数据" /> <Button android:id="@+id/button5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/id" android:layout_below="@+id/id" android:text="ID删除" /> <TextView android:id="@+id/display" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignLeft="@+id/label" android:layout_below="@+id/label" android:layout_marginTop="23dp" /> <TextView android:id="@+id/label" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignLeft="@+id/button5" android:layout_below="@+id/button6" android:layout_marginTop="14dp" /> </RelativeLayout>
运行结果: