概述
Android 6.0開始,為了更好的保護用戶隱私,引入了新的權限機制:
- 普通權限與Android 6.0之前一樣,直接在Manifest中聲明即可
- 危險權限在使用之前告知用戶.獲得用戶授權之后才可以獲取相應的權限
- 用戶可以選擇不再提醒永久拒絕某個權限
危險權限列表
粗體字為權限組,權限組下面為權限組內的具體權限</br>
申請權限組中的任何一個權限即獲得了整個權限組中的所有權限CONTACTS
- READ_CONTACTS
- WIRTE_CONTACTS
- GET_CONTACTS
PHONE
- READ_CALL_LOG
- READ_PHONE_STATE
- CALL_PHONE
- WRITE_CALL_LOG
- USE_SIP
- PROCESS_OUTGOING_CALLS
- ADD_VOICEMAIL
CALENDAR
- READ_CALENDAR
- WRITE_CALENDAR
CAMERA
CAMERA
SENSORS
BODY_SENSORS
LOCATION
- ACCESS_FINE_LOCATION
- ACCESS_COARSE_LOCATION
STORAGE
- READ_EXTERNAL_STORAGE
- WRITE_EXTERNAL_STORAGE
MICROPHONE
- RECORD_AUDIO
SMS
- READ_SMS
- RECEIVE_WAP_PUSH
- RECEIVE_MMS
- RECEIVE_SMS
- SEND_SMS
- READ_CELL_BROADCASTS
使用安卓自帶Api申請運行時權限
以獲取用戶通訊錄為例
- 在Manifest中聲明我們需要的權限
//in Manifest
<uses-permission android:name="android.permission.READ_CONTACTS"/>
- 在Accitity中申請權限
大致的邏輯:</br>
-> 檢查用戶是否授權過該權限
-> 已經授權過 -> 執行業務邏輯
-> 沒有授權 -> 申請權限 -> 成功 -> 執行業務邏輯
API:
檢查用戶是否授權過該權限
int checkSelfPermission()
返回值:</br>
PackageManager.PERMISSION_GRANTED 表示有權限
PackageManager.PERMISSION_DENIED 表示無權限獲取權限
void requestPermissions()
三個參數:
Context
new String[]{} 需要申請權限的字符串數組
RequestCode 自定義的請求碼,結果回調的方法中使用,判斷是哪個權限的申請權限請求的結果回調
void onRequestPermissionsResult()
Example:
// 申請一個讀取通訊錄的權限
private final static int PERMISSION_CONTACT = 1;
private void getPermission() {
if (ContextCompat.checkSelfPermission(thisActivity,Manifest.permission.READ_CONTACTS)!= PackageManager.PERMISSION_GRANTED) {
// 無權限時去請求權限
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.READ_CONTACTS},PERMISSION_CONTACT);
}else{
// 有權限直接進行業務操作
doSomeWork();
}
}
/**
* 直接在Activity重寫 onRequestPermissionsResult()方法即可
* requestCode requetsPermissions()方法中傳入的RequestCode
* String[] permissons requetsPermissions()方法中傳入的permission數組
* grantResults 返回的具體結果
**/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,@NonNull int[] grantResults) {
switch(requestCode) {
case REQUEST_CONTACT:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 授予權限,撥打電話
doSomeWork();
} else {
Toast.makeText(this, "請求權限被拒絕", Toast.LENGTH_SHORT).show();
}
break;
}
}
用戶拒絕權限的處理
拒絕的邏輯:
申請權限 -> 用戶拒絕 -> END
再次申請權限 -> 權限彈框會多一個不再提醒的選項 -> 用戶勾選不再提醒選項并拒絕權限 -> END Forever
為了我們業務的正常進行,我們需要避免 END FOREVER的結果,所以在第二次申請權限的時候我們要提醒用戶不要拒絕我們的權限,安卓也為我們提供了這樣的API
- boolean shouldShowRequestPermissionRationale() 是否給用戶提醒權限的必要性
在代碼中區分是第一次還是被拒絕后再次申請權限
// 重寫一下getPermission()方法
private void getPermission() {
if (ContextCompat.checkSelfPermission(thisActivity,Manifest.permission.READ_CONTACTS)!= PackageManager.PERMISSION_GRANTED) {
// 無權限時去請求權限
if(ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.READ_CONTACTS)) {
// 通常是彈框告訴用戶權限的必要性,然后繼續申請權限
new AlertDialog.Builder(this)
.setCancelable(false)
.setMessage("求求你別拒絕~")
.setPositiveButton("好的", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.READ_CONTACTS},PERMISSION_CONTACT);
}
})
.setNegativeButton("不給", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.show();
} else {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.READ_CONTACTS},PERMISSION_CONTACT);
}
}else{
// 有權限直接進行業務操作
doSomeWork();
}
}
使用第三方框架申請權限
由于國內的各家手機廠商都對安卓進行了魔改,安卓所提供的原生的API可能會在一些手機上報錯,或者無法正常申請權限。對種類繁多的手機Rom和安卓版本都進行適配會非常繁瑣,推薦大家使用一些成熟的第三方框架去獲取手機權限。
框架并不能解決所有的問題
- 寫博客之前碰到的問題:
我的App需要申請相機權限,本人用的是小米/華為的手機,當時選擇了使用AndPermission申請權限。測試了幾次之后都挺正常。然后使用同事的魅族手機測試,在用戶第一次拒絕權限之后,第二次請求權限 gradResults 返回的是Granted.所以代碼會在沒有權限的情況下繼續向后執行,相機界面變成了一片黑色.
然后我又用原生Api嘗試了一下,結果是一樣的.所以原因應該是魅族手機的權限邏輯問題.
接下來去找解決問題的方案. 在AndPermission的Issus里面作者是這樣解釋的
在有些國產機上,申請權限時系統返回的總是有權限,對于通訊錄、讀寫SD卡等權限,AndPermission的策略是拿到有權限的結果后,再執行一下讀取一條通訊錄或者向SD卡寫入一個文件來驗證是否真的有權限。但是對于撥打電話此類權限,AndPermission不能真的去撥打一個電話,因此直接回調了onGranted(),開發者可以在onGranted()中直接撥打電話,來配合AndPermission申請權限,如果有就自然撥打出去了,如果沒有權限,上述手機會彈出授權對話框或者拋出異常,如果拋出異常,AndPermission會重新回調到onDenied()方法,開發者即可認為是沒有權限。
簡單分析下作者所說的內容:
通常的邏輯: getPermission -> granted() -> success
嚴謹的邏輯: getPermission -> granted() -> do() -> 捕獲由于權限造成的異常 -> onDenied() -> getPermission()
針對我上面的相機權限問題: 在申請權限成功進入相機界面之后,在相機啟動得時候相機打開失敗/獲取不到相機參數時,重新彈框去告訴用戶.所以還是得具體情況具體分析.
最后總結幾個申請權限的注意事項:
- 在需要的時候申請權限,不要在App開屏的時候請求所有的權限。體驗非常不友好,而且容易被拒絕
- 對于一些必要權限(用戶拒絕概率較高),在申請權限之前彈框提示告知用戶.
- 有些手機可能會返回錯誤的權限信息,獲取到權限提供的數據才算結束
- 參考AndPermission的做法,我們可以在代碼中拋出由于沒有權限引起的異常,再次申請權限。或者顯示ErrorView,讓用戶點擊授權。