定義
內(nèi)容提供者,是Android四大組件之一
作用
進程之間進行數(shù)據(jù) 交互、共享 ,即跨進程通信
1.內(nèi)容提供者只是搬運工,真正的存儲、操作數(shù)據(jù)的數(shù)據(jù)源還是原來存儲數(shù)據(jù)的方式(數(shù)據(jù)庫、文件、xml或網(wǎng)絡(luò))
2.數(shù)據(jù)源可以為:數(shù)據(jù)庫、文件、XML或網(wǎng)絡(luò)等
原理
ContentProvider的底層原理 即為 Binder的機制
具體使用
ContentProvider的使用主要有以下內(nèi)容:
1.統(tǒng)一資源標識符(URI)
- 定義:URI(Uniform Resource Identifier),即統(tǒng)一資源標識符
- 作用:唯一標識ContentProvider及其中的數(shù)據(jù)
外界進程通過URI找到對應(yīng)的ContentProvider及其中的數(shù)據(jù),再進行數(shù)據(jù)操作
- 具體使用
URI分為系統(tǒng)預置和自定義,分別對應(yīng)系統(tǒng)內(nèi)置數(shù)據(jù)(通訊錄、日程表等)和自定義數(shù)據(jù)庫
自定義URI=content://com.wz.provider/User/1 , 其中
- 主題Schema(content):ContentProvider的URI前綴(Android規(guī)定)
- 授權(quán)信息Authority(com.wz.provider) :ContentProvider的唯一標識符
- 表名Path(User):ContentProvider指向數(shù)據(jù)庫的某個表名
- 記錄ID(1):表中的某個記錄(若無指定則返回全部記錄)
//設(shè)置URI
Uri uri=Uri.parse("content://com.wz.provider/User/1")
// 上述URI指向的資源是:名為 `com.wz.provider`的`ContentProvider` 中表名 為`User` 中的 `id`為1的數(shù)據(jù)
// 特別注意:URI模式存在匹配通配符* & #
// *:匹配任意長度的任何有效字符的字符串
// 以下的URI 表示 匹配provider的任何內(nèi)容
content://com.example.app.provider/*
// #:匹配任意長度的數(shù)字字符的字符串
// 以下的URI 表示 匹配provider中的table表的所有行
content://com.example.app.provider/table/#
2.MIME數(shù)據(jù)類型
作用:指定某個擴展名的文件用某種應(yīng)用程序來打開,例如指定.html文件采用text應(yīng)用程序打開
-
具體使用:
ContentProvider根據(jù)URI返回MIME類型
ContentProvider.getType(uri);MIME類型組成
每種MIME類型由兩部分組成:類型+子類型(MIME是一個字符串)
text/html text=類型,html=子類型MIME類型形式
MIME類型有2種形式:單條記錄、多條記錄
//形式1:單條記錄 vnd.android.cursor.item/自定義 //形式2:多條記錄 vnd.android.cursor.dir/自定義 //1. vnd:表示父類型和子類型具有非標準、特定的形式。 //2. vnd:父類型已經(jīng)固定好(即不能更改),只能區(qū)別是單條還是多條 //3. 子類型可自定義實例
< -- 單條記錄 -- > //單個記錄的MIME類型 vnd.android.cursor.item/vnd.yourcompanyname.contenttype //若一個uri如下 content://com.example.transportationprovider/trains/122 // 則ContentProvider會通過ContentProvider.geType(url)返回以下MIME類型 vnd.android.cursor.item/vnd.example.rail < -- 多條記錄 -- > //多條記錄的MIME類型 vnd.android.cursor.dir/vnd.yourcompanyname.contenttype // 若一個Uri如下 content://com.example.transportationprovider/trains // 則ContentProvider會通過ContentProvider.geType(url)返回以下MIME類型 vnd.android.cursor.dir/vnd.example.rail
3.ContentProvider類
組織數(shù)據(jù)的方式
- ContentProvider主要以 表格的形式 組織數(shù)據(jù)(同時也支持文件數(shù)據(jù),表格用得比較多)
- 每個表格中包含多張表,每張表包含行、列、分別對應(yīng)記錄、字段(行==記錄,列==字段)
主要方法
-
進程間共享數(shù)據(jù)的本質(zhì)就是:添加、刪除、獲取、更新。所以ContentProvider的核心方法也主要是上述4個作用
< -- 4個核心方法 -- > //外部進程向ContentProvider 中 添加 數(shù)據(jù) public Uri insert(Uri uri,ContentValues values) //外部進程 刪除 ContentProvider中的數(shù)據(jù) public int delete(Uri uri,String selection,String[] selectionArgs) //外部進程 更新 ContentProvider中的數(shù)據(jù) public int update(Uri uri,ContentValues values,String selection,String[] selectionArgs) //外部進程 獲取 ContentProvider 中的數(shù)據(jù) public Cursor query(Uri uri,String[] projection,String selection,String[] selectionArgs) //注 1.上述四個方法由外部進程回調(diào),并運行在ContentProvider進程的Binder線程池中(不是主線程) 2.存在多線程并發(fā)訪問,需要實現(xiàn)線程同步(單個SQLite不需要,SQLite內(nèi)部已經(jīng)實現(xiàn)了線程同步) < -- 2個其他方法 -- > //ContentProvider創(chuàng)建后 或 打開系統(tǒng)后其他進程第一次訪問該ContentProvider時,由系統(tǒng)進行調(diào)用 //注:運行在ContentProvider進程的主線程,故不能做耗時操作 public boolean onCreate() //得到數(shù)據(jù)類型,即返回當前Uri所代表數(shù)據(jù)的MIME類型 public Stirng getType(Uri uri) Android為常見的數(shù)據(jù)(通訊錄、日程表)提供了內(nèi)置默認的ContentProvider,但也可以根據(jù)需求自定義ContentProvider(重寫上述6個方法)
ContentProvider類不會直接與外部進程進行交互,而是通過ContentResolver類
ContentResolver類
作用:
- 統(tǒng)一管理不同ContentProvider間的操作
1.通過URI即可操作不同的ContentProvider中的數(shù)據(jù)
2.外部進程通過ContentResolver類從而與ContentProvider類進行交互
ContentResolver存在的原因:
- 一般來說,一款應(yīng)用要使用多個ContentProvider,若需要了解每個ContentProvider的不同實現(xiàn)從而完成數(shù)據(jù)的交互,操作成本高、難度大。所以在ContentProvider的基礎(chǔ)上多加了一個ContentResolver類對所有ContentProvider進行統(tǒng)一管理。
使用:
-
ContentResolver類提供了與ContentProvider相同名字和作用的4個方法
//外部進程向 ContentProvider 中添加數(shù)據(jù) public Uri insert(Uri uri,ContentValues values) // 外部進程 刪除 ContentProvider 中的數(shù)據(jù) public int delete(Uri uri, String selection, String[] selectionArgs) // 外部進程更新 ContentProvider 中的數(shù)據(jù) public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) // 外部應(yīng)用 獲取 ContentProvider 中的數(shù)據(jù) public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) -
實例說明
// 使用ContentResolver前,需要先獲取ContentResolver // 可通過在所有繼承Context的類中 通過調(diào)用getContentResolver()來獲得ContentResolver ContentResolver resolver = getContentResolver(); // 設(shè)置ContentProvider的URI Uri uri = Uri.parse("content://cn.scu.myprovider/user"); // 根據(jù)URI 操作 ContentProvider中的數(shù)據(jù) // 此處是獲取ContentProvider中 user表的所有記錄 Cursor cursor = resolver.query(uri, null, null, null, "userid desc");
4.輔助工具類
ContentUris類
- 作用:操作URI
- 具體使用:
兩個核心方法:withAppendedId() == 向URI追加一個id
-
parseId() == 從URI中獲取id
// withAppendedId()作用:向URI追加一個id Uri uri = Uri.parse("content://cn.scu.myprovider/user") Uri resultUri = ContentUris.withAppendedId(uri, 7); // 最終生成后的Uri為:content://cn.scu.myprovider/user/7 // parseId()作用:從URL中獲取ID Uri uri = Uri.parse("content://cn.scu.myprovider/user/7") long personid = ContentUris.parseId(uri); //獲取的結(jié)果為:7
UriMatcher類
作用:
1.在ContentProvider中注冊URI
2.根據(jù)URI匹配ContentProvider中對應(yīng)的數(shù)據(jù)表-
使用
步驟1:初始化UriMatcher對象
步驟2:在ContentProvider 中注冊URI( addURI() )
步驟3:根據(jù)URI 匹配 URI_CODE,從而匹配ContentProvider中相應(yīng)的資源( match() )//步驟1:初始化UriMatcher對象 UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); //常量UriMatcher.NO_MATCH = 不匹配任何路徑的返回碼 // 即初始化時不匹配任何東西 // 步驟2:在ContentProvider 中注冊URI(addURI()) int URI_CODE_a = 1; int URI_CODE_b = 2; matcher.addURI("cn.scu.myprovider", "user1", URI_CODE_a); matcher.addURI("cn.scu.myprovider", "user2", URI_CODE_b); // 若URI資源路徑 = content://cn.scu.myprovider/user1 ,則返回注冊碼URI_CODE_a // 若URI資源路徑 = content://cn.scu.myprovider/user2 ,則返回注冊碼URI_CODE_b // 步驟3:根據(jù)URI 匹配 URI_CODE,從而匹配ContentProvider中相應(yīng)的資源(match()) @Override public String getType(Uri uri) { Uri uri = Uri.parse(" content://cn.scu.myprovider/user1"); switch(matcher.match(uri)){ // 根據(jù)URI匹配的返回碼是URI_CODE_a // 即matcher.match(uri) == URI_CODE_a case URI_CODE_a: return tableNameUser1; // 如果根據(jù)URI匹配的返回碼是URI_CODE_a,則返回ContentProvider中的名為tableNameUser1的表 case URI_CODE_b: return tableNameUser2; // 如果根據(jù)URI匹配的返回碼是URI_CODE_b,則返回ContentProvider中的名為tableNameUser2的表 } }
ContentObserver類
定義:內(nèi)容觀察者
作用:觀察uri引起ContentProvider中的數(shù)據(jù)變化(當ContentProvider中的數(shù)據(jù)發(fā)生增、刪、改時就會觸發(fā)ContentObserver類)并通知外界(即 訪問 該數(shù)據(jù)的訪問者)
-
使用:
// 步驟1:注冊內(nèi)容觀察者ContentObserver // 通過ContentResolver類進行注冊,并指定需要觀察的URI getContentResolver().registerContentObserver(uri); // 步驟2:當該URI的ContentProvider數(shù)據(jù)發(fā)生變化時,通知外界(即訪問該ContentProvider數(shù)據(jù)的訪問者) public class UserContentProvider extends ContentProvider { public Uri insert(Uri uri, ContentValues values) { db.insert("user", "userid", values); // 通知訪問者 getContext().getContentResolver().notifyChange(uri, null); } } // 步驟3:解除觀察者 // 同樣需要通過ContentResolver類進行解除 getContentResolver().unregisterContentObserver(uri);
5.實例使用
ContentProvider不僅可以用于進程之間的通信,也可以用于進程內(nèi)的通信
-
進程內(nèi)通信
步驟:1.創(chuàng)建數(shù)據(jù)庫類
2.自定義ContentProvider類(創(chuàng)建提供者)
3.注冊創(chuàng)建的ContentProvider類(注冊提供者,在清單配置文件注冊)
4.訪問ContentProvider類的數(shù)據(jù)
6.感謝
文章出處:簡書大神 Carson_Ho
博客地址:http://www.itdecent.cn/p/ea8bc4aaf057