ContentProvider簡介:
ContentProvider(數據提供者)是在應用程序間共享數據的一種接口機制
ContentProvider提供瞭更為高級的數據共享方法,應用程序可以指定需要共享的數據,而其他應用程序則可以在不知數據來源、路徑的情況下,對共享數據進行查詢、添加、刪除和更新等操作
許多Android系統的內置數據也通過ContentProvider提供給用戶使用,例如通訊錄、音視頻文件和圖像文件等
在創建ContentProvider時,需要首先使用數據庫、文件系統或網絡實現底層存儲功能,然後在繼承ContentProvider的類中實現基本數據操作的接口函數,包括添加、刪除、查找和更新等功能
調用者不能夠直接調用ContentProvider的接口函數,而需要使用ContentResolver對象,通過URI間接調用ContentProvider。
使用ContentProvider可以在不同的應用程序之間共享數據。 它為存儲和獲取數據提供瞭統一的接口。
ContentProvide對數據進行封裝,不用關心數據存儲的細節。
Android系統提供瞭大量的ContentProvider,允許開發者來操作這些contentprovider所暴露的接口。
系統包:https://developer.android.com/intl/zh-cn/reference/android/provider/package-summary.html
聯系人管理的Uri:vc3Ryb25nPjwvcD4KPHA+Q29udGFjdHNDb250cmFjdC5Db250YWN0cy5DT05URU5UX1VSSTq53MDtwarPtcjLPGJyPgpDb250YWN0c0NvbnRyYWN0LkNvbW1vbkRhdGFLaW5kcy5QaG9uZS5DT05URU5UX1VSSTq53MDtwarPtcjLtcS157uwPGJyPgpDb250YWN0c0NvbnRyYWN0LkNvbW1vbkRhdGFLaW5kcy5FbWFpbC5DT05URU5UX1VSSTq53MDtwarPtcjLtcRFLW1haWwuPC9wPgo8cD48c3Ryb25nPrbgw73M5cTayN253MDttcRVcmk6PC9zdHJvbmc+PC9wPgo8cD5NZWRpYVN0b3JlLkF1ZGlvLk1lZGlhLkVYVEVSTkFMX0NPTlRFTlRfVVJJOs3isr+05rSixvfJz7XE0vTGtc7EvP48YnI+Ck1lZGlhU3RvcmUuQXVkaW8uTWVkaWEuSU5URVJOQUxfQ09OVEVOVF9VUkk6xNqyv7TmtKLG98nPtcTS9Ma1zsS8/jxicj4KTWVkaWFTdG9yZS5JbWFnZXMuTWVkaWEuRVhURVJOQUxfQ09OVEVOVF9VUkk6zeKyv7TmtKLG98nPtcTNvMaszsS8/jxicj4KTWVkaWFTdG9yZS5JbWFnZXMuTWVkaWEuSU5URVJOQUxfQ09OVEVOVF9VUkk6xNqyv7TmtKLG98nPtcTNvMaszsS8/jxicj4KTWVkaWFTdG9yZS5WaWRlby5NZWRpYS5FWFRFUk5BTF9DT05URU5UX1VSSTrN4rK/tOa0osb3yc+1xMrTxrXOxLz+PGJyPgpNZWRpYVN0b3JlLlZpZGVvLk1lZGlhLklOVEVSTkFMX0NPTlRFTlRfVVJJOsTasr+05rSixvfJz7XEytPGtc7EvP48YnI+CjwvcD4KPHA+PGJyPgo8L3A+CjxwPjxzdHJvbmc+VVJJtcS88r3po7o8L3N0cm9uZz48YnI+CjwvcD4KPHA+VXJptPqx7cHL0qqy2df3tcTK/b7do6zL/M6qz7XNs7XEw7/Su7j218rUtLj4xuTSu7j2w/vX1qOssci3vcu1zai7sLzHwryho8O/0ru49kNvbnRlbnRQcm92aWRlcra807XT0NK7uPa5q7mytcRVUkmjrNXiuPZVUknTw9Pase3KvtXiuPZDb250ZW50UHJvdmlkZXLL+czhuam1xMr9vt2hozxicj4KPC9wPgo8cD48YnI+CjwvcD4KPHA+PHN0cm9uZz5VUkm1xCYjMjY2ODQ7yr0gOjwvc3Ryb25nPjwvcD4KPHA+PHN0cm9uZz48aW1nIHNyYz0=”/uploadfile/Collfiles/20140404/2014040408455539.jpg” alt=”\”>
A:標準前綴,用來說明一個Content Provider控制這些數據,無法改變的;”content://”
B:URI 的標識,它定義瞭是哪個Content Provider提供這些數據。對於第三方應用程序,為瞭保證URI標識的唯一性,它必須是一個完整的、小寫的類名。這個標識在元素的 authorities屬性中說明:一般是定義該ContentProvider的包.類的名稱; “content://hx.android.text.myprovider”,這個部分就是ContentProvider的authority.
C:路徑,通俗的講就是你要操作的數據庫中表的名字;”content://hx.android.text.myprovider/tablename”
D:如果URI中包含表示需要獲取的記錄的ID;則就返回該id對應的數據,如果沒有ID,就表示返回全部; “content://hx.android.text.myprovider/tablename/#” #表示數據id,動態改變
註意:
路徑(path):可以用來表示我們要操作的數據,路徑的構建應根據業務而定,如下: ? 要操作contact表中id為10的記錄,可以構建這樣的路徑:/contact/10 ? 要操作contact表中id為10的記錄的name字段, contact/10/name ? 要操作contact表中的所有記錄,可以構建這樣的路徑:/contact
要操作的數據不一定來自數據庫,也可以是文件等他存儲方式,如下: 要操作xml文件中contact節點下的name節點,可以構建這樣的路徑:/contact/name
如果要把一個字符串轉換成Uri,可以使用Uri類中的parse()方法,如下: Uri uri = Uri.parse(“content://com.changcheng.provider.contactprovider/contact”)
工具類:UriMatcher :
為瞭確定該ContentProvider實際能處理的Uri,以及確定每個方法中Uri參數所操作的數據,Android提供瞭UriMacher工具類,用法如下:
1.首先把你需要匹配Uri路徑全部給註冊上(便於ContentProvider判斷具體的操作,一般是確定對單個記錄操作還是多個記錄操作),如:
uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact”, 1);
uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact/#”, 2);
void addUri(String authority,String path,int code):該方法用於向UriMacher對象註冊Uri.其中authority和path組合成一個Uri,而code則代表對應的標識碼。標識碼匹配Uri的類型,需要註冊多少個Uri則根據業務需要。
2.註冊完需要匹配的Uri後,就可以使用uriMatcher.match(uri)方法對輸入的Uri進行匹配,如果匹配就返回匹配碼.
//如果match()匹配路徑,返回匹配碼為1
content://com.changcheng.sqlite.provider.contactprovider/contact
//如果match()匹配路徑,返回匹配碼為2
content://com.changcheng.sqlite.provider.contactprovider/contact/23
int match(Uri uri):根據前面註冊的Uri來判斷指定Uri對應的標識碼。如果找不到匹配的標識碼,該方法將會返回-1。
用來解析來自ContentResolver傳遞過來的Uri則能知道這個Uri具體操作的類型,由uriMatcher.addURI指定,則根據返回的標識碼就知道瞭要操作的類型(一般是確定對單個記錄操作還是多個記錄操作)。
工具類:ContentUris :
ContentUris類用於操作Uri字符串的工具類,它有兩個比較實用的方法:
withAppendedId(uri, id)用於為路徑加上ID部分:
Uri uri =Uri.parse(“content://cn.itcast.provider.personprovider/person”)
Uri resultUri = ContentUris.withAppendedId(uri, 10);
//生成後的Uri為:content://cn.itcast.provider.personprovider/person/10
parseId(uri)方法用於從路徑中獲取ID部分:
Uri uri = Uri.parse(“content://cn.itcast.provider.personprovider/person/10”)
long personid = ContentUris.parseId(uri);//獲取的結果為:10
ContentProvider的編程方法 :
程序開發人員通過繼承ContentProvider類可以創建一個新的數據提供者,過程可以分為三步 :
繼承ContentProvider,並重載六個函數
delete():刪除數據集,返回被刪除的記錄條數
insert():添加數據集,返回新插入的記錄的Uri
qurey():查詢數據集,返回查詢得到的Cursor
update():更新數據集,返回被更新的記錄條數
onCreate():初始化底層數據集和建立數據連接等工作
getType():返回指定URI的MIME數據類型,
如果URI是單條數據,則返回的MIME數據類型應以vnd.android.cursor.item開頭
如果URI是多條數據,則返回的MIME數據類型應以vnd.android.cursor.dir/開頭
聲明CONTENT_URI,實現UriMatcher
註冊ContentProvider
屬性:
name:ContentProvider的實現類的類名
authorities:指定該ContentProvider對應的Uri(域名)
android:exported:指定該ContentProvider是否允許其他應用調用
ContentResolver的編程方法:
使用ContentProvider是通過Android組件都具有的ContentResolver對象,通過URI進行數據操作
程序開發人員隻需要知道URI和數據集的數據格式,則可以進行數據操作,解決不同應用程序之間的數據共享問題
每個Android組件都具有一個ContentResolver對象,獲取ContentResolver對象的方法是調用getContentResolver()函數
ContentResolver resolver = getContentResolver();
調用ContentResolver的如下方法:
insert(Uri url,ContentValues values):
delete(Uri url,String where String[] selectionArgs):
update(Uri uri,ContentValues values,String where,String[] selectionArgs):
query(Uri uri,String[] projection,String selection,String[] selectionArgs,String sortOrder):
實例代碼如下:
ContentProviderDemo:
工具類:(用來暴露給使用者,用作說明文檔)
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"; }
ContentProvider:
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.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; import android.database.sqlite.SQLiteDatabase.CursorFactory; 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 String getType(Uri uri) { 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 boolean onCreate() { 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 Uri insert(Uri uri, ContentValues values) { 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) { 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 Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { 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 int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 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) { _db.execSQL(DB_CREATE); } @Override public void onUpgrade(SQLiteDatabase _db, int _oldVersion, int _newVersion) { _db.execSQL("DROP TABLE IF EXISTS " + DB_TABLE); onCreate(_db); } } }
AndroidManifest.xml:
ContentResolverDemo:
工具類:
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"; }
ContentResolver:
package edu.hrbeu.ContentResolverDemo; import android.app.Activity; import android.content.ContentResolver; import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; 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 public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); nameText = (EditText)findViewById(R.id.name); ageText = (EditText)findViewById(R.id.age); heightText = (EditText)findViewById(R.id.height); idEntry = (EditText)findViewById(R.id.id_entry); labelView = (TextView)findViewById(R.id.label); displayView = (TextView)findViewById(R.id.display); Button addButton = (Button)findViewById(R.id.add); Button queryAllButton = (Button)findViewById(R.id.query_all); Button clearButton = (Button)findViewById(R.id.clear); Button deleteAllButton = (Button)findViewById(R.id.delete_all); Button queryButton = (Button)findViewById(R.id.query); Button deleteButton = (Button)findViewById(R.id.delete); Button updateButton = (Button)findViewById(R.id.update); addButton.setOnClickListener(addButtonListener); queryAllButton.setOnClickListener(queryAllButtonListener); clearButton.setOnClickListener(clearButtonListener); deleteAllButton.setOnClickListener(deleteAllButtonListener); queryButton.setOnClickListener(queryButtonListener); deleteButton.setOnClickListener(deleteButtonListener); updateButton.setOnClickListener(updateButtonListener); resolver = this.getContentResolver(); } OnClickListener addButtonListener = new OnClickListener() { @Override public void onClick(View v) { 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); } }; OnClickListener queryAllButtonListener = new OnClickListener() { @Override public void onClick(View v) { 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); } }; OnClickListener clearButtonListener = new OnClickListener() { @Override public void onClick(View v) { displayView.setText(""); } }; OnClickListener deleteAllButtonListener = new OnClickListener() { @Override public void onClick(View v) { resolver.delete(People.CONTENT_URI, null, null); String msg = "數據全部刪除" ; labelView.setText(msg); } }; OnClickListener queryButtonListener = new OnClickListener() { @Override public void onClick(View v) { 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); } }; OnClickListener deleteButtonListener = new OnClickListener() { @Override public void onClick(View v) { 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); } }; OnClickListener updateButtonListener = new OnClickListener() { @Override public void onClick(View v) { 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); } }; }