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)) + "\n"; } 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)) + "\n"; } 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>
運行結果: