Android四大元件之ContentProvider
關於ContentProvider
ContentProvider,被稱為內容提供者,通過Binder向其他元件以及其他應用提供資料。以某種Uri的形式對外提供資料,允許其他應用訪問或修改資料。其他應用程式使用ContentResolver根據Uri去訪問操作指定資料。ContentProvider主要以表格的形勢組織資料,有行列的層次性。另外還支援圖片,視訊等。
建立ContentProvider
- 自定義ContentProvider,該類繼承ContentProvider,實現六個必須需要實現的抽象方法
public class MyContentProvider extents ContentProvider{
/*
* 建立時被呼叫
*/
@Override
public boolean onCreate(){
return false;
}
/*
* 根據Uri查詢符合selection條件的全部記錄,projection是一個列名列表,選擇出指定的資料列,有點sql的select的意思
*/
@Override
public Cursor query(Uri uri,String[] project,String selection,String[] selectionArgs,String sortOrder){
//一般使用SQLiteDatabase進行資料的增刪查改
return null;
}
/*
* 返回Uri查詢出的selection條件的資料的MIME型別
*/
@Override
public String getType(Uri uri){
//當對應多條記錄時 以vnd.android.cursor.dir/開頭
//當對應單條記錄時 以vnd.android.cursor.item/開頭
return null;
}
/*
* 根據Uri插入對應的value的資料
*/
@Override
public Uri insert(Uri uri,ContentValues values){
return null;
}
/*
* 根據Uri刪除selection匹配的記錄
*/
@Override
public int delete(Uri uri,String selection,String[] selectionArgs){
return 0;
}
/*
* 根據Uri修改selection匹配的記錄
*/
@Override
public int update(Uri uri,ContentValues values,String selection,String[] selectionArgs){
return 0;
}
}
- 在AndroidManifest中註冊該ContentProvider
<provider
android:name=".MyContentProvider"
android:authorities="com.xxxx.xxx.MyContentProvider" //唯一標識,通過它外部應用訪問MyContentProvider
android:exported="true" // 屬性用於指示該服務是否能夠被其他應用程式元件呼叫或跟它互動。如果設定為true,則能夠被呼叫或互動,否則不能
android:multiprocess="true" //為true時:多例項 false:單例項
/>
還有一些其他屬性
readPermission:使用Content Provider的查詢功能所必需的許可權,即使用ContentProvider裡的query()函式的許可權;
writePermission:使用ContentProvider的修改功能所必須的許可權,即使用ContentProvider的insert()、update()、delete()函式的許可權;
permission:客戶端讀、寫 Content Provider 中的資料所必需的許可權名稱。readPermission和writePermission屬性優先於本設定。 如果同時設定了readPermission屬性,則其將控制對 Content Provider 的讀取。 如果設定了writePermission屬性,則其也將控制對 Content Provider 資料的修改。也就是說如果只設置permission許可權,那麼擁有這個許可權的應用就可以實現對這裡的ContentProvider進行讀寫;如果同時設定了permission和readPermission那麼具有readPermission許可權的應用才可以讀,擁有permission許可權的才能寫!也就是說只擁有permission許可權是不能讀的,因為readPermission的優先順序要高於permission;如果同時設定了readPermission、writePermission、permission那麼permission就無效了。
android:permission="com.xxx.myPermission"
android:readPermission="com.xxx.myPermission.read"
android:writePermission="com.xxx.myPermission.write"
Uri組成
如: content://com.xxx.xxx.myContentProvider/hello 分為三部分
- content:// :類似網路協議的http://
- com.xxx.xxx.myContentProvider:是指在AndroidManifest中authorities屬性,由該部分找到操作的ContentProvider
- hello:資源部分,訪問不同的資源,動態改變
ContentResolver操作資料
首先ContentResolver到底是什麼,由上面描述可知,ContentProvide的作用是用來暴露資料,而ContentResolver就是用來操作資料
由關係圖可以看出,當應用A想要對B的資料進行增刪查改時,就需要通過ContentResolver來實現。而ContentResolver通過Context提供的 getContentResolver方法獲取。然後通過呼叫ContentResolver的CRUD方法操作資料
- query(Uri uri,String[] project,String selection,String[] selectionArgs,String sortOrder):當應用A呼叫該方法時,相當於呼叫了Uri指向的ContentProvider的query方法
- insert(Uri uri,ContentValues values)
- delete(Uri uri,String selection,String[] selectionArgs)
- public int update(Uri uri,ContentValues values,String selection,String[] selectionArgs)
首先來舉例 查詢 例項:
引數 | 型別 | 對應SQL部分 | 描述 |
---|---|---|---|
uri | Uri | from table_name | 查詢某個應用程式指定表 |
projection | String[] | select column1,column2 | 指定查詢列名 |
selection | String | where column = value | where中約束條件 |
selectionArgs | String[] | 為where中的佔位符提供具體的值 | |
orderBy | String | order by columns1,column2 | 指定排序方式 |
//將內容字串解析成Uri物件
Uri uri = Uri.parse("content://com.xxx.xxx.myContentProvider/hello");
Cursor cursor = getContentResolver().query(uri,project,selection,selectionArgs,sortOrder);
if(cursor!=null){
while(cursor.moveToNext()){
String colum1 =cursor.getString(cursor.getColumnIndex("column1"));
int column2 = cursor.getInt(cursor.getColumnIndex("column2"));
}
cursor.close();
}
新增
ContentValues values = new ContentValues();
values.put("column1","text");
values.put("column2",1);
getContentResolver().insert(uri,values);
修改
ContentValues values = new ContentValues();
values.put("column1","");
getContentResolver().update(uri,values,"column1=? and column2 = ?",new String[]{"text","1"});
//使用selection和selectionArgs對想要更新的資料進行約束
刪除
getContentResolver().delete(uri,values,"column2=?",new String[]{"1"});
//使用selection和selectionArgs對想要更新的資料進行約束
讀取系統ContentProvider
下面展示在實際開發中如何讀取系統的聯絡人。
private void readContacts(){
Cursor cursor = null;
try{
//查詢聯絡人資料
cursor = getContentResolver().query(
ContactsContract.commonDataKinds.Phone.CONTENT_URI,
null,null,null,null);
while(cursor.moveToNext()){
//獲取聯絡人姓名
String Name=cursor.getString(
cursor.getColumnIndex(ContactContract.CommonDataKinds.Phone.DISPLAY_NAME));
//儲存姓名操作
//獲取聯絡人手機號碼
String number = cursor.getString(
cursor.getColumnIndex(ContactContract.CommonDataKinds.Phone.NUMBER));
//儲存手機號碼操作
}
}catch(Exception e){
e.printStackTrace();
}finally{
if(cursor!=null){
cursor.close();
}
}
}
監聽ContentProvider資料變化
- 操作者
在操作ContentResolver時,通過呼叫
getContext().getContentResolver().notifyChange(uri,null);
會通知註冊在該Uri的監聽者,該ContentProvider上的資料發生了變化
- 觀察者
要監聽指定Uri的ContentProvider的資料變化,需要利用ContentObserver。註冊程式碼如下
/**
* Uri 指定uri
* notifyForDescendents :boolean型別,true時,子路徑的資料改變也會通知,false:當前路徑才通知
* MyObserver 繼承ContentObserver,當所監聽資料變化,呼叫onChange()
*/
getContentResolver().registerContentObserver(uri,true,new MyObserver(new Handler()));
//自定義匿名內部監聽類
private final class MyObserver extends ContentObserver{
public MyObserver(Handler handler){
super(handler);
}
public void onChange(boolean selfChange){
//當資料改變時執行相應操作
}
}