Android基礎知識——ContentProvider

本文參考了https://blog.csdn.net/qq_35578940/article/details/52445170 在此感謝作者

內容提供者ContentProvider

? ? ? 在Android中如果想將自己應用的數據(多為數據庫中的數據)提供給第三方應用,那么我們只能通過ContentProvider來實現了。

? ContentProvider是應用程序之間共享數據的接口,使用的時候首先自定義一個類繼承ContentProvider,然后重寫query,insert,updata,delete等方法。

ContentProvider?封裝了數據的跨進程傳輸,我們可以直接使用?getContentResolver()?拿到?ContentResolver?進行增刪改查即可。

ContentProvider?以一個或多個表(與在關系型數據庫中的表類似)的形式將數據呈現給外部應用。 行表示提供程序收集的某種數據類型的實例,行中的每個列表示為實例收集的每條數據。

實現一個?ContentProvider?時需要實現以下幾個方法:

onCreate():初始化 provider

query():查詢數據

insert():插入數據到 provider

update():更新 provider 的數據

delete():刪除 provider 中的數據

getType():返回 provider 中的數據的 MIME 類型

注意:?

1.?onCreate()?默認執行在主線程,別做耗時操作,query()?也最好異步執行?

2. 上面的 4 個增刪改查操作都可能會被多個線程并發訪問,因此需要注意線程安全

ContentProvider 與 URI

ContentProvider?使用 URI 標識要操作的數據,這里的內容 URI 主要包括兩部分:

authority:整個提供程序的符號名稱

path:指向表的名稱/路徑

內容 URI 統一的形式就是:

content://authority/path

1

例如:

content://user_dictionary/words

1

當你調用?ContentResolver?方法來訪問?ContentProvider?中的表時,需要傳遞要操作表的 URI。

在通過?ContentResolver?進行數據請求時(比如?contentResolver.insert(uri, contentValues);), 系統會檢查指定 URI 的 authority 信息,然后將請求傳遞給注冊監聽這個 authority 的?ContentProvider?。這個?ContentProvider?可以監聽 URI 想要操作的內容,Android 中為我們提供了?UriMatcher?來解析 URI。

因為內容提供者是四大組件之一,因此必須在AndroidMainfest文件中進行注冊。

AndroidMainfest中注冊:

<provider?android:name=".StudentProvider"3 android:authorities="com.example.contentproviderdemo.StudentProvider">? </provider?>

下面通過一個示例來講解一下ContentProvider,在這個例子中,需要用到SQLite數據庫來存儲數據,定義了一個StudentDAO類,用于進行對SQLite的CRUD操作,這里就不提供數據訪問的源碼了,有興趣的朋友可以在下載源碼查看:

  ContentProvider實現:

package com.example.contentproviderdemo;

import com.example.dao.StudentDAO;

import android.content.ContentProvider;

import android.content.ContentUris;

import android.content.ContentValues;

import android.content.UriMatcher;

import android.database.Cursor;

import android.net.Uri;

import android.os.Bundle;

import android.util.Log;

public class StudentProvider extends ContentProvider {

? ? private final String TAG = "main";

? ? private StudentDAO studentDao = null;

? ? private static final UriMatcher URI_MATCHER = new UriMatcher(

? ? ? ? ? ? UriMatcher.NO_MATCH);

? ? private static final int STUDENT = 1;

? ? private static final int STUDENTS = 2;

? ? static {

? ? ? ? //添加兩個URI篩選

? ? ? ? URI_MATCHER.addURI("com.example.contentproviderdemo.StudentProvider",

? ? ? ? ? ? ? ? "student", STUDENTS);

? ? ? ? //使用通配符#,匹配任意數字

? ? ? ? URI_MATCHER.addURI("com.example.contentproviderdemo.StudentProvider",

? ? ? ? ? ? ? ? "student/#", STUDENT);? ? ? ?

? ? }

? ? public StudentProvider() {

? ? }? ?


? ? @Override

? ? public boolean onCreate() {

? ? ? ? // 初始化一個數據持久層

? ? ? ? studentDao = new StudentDAO(getContext());

? ? ? ? Log.i(TAG, "---->>onCreate()被調用");

? ? ? ? return true;

? ? }

? ? @Override

? ? public Uri insert(Uri uri, ContentValues values) {

? ? ? ? Uri resultUri = null;

? ? ? ? //解析Uri,返回Code

? ? ? ? int flag = URI_MATCHER.match(uri);

? ? ? ? if (flag == STUDENTS) {

? ? ? ? ? ? long id = studentDao.insertStudent(values);

? ? ? ? ? ? Log.i(TAG, "---->>插入成功, id="+id);

? ? ? ? ? ? resultUri = ContentUris.withAppendedId(uri, id);

? ? ? ? }

? ? ? ? return resultUri;

? ? }

? ? @Override

? ? public int delete(Uri uri, String selection, String[] selectionArgs) {

? ? ? ? int count = -1;

? ? ? ? try {

? ? ? ? ? ? int flag = URI_MATCHER.match(uri);

? ? ? ? ? ? switch (flag) {

? ? ? ? ? ? case STUDENT:

? ? ? ? ? ? ? ? // delete from student where id=?

? ? ? ? ? ? ? ? //單條數據,使用ContentUris工具類解析出結尾的Id

? ? ? ? ? ? ? ? long id = ContentUris.parseId(uri);

? ? ? ? ? ? ? ? String where_value = "id = ?";

? ? ? ? ? ? ? ? String[] args = { String.valueOf(id) };

? ? ? ? ? ? ? ? count = studentDao.deleteStudent(where_value, args);

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? case STUDENTS:

? ? ? ? ? ? ? ? count = studentDao.deleteStudent(selection, selectionArgs);? ? ? ? ? ? ? ?

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? }

? ? ? ? } catch (Exception e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? }

? ? ? ? Log.i(TAG, "---->>刪除成功,count="+count);

? ? ? ? return count;

? ? }

? ? @Override

? ? public int update(Uri uri, ContentValues values, String selection,

? ? ? ? ? ? String[] selectionArgs) {

? ? ? ? int count = -1;

? ? ? ? try {? ? ? ? ? ?

? ? ? ? ? ? int flag = URI_MATCHER.match(uri);

? ? ? ? ? ? switch (flag) {

? ? ? ? ? ? case STUDENT:

? ? ? ? ? ? ? ? long id = ContentUris.parseId(uri);

? ? ? ? ? ? ? ? String where_value = " id = ?";

? ? ? ? ? ? ? ? String[] args = { String.valueOf(id) };

? ? ? ? ? ? ? ? count = studentDao.updateStudent(values, where_value, args);

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? case STUDENTS:

? ? ? ? ? ? ? ? count = studentDao.updateStudent(values, selection,

? ? ? ? ? ? ? ? ? ? ? ? selectionArgs);

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? }

? ? ? ? } catch (Exception e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? }

? ? ? ? Log.i(TAG, "---->>更新成功,count="+count);

? ? ? ? return count;

? ? }

? ? @Override

? ? public Cursor query(Uri uri, String[] projection, String selection,

? ? ? ? ? ? String[] selectionArgs, String sortOrder) {

? ? ? ? Cursor cursor = null;

? ? ? ? try {

? ? ? ? ? ? int flag = URI_MATCHER.match(uri);

? ? ? ? ? ? switch (flag) {

? ? ? ? ? ? case STUDENT:

? ? ? ? ? ? ? ? long id = ContentUris.parseId(uri);

? ? ? ? ? ? ? ? String where_value = " id = ?";

? ? ? ? ? ? ? ? String[] args = { String.valueOf(id) };

? ? ? ? ? ? ? ? cursor = studentDao.queryStudents(where_value, args);

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? case STUDENTS:

? ? ? ? ? ? ? ? cursor = studentDao.queryStudents(selection, selectionArgs);

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? }

? ? ? ? } catch (Exception e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? }

? ? ? ? Log.i(TAG, "---->>查詢成功,Count="+cursor.getCount());

? ? ? ? return cursor;

? ? }

? ? @Override

? ? public String getType(Uri uri) {

? ? ? ? int flag = URI_MATCHER.match(uri);

? ? ? ? String type = null;

? ? ? ? switch (flag) {

? ? ? ? case STUDENT:

? ? ? ? ? ? type = "vnd.android.cursor.item/student";

? ? ? ? ? ? Log.i(TAG, "----->>getType return item");

? ? ? ? ? ? break;

? ? ? ? case STUDENTS:

? ? ? ? ? ? type = "vnd.android.cursor.dir/students";

? ? ? ? ? ? Log.i(TAG, "----->>getType return dir");

? ? ? ? ? ? break;

? ? ? ? }

? ? ? ? return type;

? ? }

? ? @Override

? ? public Bundle call(String method, String arg, Bundle extras) {

? ? ? ? Log.i(TAG, "------>>"+method);

? ? ? ? Bundle bundle=new Bundle();

? ? ? ? bundle.putString("returnCall", "call被執行了");

? ? ? ? return bundle;

? ? }

}

ContentProvider可以理解為一個Android應用對外開放的接口,只要是符合它所定義的Uri格式的請求,均可以正常訪問執行操作。其他的Android應用可以使用ContentResolver對象通過與ContentProvider同名的方法請求執行,被執行的就是ContentProvider中的同名方法。所以ContentProvider很多對外可以訪問的方法,在ContentResolver中均有同名的方法,是一一對應的

Uri

  在Android中,Uri是一種比較常見的資源訪問方式。而對于ContentProvider而言,Uri也是有固定格式的:

 <srandard_prefix>   ://<authority>/<data_path>/<id>

<srandard_prefix>:ContentProvider的srandard_prefix始終是content://。

<authority>:ContentProvider的名稱。

<data_path>:請求的數據類型。

<id>:指定請求的特定數據。


ContentProvider

ContentProvider也是Android應用的四大組件之一,所以也需要在AndroidManifest.xml文件中進行配置。而且某個應用程序通過ContentProvider暴露了自己的數據操作接口,那么不管該應用程序是否啟動,其他應用程序都可以通過這個接口來操作它的內部數據。

  Android附帶了許多有用的ContentProvider,但是本篇博客不會涉及到這些內容的,以后有時間會再講解。Android附帶的ContentProvider包括:

Browser:存儲如瀏覽器的信息。

CallLog:存儲通話記錄等信息。

Contacts:存儲聯系人等信息。

MediaStore:存儲媒體文件的信息。

Settings:存儲設備的設置和首選項信息。

  在Android中,如果要創建自己的內容提供者的時候,需要擴展抽象類ContentProvider,并重寫其中定義的各種方法。然后在AndroidManifest.xml文件中注冊該ContentProvider即可。

  ContentProvider是內容提供者,實現Android應用之間的數據交互,對于數據操作,無非也就是CRUD而已。下面是ContentProvider必須要實現的幾個方法:

onCreate():初始化提供者。

query(Uri, String[], String, String[], String):查詢數據,返回一個數據Cursor對象。

insert(Uri, ContentValues):插入一條數據。

update(Uri, ContentValues, String, String[]):根據條件更新數據。

delete(Uri, String, String[]):根據條件刪除數據。

getType(Uri) 返回MIME類型對應內容的URI。

除了onCreate()和getType()方法外,其他的均為CRUD操作,這些方法中,Uri參數為與ContentProvider匹配的請求Uri,剩下的參數可以參見SQLite的CRUD操作,基本一致,SQLite的內容在另外一篇博客中有講解:Android--數據持久化之SQLite。、

  Tips:還有兩個非常有意思的方法,必須要提一下,call()和bulkInsert()方法,使用call,理論上可以在ContentResolver中執行ContentProvider暴露出來的任何方法,而bulkInsert()方法用于插入多條數據。


  在ContentProvider的CRUD操作,均會傳遞一個Uri對象,通過這個對象來匹配對應的請求。那么如何確定一個Uri執行哪項操作呢?需要用到一個UriMatcher對象,這個對象用來幫助內容提供者匹配Uri。它所提供的方法非常簡單,僅有兩個:

void addURI(String authority,String path,int code):添加一個Uri匹配項,authority為AndroidManifest.xml中注冊的ContentProvider中的authority屬性;path為一個路徑,可以設置通配符,#表示任意數字,*表示任意字符;code為自定義的一個Uri代碼。

int match(Uri uri):匹配傳遞的Uri,返回addURI()傳遞的code參數。


  在創建好一個ContentProvider之后,還需要在AndroidManifest.xml文件中對ContentProvider進行配置,使用一個節點,一般只需要設置兩個屬性即可訪問,一些額外的屬性就是為了設置訪問權限而存在的,后面會詳細講解:

android:name:provider的響應類。

android:authorities:Provider的唯一標識,用于Uri匹配,一般為ContentProvider類的全名。

getType()中的MIME

  MIME類型就是設定某種擴展名的文件用一種應用程序來打開的方式類型。在ContentProvider中的getType方法,返回的就是一個MIME類型的字符串。如果支持需要使用ContentProvider來訪問數據,就上面這個Demo,getType()完全可以只返回一個Null,并不影響效果,但是覆蓋ContentProvider的getType方法對于用new Intent(String action, Uri uri)方法啟動activity是很重要的,如果它返回的MIME type和activity在中定義的data的MIME type不一致,將造成activity無法啟動。這就涉及到Intent和Intent-filter的內容了,以后有機會再說,這里不再詳解。

  從官方文檔了解到,getType返回的字符串,如果URI針對的是單條數據,則返回的字符串以vnd.android.cursor.item/開頭;如果是多條數據,則以vnd.adroid.cursor.dir/開頭。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,443評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,530評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,407評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,981評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,759評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,204評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,263評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,415評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,955評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,650評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,892評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,675評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內容