參考書籍:《第一行代碼》 第二版 郭霖
如有錯(cuò)漏奠旺,請(qǐng)批評(píng)指出蜘澜!
內(nèi)容提供器(Content Provider)主要用于在不同應(yīng)用程序之間實(shí)現(xiàn)數(shù)據(jù)共享功能(即跨程序數(shù)據(jù)共享)。它提供了一套完整的機(jī)制鄙信,允許一個(gè)程序訪問另一個(gè)程序的數(shù)據(jù),同時(shí)還能保證被訪問數(shù)據(jù)的安全性稽寒。目前扮碧,使用 Content Provider 是Android實(shí)現(xiàn)跨程序共享數(shù)據(jù)的標(biāo)準(zhǔn)方式趟章。Content Provider可以選擇只對(duì)某一部分?jǐn)?shù)據(jù)進(jìn)行共享杏糙,從而保證我們程序中的隱私數(shù)據(jù)不會(huì)有泄露的風(fēng)險(xiǎn)。
由于后面關(guān)于Content Provider的內(nèi)容會(huì)大量涉及運(yùn)行時(shí)權(quán)限蚓土,我們先來了解一下關(guān)于Android運(yùn)行時(shí)權(quán)限的內(nèi)容宏侍。
Android運(yùn)行時(shí)權(quán)限
-
關(guān)于Android權(quán)限機(jī)制
在Android開發(fā)過程中,我們會(huì)經(jīng)常遇到某些功能需要設(shè)備的一些權(quán)限蜀漆,比如讀取聯(lián)系人谅河、開啟攝像頭等。在Android6.0之前确丢,這個(gè)問題是很好解決的绷耍,只需要在AndroidManifest文件中聲明對(duì)應(yīng)權(quán)限即可,當(dāng)用戶安裝程序時(shí)鲜侥,這些權(quán)限都會(huì)一一列出來褂始,用戶繼續(xù)安裝就意味著同意應(yīng)用所請(qǐng)求的所有權(quán)限,這也就導(dǎo)致了很多應(yīng)用程序?yàn)E用權(quán)限的情況描函,造成了用戶隱私的安全問題崎苗。如果我們?cè)试S判莉,就意味著對(duì)這個(gè)權(quán)限授權(quán)了齿诞,后面不需要再請(qǐng)求,如果我們拒絕了骂租,這個(gè)功能就無法使用祷杈,除非你在手機(jī)的權(quán)限管理中手動(dòng)授予應(yīng)用這個(gè)權(quán)限。
當(dāng)然渗饮,并不是所有的權(quán)限都需要?jiǎng)討B(tài)申請(qǐng)但汞,在加入運(yùn)行時(shí)權(quán)限功能后,Android將權(quán)限分為兩個(gè)大類互站,一類是普通權(quán)限(和Android6.0之前一樣私蕾,系統(tǒng)會(huì)自動(dòng)授權(quán)),另一類是危險(xiǎn)權(quán)限胡桃,即需要我們動(dòng)態(tài)申請(qǐng)的權(quán)限踩叭。下面是Android中所有的危險(xiǎn)權(quán)限,一共9組24個(gè)權(quán)限:
也就是說翠胰,在Android6.0以上的系統(tǒng)使用上面這些權(quán)限時(shí)容贝,我們不僅要在AndroidManifest文件中聲明,還需要?jiǎng)討B(tài)申請(qǐng)之景,不在這張表里的權(quán)限斤富,我們只需要在AndroidManifest文件中聲明一下即可。上面這些是關(guān)于Android權(quán)限機(jī)制的內(nèi)容锻狗,接下來我們來看看如何動(dòng)態(tài)申請(qǐng)權(quán)限满力。
-
在程序運(yùn)行時(shí)申請(qǐng)權(quán)限
在上面的表中,PHONE組有一個(gè) CALL_PHONE權(quán)限轻纪,這個(gè)權(quán)限是撥打電話需要的油额,我們現(xiàn)在就使用這個(gè)權(quán)限來寫一個(gè)撥打10086的功能。- 首先在界面中添加一個(gè)Button刻帚,用來觸發(fā)撥打電話(很簡單潦嘶,不貼代碼了)。
- 在AndroidManifest文件中聲明權(quán)限.
<uses-permission android:name="android.permission.CALL_PHONE"/>
- 在Activity中編寫點(diǎn)擊按鈕觸發(fā)撥打電話的邏輯
public class CpMainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.cp_activity_main); ButterKnife.bind(this); initView(); } private void initView() { Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar); setSupportActionBar(toolbar); } @OnClick(R.id.but_call) public void onClick(View v) { switch (v.getId()){ default: break; case R.id.but_call: try { Intent intent = new Intent(Intent.ACTION_CALL); intent.setData(Uri.parse("tel:10086")); startActivity(intent); }catch (Exception e){ e.printStackTrace(); } break; } } }
只需要看onClick()方法中的內(nèi)容我擂,邏輯很簡單衬以,就是當(dāng)我們點(diǎn)擊我們定義的Button時(shí),定義一個(gè)隱式Intent校摩,Intent的action指定為 “Intent.ACTION_CALL”看峻,這是一個(gè)系統(tǒng)內(nèi)置的打電話的動(dòng)作,然后data部分指定協(xié)議為 tel 衙吩,號(hào)碼是10086互妓,也就是當(dāng)我們點(diǎn)擊Button時(shí),會(huì)直接撥打10086,(寫在try catch 語句塊中是為了防止程序崩潰)。不過冯勉,這是在Android 6.0 之前的寫法澈蚌,如果手機(jī)的Android版本低于6.0,這么寫是可以灼狰。但是在Android6.0以上的系統(tǒng)中宛瞄,我們點(diǎn)擊Button是沒有反應(yīng)的(可以自行測試一下)。下面我們來看在Android6.0以上的系統(tǒng)中要如何申請(qǐng)權(quán)限:
@OnClick(R.id.but_call) public void onClick(View v) { switch (v.getId()){ default: break; case R.id.but_call: if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED){ ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, 1); }else { try { Intent intent = new Intent(Intent.ACTION_CALL); intent.setData(Uri.parse("tel:10086")); startActivity(intent); }catch (Exception e){ e.printStackTrace(); } } break; } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode){ default: break; case 1: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){ try { Intent intent = new Intent(Intent.ACTION_CALL); intent.setData(Uri.parse("tel:10086")); startActivity(intent); }catch (Exception e){ e.printStackTrace(); } }else { ToastUtil.showShortToast(this, "您已拒絕電話權(quán)限交胚,請(qǐng)手動(dòng)開啟"); } break; } }
首先份汗,在事件觸發(fā)時(shí),我們需要調(diào)用 ContextCompat 的 checkSelfPermission(Context context, String permission)方法來驗(yàn)證我們需要使用的權(quán)限是否授權(quán)蝴簇,具體的權(quán)限我們使用 Manifest.permission 來獲取杯活,這個(gè)方法有一個(gè)返回值,如果和 PackageManager.PERMISSION_GRANTED 相等熬词,說明用戶已經(jīng)授權(quán)旁钧,我們可以撥打電話,否則我們就需要進(jìn)行申請(qǐng)互拾。使用 ActivityCompat 的 requestPermissions(Activiti activity, String[] permissions, int requestCode) 方法歪今,可以向用戶申請(qǐng)權(quán)限,這個(gè)方法接收三個(gè)參數(shù)摩幔,第一個(gè)參數(shù)即當(dāng)前Activity彤委,第二個(gè)參數(shù)是一個(gè)String數(shù)組鞭铆,也就是說可以同時(shí)申請(qǐng)多條權(quán)限或衡,將權(quán)限名放入數(shù)組即可,第三個(gè)參數(shù)是一個(gè)請(qǐng)求碼车遂,用來區(qū)分我們申請(qǐng)權(quán)限的位置封断,只要是唯一值就行。
在調(diào)用完requestPermission()方法后舶担,系統(tǒng)會(huì)彈出一個(gè)申請(qǐng)權(quán)限的對(duì)話框坡疼,用戶可以選擇同意或者拒絕,無論用戶選擇什么衣陶,最后都會(huì)回調(diào) onRequestPermissionsResult() 方法柄瑰,這時(shí)我們需要再來檢查用戶是否授權(quán),授權(quán)了就執(zhí)行我們的邏輯(即撥打電話)剪况,否則教沾,我們可以彈出一條Toast用來提示操作失敗。下面來看看效果:
關(guān)于動(dòng)態(tài)權(quán)限的內(nèi)容就到這里译断,下面來看關(guān)于Content Provider 的用法授翻。Content Provider的用法一般有兩種,一種是使用現(xiàn)有的Content Provider 來讀取和操作相應(yīng)程序中的數(shù)據(jù),另一種是創(chuàng)建自己的內(nèi)容提供器堪唐,給我們程序的數(shù)據(jù)提供外部訪問接口巡语。
訪問其他程序中的數(shù)據(jù)
-
ContentResolver的基本用法
一個(gè)應(yīng)用程序想要訪問其他程序通過Content Provider共享的數(shù)據(jù),就需要用到Content Resolver 類淮菠,可以通過Context的getContentResolver() 方法獲取到該類的實(shí)例男公,ContentResolver中提供了一系列方法用于對(duì)數(shù)據(jù)庫進(jìn)行CRUD操作,和SQLiteDatabase的CRUD操作相似合陵。不過ContentResolver 的CRUD方法不接收表名參數(shù)理澎,而是使用一個(gè)Uri參數(shù)代替,這個(gè)參數(shù)被稱為內(nèi)容URI曙寡。
內(nèi)容URI給Content Provider 中的數(shù)據(jù)建立了唯一的標(biāo)示符糠爬,它主要由兩部分組成:authority和path。authority可以用包名來命名举庶,用于區(qū)分應(yīng)用程序执隧;path用于區(qū)分程序中的表。例如某個(gè)程序的包名是com.laughter.learnapplication户侥,并且數(shù)據(jù)庫中有兩張表table1和table2镀琉,那么內(nèi)容URI就是:
content://com.laughter.learnapplication/table1
content://com.laughter.learnapplication/table2
“ content:// " 是協(xié)議聲明,需要加在頭部蕊唐。這樣看來屋摔,內(nèi)容URI可以清楚表達(dá)我們想要訪問那個(gè)程序的哪張表,也可以準(zhǔn)確標(biāo)識(shí)我們想要訪問的應(yīng)用程序中的數(shù)據(jù)替梨。
-
查詢數(shù)據(jù)
想要查詢Content Provider中的數(shù)據(jù)钓试,我們首先要調(diào)用 Uri 的 parse() 方法將內(nèi)容URI解析成Uri對(duì)象,Uri uri = Uri.parse("content://com.laughter.learnapplication/table1");
接下來就可以查詢數(shù)據(jù)了:
下表是查詢語句中的參數(shù)說明:Cursor cursor = getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);
查詢完成后返回的是一個(gè) Cursor 對(duì)象,我們需要從 Cursor 對(duì)象中將數(shù)據(jù)逐條讀出:
if(cursor != null){ while(cursor.moveToNext()){ String name = cursor.getString(cursor.getColumnIndex("name")); String num = cursor.getInt(cursor.getColumnIndex("num")); } cursor.close(); }
-
添加數(shù)據(jù)
ContentValues values = new ContentValues(); values.put("name", name); values.put("num", num); getContentResolver().insert(uri, values);
添加數(shù)據(jù)和SQLiteDatabase一樣副瀑,要借助于ContentValues弓熏;
-
修改數(shù)據(jù)
假設(shè)要修改 name 為 laughter,num 為 0 這一條數(shù)據(jù)的 num 屬性ContentValues values = new ContentValues(); values.put("num", num); getContentResolver().update(uri, values, "name = ? and num = ?", new String[] { "laughter", "0" });
我們使用了 selection 和 selectionArgs這兩個(gè)參數(shù)對(duì)想要更新的數(shù)據(jù)進(jìn)行約束糠睡,保證修改的是我們想要修改的數(shù)據(jù)挽鞠。
-
刪除數(shù)據(jù)
getContentResolver().delete(uri, "name = ?", new String[] { "laughter" });
操作和SQLiteDatabase 相似,很容易理解狈孔。
-
讀取系統(tǒng)聯(lián)系人
接下來根據(jù)上面的內(nèi)容實(shí)現(xiàn)一個(gè)讀取系統(tǒng)聯(lián)系人并顯示出來的小應(yīng)用信认。
-
首先在Activity布局中添加一個(gè)ListView
<ListView android:id="@+id/list_view" android:layout_width="match_parent" android:layout_height="match_parent" />
-
查詢聯(lián)系人數(shù)據(jù),并使用ListView顯示出來均抽,簡單起見嫁赏,我們使用系統(tǒng)提供的 ArrayAdapter 以及 android.R.layout.simple_list_item_1 布局
public class MainActivity extends AppCompatActivity { @BindView(R.id.list_view) ListView listView; private ArrayAdapter<String> adapter; private List<String> contctsList = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); initView(); } private void initView() { adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, contctsList); listView.setAdapter(adapter); if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED){ ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CONTACTS}, 0); }else { quaryContacts(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode){ case 0: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){ quaryContacts(); }else { ToastUtil.showShortToast(this, "您已拒絕授權(quán),無法讀取您的手機(jī)聯(lián)系人"); } break; default: break; } } private void quaryContacts() { Cursor cursor = null; try{ cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null); if (cursor != null){ while (cursor.moveToNext()){ String name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); String tel = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); contctsList.add("\n" + name + "\n\n" + tel + "\n"); } adapter.notifyDataSetChanged(); } }catch (Exception e){ e.printStackTrace(); }finally { if (cursor != null){ cursor.close(); } } } }
關(guān)于ListView的使用在前面的文章 Android基礎(chǔ)回顧(三)| 常用控件 — ListView和RecyclerView 中已經(jīng)講得很清楚了到忽,ButterKnife 的使用也講過 Android實(shí)踐(二) | 注解框架ButterKnife基本使用 橄教,我們直接看代碼的邏輯清寇,首先初始化ListView,然后檢查權(quán)限护蝶,這里使用的 READ_CONTACTS 權(quán)限屬于危險(xiǎn)權(quán)限华烟,也是需要在AndroidMenifest文件中聲明,并且動(dòng)態(tài)申請(qǐng)的:
<uses-permission android:name="android.permission.READ_CONTACTS"/>
用戶授權(quán)后持灰,就開始查詢手機(jī)中的聯(lián)系人盔夜,這里我們的Uri參數(shù)給的是 ContactsContract.CommonDataKinds.Phone.CONTENT_URI,這是Android開發(fā)團(tuán)隊(duì)封裝好的堤魁,我們可以打印出來看看:
就是這樣的一個(gè)值喂链,我們直接使用就行,因?yàn)槲覀円@取所有聯(lián)系人妥泉,所以不需要加約束條件椭微,后面的參數(shù)都傳null就行了。接著遍歷查詢的結(jié)果 Cursor 對(duì)象盲链,ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME 和 ContactsContract.CommonDataKinds.Phone.NUMBER 分別是聯(lián)系人姓名和聯(lián)系人號(hào)碼對(duì)應(yīng)的常量蝇率,直接使用,取出數(shù)據(jù)后逐條添加到ListView的數(shù)據(jù)源中刽沾,然后更新一下ListView本慕,最后別忘了關(guān)閉Cursor對(duì)象。下面來運(yùn)行一下看看效果吧:
上一篇:Android基礎(chǔ)回顧(五)| 數(shù)據(jù)存儲(chǔ)——持久化技術(shù)
下一篇:Android基礎(chǔ)回顧(七)| 使用手機(jī)多媒體