Declaring Permissions(權(quán)限說明)
每一個(gè)安卓app都在一個(gè)有限制規(guī)則的環(huán)境下運(yùn)行曲秉。如果一個(gè)app需要獲得超出限制規(guī)則的資源或者信息差油。那么這個(gè)app就必須申請(qǐng)權(quán)限。作為開發(fā)者溉潭,我們可以在app manifest中列出app需要用到的權(quán)限环凿。
根據(jù)權(quán)限的敏感程度讥电,系統(tǒng)可以回自動(dòng)的授權(quán)或者彈出權(quán)限申請(qǐng)?jiān)儐枴@绮履辍H绻愕腶pp需要打開設(shè)備的閃光燈抡锈,那么系統(tǒng)會(huì)自動(dòng)授權(quán)疾忍。但是如果你的app需要用到用戶的通訊錄,系統(tǒng)就會(huì)對(duì)用戶申請(qǐng)權(quán)限床三。對(duì)于不同的安卓系統(tǒng)版本的權(quán)限管理一罩,在android 5.1或者更低的版本,用戶需要在安卓app的時(shí)候進(jìn)行授權(quán)撇簿,在android 6.0或者更高的版本后聂渊,用戶則是在app運(yùn)行時(shí)進(jìn)行授權(quán)。
Determine What Permissions Your App Needs(確定app需要的權(quán)限)
作為一個(gè)開發(fā)者补疑,當(dāng)你的app用到權(quán)限時(shí)歧沪,就應(yīng)該倍加小心。尤其是當(dāng)你的app需要用到涉及用戶的信息或者資源的權(quán)限和app的行為會(huì)影響到用戶的手機(jī)等設(shè)備(包括其他app)時(shí)莲组。例如诊胞,一個(gè)app需要在聯(lián)網(wǎng)時(shí)用相機(jī),或者打開锹杈、關(guān)閉wifi撵孤,那么這個(gè)app需要對(duì)用戶解釋這些權(quán)限。那么應(yīng)該先列出這些權(quán)限竭望,分別出哪些是nomal 權(quán)限邪码,哪些是dangerous權(quán)限。(normal和dangerous權(quán)限見網(wǎng)址:
當(dāng)app執(zhí)行需要某些需要用到權(quán)限的操作時(shí)咬清,才會(huì)用到權(quán)限闭专,因此最好在此時(shí)詢問權(quán)限。
如果另一個(gè)app操作你的app進(jìn)行某些需要權(quán)限的操作旧烧,你的app是不需要申請(qǐng)權(quán)限的影钉。
例如,如果你的app需要讀取用戶的通訊錄掘剪,就需要read_contacts權(quán)限平委,但是如果你的app打開另一個(gè) app,讓那個(gè)app去讀取用戶通訊錄夺谁,那么你的app就不需要任何權(quán)限廉赔,當(dāng)然,那個(gè)app就必須申請(qǐng)權(quán)限匾鸥。詳細(xì)情況見網(wǎng)址:https://developer.android.com/intl/zh-cn/training/permissions/best-practices.html#perms-vs-intents
Add Permissions to the Manifest(在manifest中添加權(quán)限)
為了說明你的app需要一個(gè)權(quán)限蜡塌,在manifest中用<uses-permission>去添加權(quán)限。例如扫腺,一個(gè)app需要用到發(fā)送短信的權(quán)限岗照,那么在manifest中就需要如下聲明:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.snazzyapp">
<uses-permission android:name="android.permission.SEND_SMS"/>
<application ...>
...
</application>
</manifest>
系統(tǒng)會(huì)根據(jù)權(quán)限的敏感程度做出不用的行為。如果這個(gè)權(quán)限不涉及用戶的任何隱私,系統(tǒng)會(huì)自動(dòng)授權(quán)攒至。如果這個(gè)權(quán)限涉及到用戶的敏感信息厚者,系統(tǒng)會(huì)申請(qǐng)獲取權(quán)限。詳情見網(wǎng)址:https://developer.android.com/guide/topics/security/permissions.html#normal-dangerous
Requesting Permissions at Run Time(運(yùn)行時(shí)獲取權(quán)限)
從Android6.0(api23)開始迫吐,在app運(yùn)行時(shí)库菲,用戶給予其權(quán)限而不是之前的在安裝的時(shí)候進(jìn)行授權(quán)。由于用戶在安裝或者更新app時(shí)不需要進(jìn)行授權(quán)志膀,這將簡化app的安裝過程熙宇。同時(shí)這也給予用戶更多的權(quán)限管理,例如溉浙,用戶可以給一款拍照app授權(quán)camera權(quán)限烫止,并拒絕location權(quán)限。用戶隨時(shí)在app的設(shè)置界面關(guān)閉權(quán)限戳稽。
系統(tǒng)的權(quán)限主要?jiǎng)澐譃?類馆蠕,normal和dangerous權(quán)限。
1.normal權(quán)限不直接涉及到用戶的隱私惊奇。如果app在manifest中添加了權(quán)限互躬,那么系統(tǒng)會(huì)自動(dòng)的授權(quán)。
2.dangerous權(quán)限會(huì)使用戶可以獲取用戶的隱私數(shù)據(jù)颂郎,如果app在manifest列出了normal權(quán)限吼渡,系統(tǒng)會(huì)自動(dòng)授權(quán)。如果在manifest列出了dangerous權(quán)限乓序,用戶需要對(duì)權(quán)限做出批準(zhǔn)或解決寺酪。
(獲取更多關(guān)系normal和dangerous 權(quán)限,可見:https://developer.android.com/guide/topics/security/permissions.html#normal-dangerous)
不管是哪個(gè)版本的安卓系統(tǒng)替劈,我們都需要在manifest中進(jìn)行權(quán)限的申明(不管是normal或者dangerous權(quán)限)房维。但是,申明完了之后系統(tǒng)會(huì)根據(jù)不同的sdk和android系統(tǒng)版本做出不同的處理抬纸。
1.如果設(shè)備運(yùn)行在android5.1或以下系統(tǒng)或者你的app的target sdk在22或者以下。那么當(dāng)你列出了一系列的dangerous權(quán)限的話耿戚,用戶則在安裝app的時(shí)候進(jìn)行授權(quán)湿故,如果用戶不同意授權(quán),那么app根本就不安裝了膜蛔。
2.如果手機(jī)時(shí)6.0或以上坛猪,并且你的app的target sdk大于等于23 。App必須在manifest中列出權(quán)限皂股,并且對(duì)dangerous權(quán)限進(jìn)行逐一的詢問授權(quán)墅茉,用戶可以接受或者拒絕任意一個(gè)dangerous權(quán)限,app可以在用戶拒絕權(quán)限后運(yùn)行。
注意:用android6.0(api23)開始就斤,即使app的target sdk小于23悍募,用戶也可以在任何時(shí)候去除權(quán)限,所以作為開發(fā)者洋机,你需要測試一下你的app在沒有某些情況下是否能正常運(yùn)行(無論你的target sdk是多少)坠宴。
這里將告訴你如何使用support library去檢測,申請(qǐng)權(quán)限绷旗。在android 的framework層提供了一些方法為android6.0服務(wù)喜鼓。為了使support library更簡化使用,開發(fā)者不需要在調(diào)用相關(guān)的方法前去檢測android的版本
在android 6.0中衔肢,權(quán)限分為各個(gè)權(quán)限組庄岖。如下圖,每個(gè)權(quán)限組中只要有1個(gè)權(quán)限被允許了角骤,其他權(quán)限也默認(rèn)允許了隅忿。也就是說如果在代碼中需要添加一個(gè)聯(lián)系人write_contacts權(quán)限的話,由于write_contacts和read_contacts都屬于android.permission-group.CONTACTS權(quán)限組启搂,read_contactst也就自動(dòng)授權(quán)了硼控。
在6.0中進(jìn)行權(quán)限開發(fā)
第一步 檢測權(quán)限
private boolean checkPermission(){
int result = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS);
if (result == PackageManager.PERMISSION_GRANTED){
return true;
} else {
return false;
}
}
第二步 如果沒有權(quán)限申請(qǐng)權(quán)限
private void requestPermission(){
if(ActivityCompat.shouldShowRequestPermissionRationale(activity,Manifest.permission.READ_CONTACTS)){
//解釋一下為什么需要申請(qǐng)這個(gè)權(quán)限 第二次或以后彈出權(quán)限詢問框時(shí)走此分支。并彈出的詢問框時(shí)帶有never ask again的勾選框
Snackbar.make(view, "為確保功能正常使用,我們需要您的授權(quán)胳赌。", Snackbar.LENGTH_INDEFINITE).setAction("ok", new View.OnClickListener() {
@Override
public void onClick(View view) {
ActivityCompat.requestPermissions(activity,new String[]{Manifest.permission.READ_CONTACTS},PERMISSION_REQUEST_CODE);
}
}).show();
} else {
ActivityCompat.requestPermissions(activity,new String[]{Manifest.permission.READ_CONTACTS},PERMISSION_REQUEST_CODE);
}
}
第一次點(diǎn)擊獲取權(quán)限的時(shí)候會(huì)走else分支牢撼,這時(shí)候彈出的詢問框是不會(huì)有never ask again的勾選框的。
當(dāng)用戶第一次點(diǎn)擊拒絕的時(shí)候疑苫,以后如果用戶再次點(diǎn)擊按鈕需要用到權(quán)限的時(shí)候
仍會(huì)彈出詢問框熏版,不過這時(shí)候彈出的詢問框是帶有never ask again的
第二次點(diǎn)擊按鈕的時(shí)候則會(huì)走if分支,這時(shí)候彈出的權(quán)限詢問是帶有never ask again的勾選框
第三次(如果第二次仍然拒絕)則和第二次一樣捍掺。除非勾選了never ask again撼短,當(dāng)勾選了checkbox后, allow會(huì)隱藏起來 挺勿。
因?yàn)槿绻坏c(diǎn)擊了allow 就授權(quán)了曲横,以后也不需要再詢問。那么如果app關(guān)閉了后,再次點(diǎn)擊按鈕需要權(quán)限的時(shí)候,就不會(huì)彈出詢問框了涩拙。
監(jiān)聽權(quán)限回調(diào)
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST_CODE:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Snackbar.make(view,"Permission Granted, Now you can access contacts data.",Snackbar.LENGTH_LONG).show();
Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);
try {
startActivity(intent);
} catch (Exception e) {
// TODO: handle exception
Toast.makeText(this, "無法訪問通信錄,請(qǐng)?jiān)O(shè)置通信錄訪問權(quán)限", Toast.LENGTH_SHORT).show();
}
} else {
Snackbar.make(view,"Permission Denied, You cannot access contacts data.",Snackbar.LENGTH_LONG).show();
}
break;
}
}