Android4開發入門經典 之 第九部分:Content Provider【私塾線上原創】
Content Provider基礎知識
http://sishuok.com/forum/blogPost/list/0/2749.html#7733
Content Provider介紹
Android中的Content Provider機制可以支援在多個應用中儲存和讀取資料。這也是跨應用共享資料的唯一方式。在android系統中,沒有一個公共的記憶體區域,供多個應用共享儲存資料,要在多個應用中共享資料,就需要使用Content Provider。
Android提供了一些常用資料型別的Contentprovider,比如音訊、視訊、圖片和私人通訊錄等。可在android.provider包下面找到一些android提供的Content Provider。
公開應用私有資料的兩種方式
1:建立自己的Content Provider,需要繼承ContentProvider類,讓其他應用來訪問自己的Content Provider。
2:把自己的資料通過Content Provider新增到其他應用中去,這樣所有的應用都可以通過那個Content Provider來訪問這些資料。
所有Content Provider都需要實現相同的介面,通過這個介面來進行資料的增加、修改、刪除和查詢的功能。
要使用Content Provider是非常簡單的,只需要獲得ContentResolver物件,然後通過這個物件進行資料的CRUD操作。獲得ContentResolver的方式如下:ContentResolver cr = getContentResolver();
Android系統負責初始化所有的Content Provider,不需要使用者自己去建立。實際上,Content Provider的使用者都不可能直接訪問到Content Provider例項,只能通過ContentResolver在中間代理。
Content Provider展示資料類似一個資料庫的表。其中:每行有個值唯一的數字欄位,名為_ID,可用於對錶中指定記錄的定位;Content Provider返回的資料結構,類似JDBC的ResultSet,在Android中,是Cursor物件。
理解URI
1:每個Content Provider定義一個唯一的公開的URI,用於指定到它的資料集。一個Content Provider可以包含多個資料集,這樣,就需要有多個URI與每個資料集相對應。
2:URI的格式,標準的格式分成了四個部分,示例如下:
content:// cn.javass.users /students /12
(1)content://:標準字首,用來說明一個Content Provider控制這些資料
(2)cn.javass.users:URI的標識,它定義了是哪個Content Provider的實現來提供這些資料。為了保證URI標識的唯一性,它必須是一個完整的、小寫的、ContentProvider實現類名。這個標識在 元素的 authorities屬性中說明:
(3)/students :路徑,Content Provider用來確定當前需要什麼型別的資料,URI中可能包括0到多個路徑
(4)12:具體某條資料的標識,如果URI中包含,表示需要獲取的記錄的ID;如果沒有ID,就表示返回全部。
ContentResolver的使用
1:ContentResolver通過URI來操作ContentProvider提供的資料。因此你必須知道要運算元據的URI,除此之外,還必須知道要操作的資料段的名稱,以及此資料段的資料型別。如果你想要獲取一個特定的記錄,你還必須知道此記錄的ID
2: ContentResolver的常用方法是完全類似於資料庫操作的,如下:
(1)新增:insert(Uri url, ContentValues values),返回Uri
(2)刪除:delete(Uri url, String where, String[] selectionArgs),返回操作的記錄條數
(3)修改:update(Uri uri, ContentValues values, String where, String[] selectionArgs) ,返回操作的記錄條數
(4)查詢:query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder),返回Cursor
建立自己的Content Provider
UriMatcher:用於匹配Uri,基本用法如下:
1:註冊能匹配的Uri
(1)常量UriMatcher.NO_MATCH表示不匹配任何路徑的返回碼(-1)。
UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
(2)如果match()方法匹配某個路徑,設定一個返回的值。
例如匹配content://com.android.calendar/calendars路徑,返回匹配碼為1。uriMatcher.addURI(“content://com.android.calendar”, “calendars”, 1);
(3)如果match()方法匹配某個URI,設定一個返回的值。例如匹配
content://com.android.calendar/calendars/11這個URI,返回匹配碼為2。uriMatcher.addURI(“content://com.android.calendar”, “calendars/#”, 2);
2:註冊完需要匹配的Uri後,就可以使用uriMatcher.match(uri)方法對輸入的Uri進行匹配,如果匹 配就返回匹配碼。
ContentUris:用於操作Uri路徑後面的ID部分,它有兩個比較實用的方法:
1:withAppendedId(uri, id)用於為路徑加上ID部分
2:parseId(uri)方法用於從路徑中獲取ID部分
建立自己的Content Provider,基本步驟如下:
1:寫一個類繼承ContentProvider,就需要實現相應的方法
2: Content Provider通常需要對外提供:CONTENT_URI、URI_AUTHORITY,對外的資料欄位常量等,例如:
java程式碼:
public static final String URI_AUTHORITY = "cn.javass.mycp"; public static final String URI_PATH = "Users"; public static final String URI_PATH2 = "Users/#"; public static final Uri CONTENT_URI = Uri.parse("content://" + URI_AUTHORITY + "/" + URI_PATH); //對外的資料欄位 public static final String COLUMN_UUID = "uuid"; public static final String COLUMN_NAME = "name";
3:提供UriMatcher,用來判斷外部傳入的Uri是否帶有id,好區分處理:
java程式碼:
public static final int ALL_RECORDS = 1; public static final int SINGLE_RECORD = 2; public static UriMatcher sMatcher = null; static { sMatcher = new UriMatcher(UriMatcher.NO_MATCH); sMatcher.addURI(URI_AUTHORITY, URI_PATH, ALL_RECORDS); sMatcher.addURI(URI_AUTHORITY, URI_PATH2, SINGLE_RECORD); }
然後就是根據自己儲存資料的具體實現,來實現Content Provider的方法,這裡以前面SQLite的示例來演示如何實現這些方法。
新增功能的簡單實現
java程式碼:
public Uri insert(Uri uri, ContentValues values) { Uri retUri = null; if(sMatcher.match(uri)==ALL_RECORDS){ //判斷是否需要處理,只有符合的才處理 SQLiteDatabase db = dh.getWritableDatabase(); long id = db.insert("tbl_user",null, values); retUri = ContentUris.withAppendedId(uri, id); } return retUri; }
查詢功能的簡單實現
java程式碼:
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { if(sMatcher.match(uri)==ALL_RECORDS){ SQLiteDatabase db = dh.getWritableDatabase(); Cursor c = db.query("tbl_user", projection, selection,selectionArgs, "", "", sortOrder,""); return c; }else if(sMatcher.match(uri)==SINGLE_RECORD){ //這裡應該處理帶id的uri,省略了..... } return null; }
修改功能的簡單實現
java程式碼:
public int update(Uri uri, ContentValues values, String selection,String[] selectionArgs) { int ret = 0; if(sMatcher.match(uri)==ALL_RECORDS){ //判斷是否需要處理,只有符合的才處理 SQLiteDatabase db = dh.getWritableDatabase(); ret = db.update("tbl_user",values,selection, selectionArgs); }else if(sMatcher.match(uri)==SINGLE_RECORD){//這裡應該處理帶id的uri,省略了.....} return ret; }
刪除功能的簡單實現
java程式碼:
public int delete(Uri uri, String selection, String[] selectionArgs) { int ret = 0; if(sMatcher.match(uri)==ALL_RECORDS){ //判斷是否需要處理,只有符合的才處理 SQLiteDatabase db = dh.getWritableDatabase(); ret = db.delete("tbl_user", selection, selectionArgs); }else if(sMatcher.match(uri)==SINGLE_RECORD){//這裡應該處理帶id的uri,省略了.....} return ret; }
getType的簡單實現,getType方法返回資料的MIME type
java程式碼:
public String getType(Uri uri) { switch (sMatcher.match(uri)) { case ALL_RECORDS: return "vnd.android.cursor.dir/vnd.cn.javass.users"; case SINGLE_RECORD: return "vnd.android.cursor.item/vnd.cn.javass.users"; default: throw new IllegalArgumentException("unknown URI " + uri); } }
onCreate方法的簡單實現
java程式碼:
public boolean onCreate() { if (mContext == null) {mContext = this.getContext();} if(dh==null){ dh = new DBHelper(mContext,"testDB1",null,1); } return true; }
使用自己的Content Provider
有了自己實現的Content Provider後,要使用就很簡單了。首先要在AndroidManifest.xml檔案中註冊自己的Content Provider,示例如下:
java程式碼:
使用新增的功能,先示範單條新增:
java程式碼:
ContentValues values = new ContentValues(); values.put(MyCP.COLUMN_UUID, "test1"); values.put(MyCP.COLUMN_NAME,"cc1"); Uri uri = getContentResolver().insert(MyCP.CONTENT_URI, values);
還可以使用批處理的方式來使用新增的功能,示例如下:
java程式碼:
ArrayListps = new ArrayList (); ops.add(ContentProviderOperation.newInsert(MyCP.CONTENT_URI) .withValue(MyCP.COLUMN_UUID, "test2") .withValue(MyCP.COLUMN_NAME, "cc2") .build()); ops.add(ContentProviderOperation.newInsert(MyCP.CONTENT_URI) .withValue(MyCP.COLUMN_UUID, "test3") .withValue(MyCP.COLUMN_NAME, "cc3") .build()); try { getContentResolver().applyBatch(MyCP.URI_AUTHORITY,ops); } catch (Exception e) { e.printStackTrace(); }
使用查詢的功能:
java程式碼:
Cursor c = getContentResolver().query(MyCP.CONTENT_URI, new String[] {MyCP.COLUMN_UUID, MyCP.COLUMN_NAME}, null,null, null); while (c.moveToNext()) { String dId = ""+c.getString(c.getColumnIndex(MyCP.COLUMN_UUID)); String name = c.getString(c.getColumnIndex(MyCP.COLUMN_NAME)); Log.i("now query","dataId="+dId+",name="+name); }
使用修改的功能,這裡只示例單條的處理,批處理的方式參見前面新增的實現:
java程式碼:
ContentValues values = new ContentValues(); values.put(MyCP.COLUMN_UUID, "test1"); values.put(MyCP.COLUMN_NAME,"cc1update"); getContentResolver().update(MyCP.CONTENT_URI, values,MyCP.COLUMN_UUID+"=?",new String[]{"test1"});
使用刪除的功能:
java程式碼:
getContentResolver().delete(MyCP.CONTENT_URI, MyCP.COLUMN_UUID + "=?",new String[] { "test2" });
幾點說明
1:這裡建立的自己的Content Provider是非常簡單的,主要是沿用了前面SQLite的示例,當然也可以使用檔案操作的示例
2:一般來說,提供Content Provider的資料,應該有一個long型的id欄位,由於前面的示例沒有,所以上面的示例沒有提供
3:在自己的Content Provider實現裡面,應該根據Uri是否帶有id的情況進行相應的處理,為了示例的簡單,就沒有那麼實現了
4:由於Content Provider可能被多個應用同時使用,因此需要在實現Content Provider的時候進行多執行緒控制,目前並沒有實現這樣的功能
5:處理多執行緒的一個好方式就是,當資料發生改變的時候,通知所有相關的資料改變,比如在新增、修改、刪除方法裡面提供:
java程式碼:
getContext().getContentResolver().notifyChange(Uri, null);
操作通訊錄
要使用其他應用提供的Content Provider是非常簡單的,只需要獲取ContentResolver物件,然後使用它的方法運算元據即可。
這裡以最常見的功能:操作通訊錄 為例來示範如何使用Content Provider。
記得新增操作Contacts需要的許可權
java程式碼:
先看看新增的功能實現,先以單獨操作的方式來示範:
java程式碼:
//1:新增原始的帳號資訊 ContentValues values = new ContentValues(); values.put(RawContacts.ACCOUNT_TYPE, "userAccount"); values.put(RawContacts.ACCOUNT_NAME, "cc"); Uri rawContactUri = getContentResolver().insert(RawContacts.CONTENT_URI, values); long rawContactId = ContentUris.parseId(rawContactUri); //2:新增賬戶人員的姓名 values.clear(); values.put(Data.RAW_CONTACT_ID, rawContactId); values.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); values.put(StructuredName.DISPLAY_NAME, "cc1Name"); getContentResolver().insert(Data.CONTENT_URI, values);
java程式碼:
//3:新增電話資訊 values.clear(); values.put(Data.RAW_CONTACT_ID, rawContactId); values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); values.put(Phone.NUMBER, "13567890987"); values.put(Phone.TYPE, Phone.TYPE_CUSTOM); values.put(Phone.LABEL, "cc1"); Uri dataUri = getContentResolver().insert(Data.CONTENT_URI,values); //同理,還可以新增Email等等資訊
新增的功能實現,以批處理操作的方式來示範:
前面第一步,新增原始的帳號資訊的過程是一樣的,批處理最好用在後面都是對Data進行操作的過程中,示例如下:
java程式碼:
ArrayListps = new ArrayList (); ops.add(ContentProviderOperation.newInsert(Data.CONTENT_URI) .withValue(Data.RAW_CONTACT_ID, rawContactId) .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE) .withValue(StructuredName.DISPLAY_NAME, "cc1Name") .build()); ops.add(ContentProviderOperation.newInsert(Data.CONTENT_URI) .withValue(Data.RAW_CONTACT_ID, rawContactId) .withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE) .withValue(Phone.NUMBER, "13567890989") .withValue(Phone.TYPE, Phone.TYPE_CUSTOM) .withValue(Phone.LABEL, "cc1").build()); try { getContentResolver().applyBatch(ContactsContract.AUTHORITY,ops); } catch (Exception e) { e.printStackTrace(); }
查詢功能的實現,示例如下:
java程式碼:
//1:得到要操作的原始帳號資訊 Cursor c = getContentResolver().query(RawContacts.CONTENT_URI, new String[]{RawContacts._ID},RawContacts.ACCOUNT_NAME+"=? ",new String[]{"cc"}, null); long rawContactId = 0L; while(c.moveToNext()){ rawContactId = c.getLong(c.getColumnIndex(RawContacts._ID)); } c.close(); //然後開始獲取你需要的資料,這裡示範讀取電話資料,同理可以讀取其他的資料,如Email資料 c = getContentResolver().query(Data.CONTENT_URI, new String[] {Data._ID, Phone.NUMBER, Phone.TYPE, Phone.LABEL}, Data.RAW_CONTACT_ID+"=?"+" and "+Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'", new String[]{""+rawContactId}, null); while (c.moveToNext()) { String dId = ""+c.getInt(c.getColumnIndex(Data._ID)); String name = c.getString(c.getColumnIndex(Phone.LABEL)); String num = c.getString(c.getColumnIndex(Phone.NUMBER)); String type = c.getString(c.getColumnIndex(Phone.TYPE)); Log.i("now query","dataId="+dId+",name="+name+",name="+num+", type="+type); }
修改功能的實現,示例如下:
java程式碼:
//1:應該要先得到要修改的Data資料,這裡為了示範簡單,就直接改了 //2:設定修改的值 ContentValues values = new ContentValues(); values.put(Data.RAW_CONTACT_ID, 3);//測試時的資料是3 values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); values.put(Phone.NUMBER, “13567890981”);//修改了 values.put(Phone.TYPE, Phone.TYPE_CUSTOM); values.put(Phone.LABEL, “upuser1”);//修改了 getContentResolver().update(Data.CONTENT_URI, values, Data._ID + "=?", new String[] { “31” });//測試時的id是31
修改也可以使用批處理的功能來實現,可以參考前面新增的批處理實現。
刪除功能的實現,示例如下:
java程式碼:
//1:應該要先得到要刪除的Data資料,這裡為了示範簡單,就直接改了 //2:刪除資料,直接刪除原始的內容 getContentResolver().delete(RawContacts.CONTENT_URI, Data._ID + "=?", new String[] { "31" });
只要刪除了原始的帳號資料,那麼所有與它附屬的資料,比如電話、Email等都會被自動刪除。如果一個Contact對應的所有原始帳號資料都被刪除掉了,那麼Contact會被自動刪除掉。
視訊配套PPT,視訊地址【 Android4開發入門經典獨家視訊課程】
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/26715458/viewspace-717433/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Android4開發入門經典 之 第三部分:Activity【私塾線上原創】Android
- Android4開發入門經典 之 第五部分:Service【私塾線上原創】Android
- Android4開發入門經典 之 第八部分:SQLite【私塾線上原創】AndroidSQLite
- Android4開發入門經典 之 第十二部分:最佳實踐【私塾線上原創】Android
- Android4開發入門經典 之 第六部分:Broadcast【私塾線上原創】AndroidAST
- Android4開發入門經典 之 第十部分:多媒體【私塾線上原創】Android
- Android4開發入門經典 之 第四部分:使用者介面【私塾線上原創】Android
- Android4開發入門經典 之 第十一部分:網路程式設計【私塾線上原創】Android程式設計
- Android4開發入門經典 之 第七部分(1):資料儲存【私塾線上原創】Android
- Android4開發入門經典 之 第七部分(2):資料儲存【私塾線上原創】Android
- Android4開發入門經典 之 第二部分:Android應用的核心基礎【私塾線上原創】Android
- Android4開發入門經典 之 第四部分:使用者介面Android
- 《Flutter 入門經典》之“Flutter 入門 ”Flutter
- 嵌入式開發 ARM入門經典
- Webpack經典入門Web
- [轉]BI入門經典
- Kafka入門經典教程Kafka
- BI入門經典 (轉)
- 聊聊經典機器學習入門機器學習
- 安卓入門---安卓開發實戰經典1-3章安卓
- Unix 入門經典 筆記筆記
- springboot入門經典Spring Boot
- Python 入門之經典函式例項(二)Python函式
- C++入門經典第九章指標第一節自測題C++指標
- 《HTML5移動應用開發入門經典》——2.9 測驗HTML
- Python入門經典案例一Python
- 《jQueryMobile入門經典》——2.4 總結jQuery
- 最經典的黑客入門教程黑客
- Android中Content ProviderAndroidIDE
- Android Content Provider SecurityAndroidIDE
- 《Windows Phone 7入門經典之使用Silverlight和XNA開發Windows Phone應用》書評Windows
- 經典加密演算法入門-RSA加密演算法
- 博弈論經典模型解析(入門級)模型
- 《jQueryMobile入門經典》——2.5 問與答jQuery
- 部分JS經典題目解析JS
- 遊戲開發新手入門之DirectX入門(轉)遊戲開發
- C語言入門經典(第5版)C語言
- Go語言入門經典第18章Go