内容提供器(Content-Provider)完整使用指南
[TOC]
1. 什么是內(nèi)容提供器?
? 內(nèi)容提供器(Content Provider) 主要用于在不同的應(yīng)用程序之間實(shí)現(xiàn)數(shù)據(jù)的共享功能,他提供了一套完整的機(jī)制,允許一個(gè)程序訪問(wèn)另一個(gè)程序中的數(shù)據(jù),同時(shí)還可以保證被訪問(wèn)的數(shù)據(jù)的安全性,目前使用內(nèi)容提供器十Android實(shí)現(xiàn)跨程序共享數(shù)據(jù)的標(biāo)準(zhǔn)方式. 內(nèi)容提供器可以選擇只對(duì)哪一部分的數(shù)據(jù)進(jìn)行共享,這樣就可以保證我們數(shù)據(jù)的安全性.
?
2. 如何使用內(nèi)容提供器獲取其他應(yīng)用的數(shù)據(jù)
2.1 權(quán)限聲明
? 運(yùn)行時(shí)權(quán)限:6.0系統(tǒng)中引入的新功能,為了能更好的保護(hù)用戶的隱私.
? 通常的權(quán)限聲明只需要在MainFest中添加要使用的權(quán)限就可以了,6.0及以后對(duì)于某些權(quán)限還需要在運(yùn)行的時(shí)候在代碼中檢測(cè)是否有這個(gè)權(quán)限否則彈出對(duì)話框申請(qǐng),拒絕的話是無(wú)法使用的.
? **并不是所有的權(quán)限都需要用到運(yùn)行時(shí)權(quán)限,只有關(guān)系到用戶隱私的才需要.除了在ManiFest中聲明以外,還要在代碼中重新請(qǐng)求一 遍,如果沒(méi)有使用運(yùn)行時(shí)權(quán)限則會(huì)導(dǎo)致這個(gè)應(yīng)用拋出異常SecurityException() **
?
public class MainActivity extends AppCompatActivity {private String TAG = "TAG";protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);checkSelf();//檢查權(quán)限}private void checkSelf() {//如果檢查多個(gè)權(quán)限的話,可以將要檢查的權(quán)限放入數(shù)組或者集合當(dāng)中,遍歷檢查即可if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {/*** 通過(guò)checkSelfPermission可以檢查當(dāng)前這個(gè)應(yīng)用有沒(méi)有獲取到指定的那個(gè)權(quán)限,沒(méi)有的話就調(diào)用請(qǐng)求權(quán)限的那個(gè)方法.* 根據(jù)返回值來(lái)判斷狀態(tài) 0表示權(quán)限已給予,-1表示沒(méi)有獲取到權(quán)限.*/Log.d(TAG, "checkSelf: 權(quán)限允許");} else {/*** 通過(guò)ActivityCompat.requestPermissions動(dòng)態(tài)的申請(qǐng)權(quán)限.* 第一個(gè)參數(shù):當(dāng)前的上下文* 第二個(gè)參數(shù):需要申請(qǐng)的權(quán)限的字符串,保存在數(shù)組中.* 第三個(gè)參數(shù):查詢碼,請(qǐng)求權(quán)限的最終結(jié)果會(huì)通過(guò)回調(diào)的方式->onRequestPermissionsResult();在這個(gè)方法中,告訴你最后請(qǐng)求成功了沒(méi)有*/ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_CALENDAR,Manifest.permission.CAMERA}, 1);}}public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {/*** 通過(guò)請(qǐng)求碼來(lái)判斷具體是申請(qǐng)了什么權(quán)限,以及結(jié)果;* grantResult[] 保存著申請(qǐng)權(quán)限后,根據(jù)申請(qǐng)權(quán)限的先后順序保存*/switch (requestCode) {case 1:if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {Log.d(TAG, "onRequestPermissionsResult: 已授權(quán)");for (int i = 0; i < grantResults.length; i++) {Log.d(TAG, "onRequestPermissionsResult: 授權(quán)情況:"+grantResults[i]);}} else {Log.d(TAG, "onRequestPermissionsResult: 您拒絕了授權(quán)");}break;case 2:break;default:break;}} }復(fù)制代碼?
? 以上就是運(yùn)行時(shí)權(quán)限的基本用法
2.2 內(nèi)容提供器的基本用法及原理
? 內(nèi)容提供器的用法一般有兩種,一是使用現(xiàn)有的內(nèi)容提供器來(lái)讀取和操作相應(yīng)程序中的數(shù)據(jù),二是自己創(chuàng)建一個(gè)內(nèi)容提供器給我們的程序的數(shù)據(jù)提供外部訪問(wèn)接口 (就是通過(guò)別人(程序)提供的內(nèi)容提供器來(lái)獲取別人(程序)想要給我們使用的數(shù)據(jù)),系統(tǒng)自帶的短信,電話簿,媒體庫(kù)等程序都提供了類似的訪問(wèn)接口,我們就可以利用這個(gè)來(lái)進(jìn)行再次開發(fā)和使用了.
?
? 如果想要獲取內(nèi)容提供器中的數(shù)據(jù),那么就需要借助Content-Resolver類,Context中的GetContentResolver方法可以獲取到該類的實(shí)例. Content-Resolver提供了類似SqLiteDatabase類的方法,可以對(duì)共享的數(shù)據(jù)進(jìn)行CRUD 操作,只是參數(shù)略微有點(diǎn)不同罷了.
? 內(nèi)容提供器是通過(guò)Uri來(lái)尋找數(shù)據(jù)源的 Uri由authority和path組成;authority用于區(qū)分不同的程序,通常使用包名,path是對(duì)同一程序不同的表做區(qū)分用的.
? 比如某個(gè)包名為 com.example.test 有兩張表table1,table2;
? 則標(biāo)準(zhǔn)的Uri格式為(://前面的內(nèi)容為協(xié)議):
? content://com.example.test/table1
? content://com.example.test/table2
? 這樣就可以明確的表達(dá)出我們想要訪問(wèn)哪個(gè)程序的哪個(gè)表里面的數(shù)據(jù)了. 所以內(nèi)容提供器的CRUD都只接受Uri參數(shù)來(lái)確定位置.
查找:
Uri uri=Uri.parse("content://com.example.test/table2");
Cursor cursor=getContentResolver().quert(uri,projection,selection,selectionArgs,sortOrder);
| uri | from table_name | 指定查詢某個(gè)應(yīng)用程序下的某一張表 |
| projection | select column1,column2 | 指定查詢的列名 |
| selection | where column =value | 指定where的約束條件 |
| selectionArgs | - | 為where中的占位符提供具體的值 |
| sortOrder | order by column1,column2 | 指定查詢結(jié)果的排序方式 |
從cursor中取出值也和數(shù)據(jù)庫(kù)的操作一樣,選擇想要獲取的數(shù)據(jù)類型,再選擇列名即可得到
...String data=cursor.getString(cursor.getColumnIndex("columnName"));...復(fù)制代碼插入:
? 和數(shù)據(jù)庫(kù)的操作差不多,都是講數(shù)據(jù)組裝到ContentValues中
ContentValues values=new ContentValues();values.put("columnName1","value1");values.put("columnName2","value2");getContentResolver().insert(uri,values); 復(fù)制代碼更新:
ContentValues values=new ContentValues(); values.put("columnName1","value1"); getContentResolver().update(Uri.parse(""),contentValues,"where column1=?",new String[]{"1"}); 復(fù)制代碼update()中的參數(shù)解釋:第一個(gè)參數(shù)指定數(shù)據(jù)的位置;第二個(gè)參數(shù)指定要更新成什么值;第三個(gè)參數(shù)指定條件;第四個(gè)參數(shù)指定where條件語(yǔ)句中的缺省值;
刪除:
?
getContentResolver().delete(Uri.parse(""),"column=?",new String[]{"1"}); 復(fù)制代碼? 參數(shù)的意思和上面幾個(gè)差不多.就不過(guò)多的解釋了.
2.3 使用內(nèi)容提供器獲取數(shù)據(jù)
? 下面使用系統(tǒng)已經(jīng)給我們提供好了的內(nèi)容提供器來(lái)獲取通訊簿中的姓名的電話號(hào)碼吧,先確保的確保存了幾個(gè)電話號(hào)碼在通訊簿中.
1.首先需要獲取READ_CANTACTS權(quán)限否則是不能讀取數(shù)據(jù)的 并且會(huì)報(bào)錯(cuò).
2.查詢語(yǔ)句和數(shù)據(jù)庫(kù)的用法相似
public class MainActivity extends AppCompatActivity {private String TAG = "TAG";protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_CONTACTS}, 1);} else {getData();}}private void getData() {Cursor cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);//傳入的Uri是ContactsContract.CommonData-Kinds.Phone類已經(jīng)幫我們封裝好了的一個(gè)常量//點(diǎn)開源代碼可以看到 : //public static final Uri CONTENT_URI = Uri.withAppendedPath(Data.CONTENT_URI,"phones");if (cursor != null) {while (cursor.moveToNext()) {String name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));//ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME 也是一個(gè)常量,為保存該數(shù)據(jù)的類名.下同String phone = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));Log.d(TAG, "getData: " + name + "-" + phone);}}}public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {switch (requestCode) {case 1:if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {getData();} else {Log.d(TAG, "onRequestPermissionsResult: 你點(diǎn)擊了拒絕!");}break;default:break;}} }復(fù)制代碼運(yùn)行程序就可以看到姓名和電話一起打印出來(lái)了.
主要的內(nèi)容都在getData();方法中,具體的解釋都寫在注釋中,只要知道原理 那么使用起來(lái)就變得十分的方便了.
3. 創(chuàng)建自己的內(nèi)容提供器
基礎(chǔ)知識(shí):
通過(guò)新建一個(gè)類去繼承ContentProvider的方式來(lái)創(chuàng)建一個(gè)內(nèi)容提供器,但是必須重寫里面的六個(gè)方法;
?
| onCreate(); | boolean | 初始化內(nèi)容提供器的時(shí)候調(diào)用.通常會(huì)在這里完成對(duì)數(shù)據(jù)庫(kù)的創(chuàng)建和升級(jí)操作.返回true表明內(nèi)容提供器初始化成功,false失敗. |
| query(uri,projection, selection, selectionArgs, sortOrder); | Cursor | 從內(nèi)容提供器中查找數(shù)據(jù),使用uri參數(shù)來(lái)確定要查找哪一張表格,projection用于確定查找哪些列,selection用于確定查找的條件,selectionArgs用于填充selection中的條件的缺省值,sortOrder用于對(duì)查詢結(jié)果排序 ; 查詢結(jié)果放入Cursor中返回; |
| insert(uri, values); | Uri | 向內(nèi)容提供器中添加一條數(shù)據(jù),使用uri來(lái)確定要添加到的表,待添加的數(shù)據(jù)放在values參數(shù)中; 添加成功以后 返回一個(gè)用于表示這條新紀(jì)錄的Cursor; |
| update(uri, values, selection, selectionArgs); | int | 更新內(nèi)容提供器中已有的數(shù)據(jù),使用Uri參數(shù)來(lái)確定更新哪一張表中的數(shù)據(jù),新的數(shù)據(jù)保存在values中(只更新這里有寫出來(lái)的值),selection和selectIonArgs參數(shù)用于約束更新哪些行;受影響的行數(shù)將作為返回值 返回 |
| delete(uri, selection, selectionArgs) | int | 刪除內(nèi)容提供器中的數(shù)據(jù),uri參數(shù)用來(lái)確定刪除哪張表中的數(shù)據(jù),selection和selectionArgs參數(shù)用于約束刪除哪些行;被刪除的行數(shù)將作為返回值返回; |
| getType(uri)(); | String | 根據(jù)傳入的Uri來(lái)返回相對(duì)應(yīng)的MIME類型字符串 |
Uri兩種格式的說(shuō)明:
? content://com.example.test/table1/1;
表示調(diào)用方期望訪問(wèn)的是com.exampke.test應(yīng)用里面表table1中id為1的數(shù)據(jù)
? content://com.example.test/table1;
表示調(diào)用方期望訪問(wèn)的是com.exampke.test應(yīng)用里面表table1的所有數(shù)據(jù)
不過(guò)通常都使用通配符的方式來(lái)匹配這兩種格式:
:表示匹配任意長(zhǎng)度的數(shù)字
#* :表示匹配任意長(zhǎng)度的字符
上面的內(nèi)容就可以寫成: content://com.example.test/* 或 content://com.example.test/table1/#
getType();返回?cái)?shù)據(jù)說(shuō)明
? content://com.example.test/table1/1;
? content://com.example.test/table1;
還是以上面這兩個(gè)為例子.
MIME字符串: 以vnd開頭 + . + android.cursor.dir/(或android.cursor.item/) + vnd. + AUTHORITY + . + PATH
? vnd.android.cursor.dir/vnd.com.example.test.table1;
? vnd.android.cursor.item/vnd.com.example.test.table1
3.1 創(chuàng)建的基本步驟
創(chuàng)建一個(gè)繼承自ContentProvider的類,并重寫里面的六個(gè)方法
其中內(nèi)容提供器必須在ManiFest.xml中進(jìn)行注冊(cè),否則無(wú)法使用
<providerandroid:name=".MyProvider" android:authorities="com.example.h.content_demo_provider"android:enabled="true"android:exported="true" /> 復(fù)制代碼第一個(gè)參數(shù):類名
第二個(gè)參數(shù):通常使用包名來(lái)使用, 可以區(qū)分 不同的程序之間的內(nèi)容提供器
第三個(gè)參數(shù):啟用
第四個(gè)參數(shù):表示允許被其他的應(yīng)用程序進(jìn)行訪問(wèn)
新建一個(gè)內(nèi)容提供器:
public class MyProvider extends ContentProvider {public static final int BOOK_DIT = 0;public static final int BOOK_ITEM = 1;public static final int CATEGORY_DIR = 2;public static final int CATEGORT_ITEM = 3;public static UriMatcher sUriMatcher;public static final String AUTHORITY = "com.example.h.content_demo_provider";private MyDatabaseHelper mMyDatabaseHelper;private SQLiteDatabase db;{sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);sUriMatcher.addURI(AUTHORITY, "book", BOOK_DIT);sUriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);sUriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);sUriMatcher.addURI(AUTHORITY, "category/#", CATEGORT_ITEM);//UriMatcher 可以匹配uri 通過(guò)調(diào)用他的match()方法 匹配到就會(huì)返回我們?cè)谏厦嫣砑觰ri時(shí)填入的第三個(gè)參數(shù)}/*** 初始化內(nèi)容提供器的時(shí)候調(diào)用,通常會(huì)在這里完成對(duì)數(shù)據(jù)庫(kù)的創(chuàng)建和升級(jí)等操作* 返回true表示內(nèi)容提供器初始化成功,返回false則表示失敗.*/public boolean onCreate() {//對(duì)當(dāng)前內(nèi)容提供器需要的資源進(jìn)行初始化mMyDatabaseHelper = new MyDatabaseHelper(getContext(), "info.db", null, 1);db = mMyDatabaseHelper.getWritableDatabase();Log.d(TAG, "onCreate: 內(nèi)容提供器初始化完成");return true;}public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable Stringselection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {//查詢方法,通過(guò)解析uri來(lái)判斷想要查詢哪個(gè)程序的哪個(gè)表.通過(guò)UriMatchder進(jìn)行匹配 如果有就返回前面addUri()中填入的codeCursor cursor = null;switch (sUriMatcher.match(uri)) {case BOOK_DIT:cursor = db.query("book", projection, selection, selectionArgs, null, null,sortOrder);Log.d(TAG, "query: 查詢整個(gè)表" + cursor);return cursor;case BOOK_ITEM:String itemId = uri.getPathSegments().get(1);cursor = db.query("book", projection, "id=?", new String[]{itemId}, null, null,sortOrder);/*** .getPathSegments()它會(huì)將內(nèi)容URI權(quán)限之后的部分以 / 進(jìn)行分割,并把分割后的結(jié)果放入到一個(gè)字符串列表中,* 返回的列表[0]存放的就是路徑,[1]存放的就是id*/return cursor;case CATEGORT_ITEM:String itemId2 = uri.getPathSegments().get(1);cursor = db.query("category", projection, "id=?", new String[]{itemId2}, null, null,sortOrder);return cursor;case CATEGORY_DIR:cursor = db.query("category", projection, selection, selectionArgs, null, null,sortOrder);return cursor;default:return cursor;}}public String getType(@NonNull Uri uri) {switch (sUriMatcher.match(uri)) {case BOOK_DIT:return "vnd.android.cursor.dir/vnd.com.example.h.content_demo_provider.book";case BOOK_ITEM:return "vnd.android.cursor.item/vnd.com.example.h.content_demo_provider.book";case CATEGORT_ITEM:return "vnd.android.cursor.item/vnd.com.example.h.content_demo_provider.category";case CATEGORY_DIR:return "vnd.android.cursor.dir/vnd.com.example.h.content_demo_provider.category";default:return null;}}public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {Uri uriReturn = null;switch (sUriMatcher.match(uri)) {case BOOK_DIT:case BOOK_ITEM:long value = db.insert("book", null, values);uriReturn = Uri.parse("content://" + AUTHORITY + "/book/" + value);return uriReturn;//返回新插入行的行id,如果發(fā)生錯(cuò)誤則返回-1case CATEGORT_ITEM:case CATEGORY_DIR:long value2 = db.insert("category", null, values);uriReturn = Uri.parse("content://" + AUTHORITY + "/category/" + value2);return uriReturn;default:return uriReturn;}}public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[]selectionArgs) {int deleteRows = 0;switch (sUriMatcher.match(uri)) {case BOOK_DIT:deleteRows = db.delete("book", selection, selectionArgs);return deleteRows;case BOOK_ITEM:String itemId1 = uri.getPathSegments().get(1);deleteRows = db.delete("book", "id=?", new String[]{itemId1});return deleteRows;case CATEGORT_ITEM:String itemId2 = uri.getPathSegments().get(1);deleteRows = db.delete("category", "id=?", new String[]{itemId2});return deleteRows;case CATEGORY_DIR:deleteRows = db.delete("category", selection, selectionArgs);return deleteRows;default:return deleteRows;}}public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable Stringselection, @Nullable String[] selectionArgs) {int updateRows = 0;switch (sUriMatcher.match(uri)) {case BOOK_DIT:updateRows = db.update("book", values, selection, selectionArgs);return updateRows;case BOOK_ITEM:String itemId1 = uri.getPathSegments().get(1);updateRows = db.update("book", values, "id=?", new String[]{itemId1});return updateRows;case CATEGORT_ITEM:String itemId2 = uri.getPathSegments().get(1);updateRows = db.update("category", values, "id=?", new String[]{itemId2});return updateRows;case CATEGORY_DIR:updateRows = db.update("category", values, selection, selectionArgs);return updateRows;default:return updateRows;}} }復(fù)制代碼新出現(xiàn)的方法:
uri.getPathSegments().get(1); //getPathSegments()返回一個(gè)集合,它將uri中authority后面的內(nèi)容進(jìn)行分割 即get(0)表名和id的值get(1) 這樣取出來(lái)的id就可以當(dāng)作條件對(duì)數(shù)據(jù)庫(kù)進(jìn)行條件查詢了 .
3.2 跨程序獲取數(shù)據(jù)
public class MainActivity extends AppCompatActivity implements View.OnClickListener {private Button btn1, btn2, btn3, btn4;private Uri mUriBook = Uri.parse("content://com.example.h.content_demo_provider/book");private Uri mUriCategory = Uri.parse("content://com.example.h.content_demo_provider/category");protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initView();//randomInsert();iQuery();}private void initView() {btn1 = findViewById(R.id.button);btn2 = findViewById(R.id.button2);btn3 = findViewById(R.id.button4);btn4 = findViewById(R.id.button5);btn4.setOnClickListener(this);btn3.setOnClickListener(this);btn2.setOnClickListener(this);btn1.setOnClickListener(this);}private void iQuery() {Cursor cursor = getContentResolver().query(mUriBook, null, null, null, null);if (cursor != null) {while (cursor.moveToNext()) {String id = cursor.getString(cursor.getColumnIndex("id"));String name = cursor.getString(cursor.getColumnIndex("name"));String author = cursor.getString(cursor.getColumnIndex("author"));System.out.println(id + name + author);}} else {System.out.println("為空");}}private void randomInsert() {ContentValues contentValues1 = new ContentValues();ContentValues contentValues2 = new ContentValues();for (int i = 0; i < 5; i++) {contentValues1.put("name", "name" + i);contentValues1.put("author", "author" + i);System.out.println(getContentResolver().insert(mUriBook, contentValues1));contentValues2.put("type", "type" + i);contentValues2.put("code", "code" + i);System.out.println(getContentResolver().insert(mUriCategory, contentValues2)); ;}//這個(gè)方法只是通過(guò)內(nèi)容提供器對(duì)另一個(gè)程序中的數(shù)據(jù)庫(kù)寫一點(diǎn)數(shù)據(jù)進(jìn)去方便我們進(jìn)行后續(xù)的CRUD.}private void iDelete(String value) {ContentValues contentValues = new ContentValues();int x = 0;Uri uri = Uri.parse("content://com.example.h.content_demo_provider/book/" + value);x = getContentResolver().delete(uri, null, null);Log.d("TAG", "iDelete: 刪除后的返回值:" + x);}public void onClick(View v) {switch (v.getId()) {case R.id.button:iQuery();break;case R.id.button2:break;case R.id.button4:iDelete("1");break;case R.id.button5:break;default:break;}} } 復(fù)制代碼? 主要還是通過(guò)定義好Uri 然后傳遞給內(nèi)容提供器,告訴它你想做什么,最后他會(huì)將執(zhí)行結(jié)果通過(guò)返回值告訴你,更新功能和其他幾個(gè)差不多就不展開解析了,
3.3 總結(jié)
經(jīng)過(guò)上面的實(shí)踐,給我的感覺(jué)就是, 某個(gè)程序A提供了一個(gè)內(nèi)容提供器(這個(gè)內(nèi)容提供器本質(zhì)上就是對(duì)本程序內(nèi)的數(shù)據(jù)庫(kù)進(jìn)行CRUD 不過(guò)可以對(duì)他進(jìn)行限制一些權(quán)限 只給想給的數(shù)據(jù)), 然后程序B想要訪問(wèn)程序A中的某些數(shù)據(jù), 程序B可以通過(guò)Uri對(duì)程序A允許的范圍內(nèi)進(jìn)行CRUD
總結(jié)
以上是生活随笔為你收集整理的内容提供器(Content-Provider)完整使用指南的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: mybatis出现 Parameter
- 下一篇: POJ3694 Network