什麼是內容提供器?java
跨程序共享數據以內容提供器,這是個什麼功能?看到這個名稱的時候最能給咱們提供信息的應該是「跨程序」這個詞了,是的重點就是這個詞,這個內容提供器的做用主要是用於在不一樣的引用程序之間實現數據共享的功能,它提供了一完整的機制,容許一個程序訪問另外一個程序中的數據,同時還能保證被訪問的數據的安全性,在目前使用內容提供器是Android實現跨程序共享數據的標準方式。不一樣於文件存儲和SharePreferences存儲中的兩種全局可讀性操做模式,內容提供器能夠選擇只對那一部分數據進行共享,從而保證咱們程序中的隱私數據不糊有泄漏的風險。android
不過在理解這個內容提供器以前,咱們須要理解Android的運行時權限,這個就不須要咱們解釋什麼是運行時權限了,由於咱們在以前其實已經使用過,好比「相機權限」,「照片權限」,「位置權限」等等!數據庫
運行時權限數組
Android 將全部的權限大體的分爲兩類,一類是普通權限,另外一類是危險權限,咱們在下面將危險的權限整理了出來,供之後咱們參考使用:安全
舉個小栗子app
下面是針對打電話咱們寫的一個小Demo,其實邏輯很簡單,說的直接點就是一句話「有權限直接打電話,沒有權限就請求完了再打」。下面是點擊事件咱們作的操做ide
// 利用checkSelfPermission這個函數檢查有沒有運行時權限 // 有權限就直接調用下面的call()方法,沒有就請求權限 // checkSelfPermission(MainActivity.this, android.Manifest.permission.CALL_PHONE) 是否等於PackageManager.PERMISSION_GRANTED // PERMISSION_GRANTED 贊成權限 if (ContextCompat.checkSelfPermission(MainActivity.this, android.Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED){ // ActivityCompat的requestPermissions請求權限 // 第二個參數是一個String數組,咱們須要把申請的權限名稱放到數組中便可 // 第三個參數是請求碼,這個請求碼咱們在下面權限回調的時候能夠用來作判斷,判斷是那個權限再作相應的操做 ActivityCompat.requestPermissions(MainActivity.this,new String[]{ android.Manifest.permission.CALL_PHONE},1); }else { call(); }
接着就是咱們打電話的call()方法的操做,以及最後權限請求回來以後的回調方法:函數
// 防止有異常發生,寫在這裏 public void call(){ try { Intent intent = new Intent(Intent.ACTION_CALL); intent.setData(Uri.parse("tel:10086")); startActivity(intent); }catch (SecurityException e){ e.printStackTrace(); } } //全部的權限回調都是在這裏,咱們根據requestCode來判斷是哪一個權限 @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode){ case 1: if (grantResults.length>0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){ call(); }else { Toast.makeText(this,"打電話須要權限才能使用",Toast.LENGTH_LONG).show(); } } }
訪問其餘程序中的數據須要-ContentResolverthis
對於每個應用程序來講,若是你想要訪問內容提供器當中共享的數據,就必定要藉助 ContentResolver 類,能夠經過Context類當中的getContentResolver()方法來獲取該類的實例, ContentResolver 類當中也提供了一系列的方法用於對數據進行CRUD的操做, insert() 添加 update() 更新 delete() 刪除 query() 查詢 對象
不一樣於SQLiteDatabase,ContentResolver的CRUD的操做是不接收表名參數的,而是使用一個Uri參數表示。這個參數被稱爲內容URI,內容URI給內容提供器中的數據表創建了惟一的標識符,它主要是由兩部分組成,一部分是 authority,它是用於對不一樣的應用程序作區分,通常爲了不衝突,都會採起程序包的方式來進行命名, 另外一部分是path,path則是相對於同一應用程序中的表走區分的,一般都是添加在authority的後面。固然只有這兩部分仍是不夠的,咱們須要在前面加上協議聲明,所以標準的形式咱們舉個例子:
content://com.example.app.provider/table1 當中content://是咱們頭部的協議 com.example.app.provider是authority /table1就是path
上面咱們獲得一個URI字符串以後,咱們還須要將它解析成Uri對象才能使用,解析的方法也很簡單以下:
Uri uri = Uri.parse("content://com.example.app.provider/table1")
有了這個Uri這個對象以後,咱們就能夠利用它來查詢數據了,代碼以下:
Cursor cursor = getContentResolver().query(
uri, 指定查詢某一個應用下面的某張表
projection, 指定查詢的列名
selection, 指定where的約束條件
selectionArgs, 爲where中的佔位符提供具體的值
sortOrder); 指定查詢結果的排序方式
查詢完成以後返回的仍然是一個Cursor對象,這時候咱們就能夠將數據從Cursor對象中逐個讀取出來了,
讀取的思路仍然是經過移動遊標的位置來遍歷Cursor的全部行沒而後再取出每個行中國的數據沒代碼以下:
if(cursor != null){
while(cursor.moveNext()){
String column1 = cursor.getString(cursor.getColumnIndex("column1"));
Int column1 = cursor.getInt(cursor.getColumnIndex("column1"));
}
}
掌握了比較複雜的查詢以後,剩下的增長,刪除,修改就比較簡單了,咱們也就不在說了!
咱們讀取一下聯繫人
咱們這裏寫一個小demo,來讀取一下聯繫人的信息,而後把它展現在一個ListView當中,具體的代碼以下:
public class ContactsActivity extends AppCompatActivity { //適配器和一個數組,用來存儲聯繫人信息 ArrayAdapter<String> adapter; List<String> list = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_contacts); ListView contactsList = (ListView) findViewById(R.id.contactsListView); adapter = new ArrayAdapter<String>(ContactsActivity.this, android.R.layout.simple_list_item_1, list); contactsList.setAdapter(adapter); // 先檢查有沒有獲取通信錄的權限,要是沒有就先請求權限 if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED){ ActivityCompat.requestPermissions(this,new String[]{android.Manifest.permission.READ_CONTACTS},2); }else { readContacts(); } } // 獲取聯繫人 private void readContacts(){ Cursor cursor = null; try{ cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,null,null,null); if (cursor != null){ while(cursor.moveToNext()){ // 聯繫人的名稱 String displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); // 聯繫人的手機號 String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); list.add(displayName+"\n"+number); } // 給適配器發消息說數據改變了,這時候會從新刷新一次數據 adapter.notifyDataSetChanged(); } }catch (Exception e){ e.printStackTrace(); }finally { if (cursor != null){ cursor.close(); } } } // 獲取權限結果 @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode){ case 2: if (grantResults.length>0&& grantResults[0] == PackageManager.PERMISSION_GRANTED){ readContacts(); }else { Toast.makeText(this,"訪問通信錄須要權限",Toast.LENGTH_SHORT).show(); } } } }
建立本身的內容提供器
前面也說過,要是想實現跨程序之間的共享數據的功能,官方推薦的就是使用內容提供器,咱們能夠新建一個類去繼承ContentProvider的方式來建立一個本身的內容提供器。ContentProvider裏面的6個抽象方法咱們所有重寫
onCreate()
初始化內容提供器的時候調用,一般會在這裏完成數據庫的建立和升級操做,返回true表示成功,返回false表示失敗,注意的是隻有當ContentResolver嘗試訪問咱們的數據庫的時候內容提供器纔會被初始化
query()
從內容提供器中查詢數據,使用uri參數來肯定查詢哪張表,具體的參數咱們就不在說了,前面咱們已經說過,查詢的具體的結果就在cursor對象中存放返回
insert()
添加數據咱們也就再也不說了,成功以後會返回一個用於表示這條記錄的URI
update()
注意的就一點,受影響的函數將做爲返回值返回
delete()
這個和更新同樣也是將受影響的行數做爲返回值返回
getType()
根據傳入的內容URI來返回相應的MIME類型
方法具體的內容咱們就不在多說了,能夠本身點進類裏面去看看。有一點須要注意的就是URI,有一點須要咱們注意:
* 表示匹配任意長度的任意字符
# 表示匹配任意長度的數字
因此,咱們把一個可以匹配任意表的內容URI能夠寫成:content://com.example.app.provider/*
咱們把一個可以匹配表中任意一行數據的內容URI能夠寫成:content://com.example.app.provider/table1/#
最後還有一個問題,就是內容URI的匹配問題,有個類UriMatcher類能夠了解一下,咱們就不在多說了,想一想你匹配好了URI,當外面用戶進行CRUD操做的時候具體的返回內容就是咱們本身自定義的了!以上就是咱們要說的關於內容控制器的內容!