
為什么需要6.0運(yùn)行時(shí)權(quán)限
- 更友好
6.0以前的安裝時(shí)權(quán)限,會(huì)在應(yīng)用安裝時(shí)列出所有需要的權(quán)限,當(dāng)列出一些危險(xiǎn)權(quán)限時(shí),用戶不知每個(gè)權(quán)限的具體用途,可能因?yàn)檫@些權(quán)限警告而放棄安裝應(yīng)用。對于一些非裝不可的應(yīng)用,用戶則不得不被迫接受所有權(quán)限,很容易安裝了一些流氓APP,體驗(yàn)不佳。
6.0以后的運(yùn)行時(shí)權(quán)限,可以在調(diào)用相關(guān)功能之前判斷權(quán)限授權(quán)狀態(tài),并自定義提示彈框告知用戶權(quán)限用途,使用戶清楚了解之后,再授權(quán)使用。

- 更穩(wěn)定
6.0系統(tǒng)的手機(jī)對于每個(gè)應(yīng)用,都有個(gè)權(quán)限設(shè)置頁面,可以手動(dòng)開關(guān)權(quán)限,如果用戶在設(shè)置頁面誤關(guān)了某個(gè)權(quán)限,若沒在程序運(yùn)行時(shí)做判斷,則會(huì)導(dǎo)致相關(guān)功能的調(diào)用失敗,引起崩潰等。

如何實(shí)現(xiàn)運(yùn)行時(shí)權(quán)限
- 設(shè)置targetSdkVersion
只有targetSdkVersion>=23,且安裝在6.0以上的手機(jī)時(shí),運(yùn)行時(shí)權(quán)限機(jī)制才能正常運(yùn)作
手機(jī)系統(tǒng) | targetSdkVersion<23 | targetSdkVersion>=23 |
---|---|---|
5.0+ | 安裝時(shí)權(quán)限 | 安裝時(shí)權(quán)限 |
6.0+ | 安裝時(shí)權(quán)限 | 運(yùn)行時(shí)權(quán)限 |
- 代碼實(shí)現(xiàn)
在需要使用某項(xiàng)權(quán)限時(shí),通過V4包的checkSelfPermission判斷權(quán)限是否授權(quán),通過requestPermissions申請某項(xiàng)權(quán)限
// 需要執(zhí)行某項(xiàng)需要權(quán)限的操作時(shí)
if (ContextCompat.checkSelfPermission(this, "某項(xiàng)權(quán)限") == PackageManager.PERMISSION_GRANTED) {
// 執(zhí)行操作
} else {
// 請求權(quán)限
ActivityCompat.requestPermissions(this, new String[]{"某項(xiàng)權(quán)限"}, requestCode);
}
在Activity的onRequestPermissionsResult回調(diào)方法中,處理權(quán)限授權(quán)結(jié)果
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 授權(quán)成功,執(zhí)行操作
} else {
// 權(quán)限被拒絕
if (ActivityCompat.shouldShowRequestPermissionRationale(this, "權(quán)限名稱")) {
// shouldShowRequestPermissionRationale=true: 表示權(quán)限被拒絕,且沒有勾選"never ask again"
// 正常的權(quán)限被拒絕流程,可以繼續(xù)申請權(quán)限,重復(fù)以上流程
} else {
// shouldShowRequestPermissionRationale=false: 在權(quán)限被拒絕,且勾選"never ask again"的情況下,返回false
// 繼續(xù)申請權(quán)限的時(shí)候,不會(huì)再彈出默認(rèn)的系統(tǒng)彈框,需要自定義提示彈框,并引導(dǎo)用戶去權(quán)限設(shè)置頁面,手動(dòng)開啟權(quán)限
}
}
}
LsPermission工具類的使用
- 主要實(shí)現(xiàn)邏輯參考PermissionGen,封裝了權(quán)限判斷,請求,結(jié)果處理等通用邏輯,目前只支持context=AppCompatActivity,如果在Fragment中使用時(shí)可以調(diào)用getActivity()獲取上層AppCompatActivity。

private void normal() {
final String[] permissions =
new String[] {Manifest.permission.CALL_PHONE, Manifest.permission.ACCESS_FINE_LOCATION};
PermissionUtil.request(this, permissions, new OnPermissionAdapter() {
/**
* @desc 申請的權(quán)限全被授權(quán)
*/
@Override
public void onGrant() {
showToast("權(quán)限被同意");
callPhone();
}
/**
* @desc 權(quán)限被拒絕
*/
@Override
public void onDeny(List<String> permissions) {
showToast("用戶拒絕授權(quán)" + permissions.toString());
}
/**
* @desc 權(quán)限被拒絕,且勾選"never ask again"
*/
@Override
public void onNeverAsk(List<String> permissions) {
showToast("用戶拒絕授權(quán), 并勾選 never ask again " + permissions.toString());
PermissionUtil.showNeverAskDialog(MainActivity.this, "這個(gè)權(quán)限很重要");
}
/**
* @desc 無論權(quán)限授權(quán)成功還是失敗,都會(huì)回調(diào)
*/
@Override
public void always(List<String> grantPermissions, List<String> denyPermissions,
List<String> foreverDenyPermissions) {
showToast("授權(quán): " + grantPermissions.toString() + "\n 拒絕: " + denyPermissions.toString()
+ "\n never ask: " + foreverDenyPermissions.toString());
}
});
}
/**
* @desc 將權(quán)限回調(diào)轉(zhuǎn)發(fā)給PermissionUtil處理
* @author listen
* @date 2017/2/24 13:47
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
PermissionUtil.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
- PermissionUtil內(nèi)部通過SparseArray保存了requestCode和權(quán)限回調(diào)的鍵值對,避免了連續(xù)點(diǎn)擊時(shí)重復(fù)回調(diào)情況,對于不同的權(quán)限申請,推薦使用不同的requestCode,避免回調(diào)覆蓋的問題。
private static SparseArray<OnPermissionListener> mPermissionRequestList = new SparseArray<>();
public static void request(final Context context, int requestCode, String[] permissions,
OnPermissionListener listener) {
/** 存在未授權(quán)的權(quán)限 */
synchronized (mPermissionRequestList) {
if (null != mPermissionRequestList.get(requestCode)) {
/** 當(dāng)前權(quán)限請求已經(jīng)存在,不重復(fù)添加 */
log("the same permission is requesting");
} else {
/** 將當(dāng)前權(quán)限請求加入隊(duì)列 */
/** 如果一個(gè)頁面中存在分別觸發(fā)A, B多個(gè)權(quán)限的情況, 則最好將不同權(quán)限申請對應(yīng)不同的requestCode, 存入SparseArray分別處理 */
mPermissionRequestList.put(requestCode, listener);
/** 執(zhí)行權(quán)限申請 */
ActivityCompat.requestPermissions();
}
}
}
在權(quán)限回調(diào)的時(shí)候從SparseArray移除Listener
public static void onRequestPermissionsResult(Context context, int requestCode, String[] permissions,
int[] grantResults) {
final OnPermissionListener listener;
synchronized (mPermissionRequestList) {
listener = mPermissionRequestList.get(requestCode);
mPermissionRequestList.remove(requestCode);
}
if (null != listener) {
/** 執(zhí)行回調(diào) */
listener.onGrant();
listener.onDeny();
listener.onNeverAsk();
listener.always();
} else {
log("request is not exists");
}
}
代碼地址:LsPermission
除了基本的權(quán)限申請邏輯的封裝以外,還寫了類似微信,支付寶,百度地圖等在啟動(dòng)頁的權(quán)限申請Demo,算是PermissionUtil的簡單運(yùn)用。
參考:
Android 6.0 運(yùn)行時(shí)權(quán)限處理完全解析
Android 6.0 運(yùn)行時(shí)權(quán)限管理最佳實(shí)踐
聊一聊Android 6.0的運(yùn)行時(shí)權(quán)限