前言:
Android 6.0 為了保護(hù)用戶隱私,將一些權(quán)限的申請(qǐng)放在了應(yīng)用運(yùn)行的時(shí)候去申請(qǐng), 比如以往的開(kāi)發(fā)中,開(kāi)發(fā)人員只需要將需要的權(quán)限在清單文件中配置即可,安裝后用戶可以在設(shè)置中的應(yīng)用信息中看到:XX應(yīng)用以獲取****權(quán)限。用戶點(diǎn)擊可以選擇給應(yīng)用相應(yīng)的權(quán)限。此前的應(yīng)用權(quán)限用戶可以選擇允許、提醒和拒絕。在安裝的時(shí)候用戶是已經(jīng)知道應(yīng)用需要的權(quán)限的。但是這樣存在一個(gè)問(wèn)題,就是用戶在安裝的時(shí)候,應(yīng)用需要的權(quán)限十分的多(有些開(kāi)發(fā)者為了省事,會(huì)請(qǐng)求一些不必要的權(quán)限或者請(qǐng)求全部的權(quán)限),這個(gè)時(shí)候用戶在安裝應(yīng)用的時(shí)候也許并沒(méi)有發(fā)現(xiàn)某些侵犯自己隱私的權(quán)限請(qǐng)求,安裝之后才發(fā)現(xiàn)自己的隱私數(shù)據(jù)被竊取。其實(shí)Android6.0 動(dòng)態(tài)權(quán)限一方面是為了廣大用戶考慮,另一方面其實(shí)是Google為了避免一些不必要的官司。
目前針對(duì)Android6.0權(quán)限適配的邏輯代碼,網(wǎng)上已經(jīng)很多了。這里針對(duì)用戶拒絕了權(quán)限請(qǐng)求并且選擇了□ 不在提示”時(shí)該如何處理。
權(quán)限流程:
▲在Api 23中, 權(quán)限需要?jiǎng)討B(tài)獲取, 核心權(quán)限必須滿足. 標(biāo)準(zhǔn)流程:
▲但這里有個(gè)問(wèn)題,那就是在系統(tǒng)授權(quán)彈窗環(huán)節(jié),提醒框會(huì)有個(gè)不再提示的復(fù)選框,如果用戶點(diǎn)擊不再提示,并拒絕授權(quán),那么再下次授權(quán)的時(shí)候,系統(tǒng)授權(quán)彈窗的提示框就不會(huì)在提示,所以我們很有必要需要自定義權(quán)限彈窗提示框,那么流程圖就變成如下了。
權(quán)限類型:
▲ 在圖中,我們可以看到整個(gè)權(quán)限里,可以分為系統(tǒng)權(quán)限和特殊權(quán)限授權(quán)。系統(tǒng)權(quán)限中,又分為normal和dangerous類型。
normal:這個(gè)權(quán)限類型并不直接威脅到用戶的隱私,可以直接在manifest清單里注冊(cè),系統(tǒng)會(huì)幫我們默認(rèn)授權(quán)的。
dangerous:這個(gè)可以直接給app訪問(wèn)用戶一些敏感的數(shù)據(jù),不僅需要在manifest清單里注冊(cè),同時(shí)在使用的時(shí)候,需要向系統(tǒng)請(qǐng)求授權(quán)。
值得注意一點(diǎn),這里有特殊權(quán)限授權(quán)的區(qū)別,分別是SYSTEM_ALERT_WINDOW 和 WRITE_SETTINGS,雖然這兩個(gè)權(quán)限也是屬于dangerous權(quán)限類型,但是這兩個(gè)授權(quán)請(qǐng)求方式和其他dangerous權(quán)限是不一樣的,需要特殊處理 。
案例介紹:
根據(jù)官方介紹,當(dāng)發(fā)起首次權(quán)限請(qǐng)求時(shí)不會(huì)提供“不再提示”這一選擇,但如果用戶之前拒絕過(guò)權(quán)限請(qǐng)求并且再次發(fā)起權(quán)限請(qǐng)求時(shí),權(quán)限請(qǐng)求對(duì)話框才會(huì)提供“不在提示”這一選項(xiàng)供用戶選擇,當(dāng)用戶選擇了“不再提示”之后就不會(huì)再?gòu)棾鰴?quán)限請(qǐng)求對(duì)話框。各大手機(jī)廠商的定制系統(tǒng)對(duì)該邏輯進(jìn)行了一定的改動(dòng),例如小米MIUI系統(tǒng)當(dāng)用戶首次選擇拒絕權(quán)限時(shí)之后就會(huì)“不在提示”了,華為EMUI系統(tǒng)在首次彈出權(quán)限請(qǐng)求時(shí)就會(huì)提供“不在提示”的選項(xiàng),三星部分系統(tǒng)始終不提供“不在提示”的選項(xiàng)。
當(dāng)用戶選擇了“不在提示”時(shí),開(kāi)發(fā)者需要引導(dǎo)用戶去設(shè)置頁(yè)手動(dòng)授權(quán)!
但是android 6.0系統(tǒng)并未提供對(duì)“不在提示”這一選項(xiàng)的監(jiān)聽(tīng),那么開(kāi)發(fā)者該如何判斷用戶是否選擇了“不在提示”這一選項(xiàng)呢?
答案是通過(guò)shouldShowRequestPermissionRationale()這一方法。
這一方法的作用是判斷是否需要向用戶解釋為何需要請(qǐng)求該權(quán)限。
當(dāng)首次發(fā)起權(quán)限請(qǐng)求時(shí)該方法返回false。
當(dāng)?shù)诙螜?quán)限請(qǐng)求時(shí)該方法返回true。
當(dāng)發(fā)起第二次權(quán)限請(qǐng)求并且當(dāng)用戶選擇了“不再提示”這一選項(xiàng)時(shí)該方法返回false。 通過(guò)這個(gè)邏輯我們可以反推出:當(dāng)進(jìn)行第二次權(quán)限請(qǐng)求被拒絕并且shouldShowRequestPermissionRationale()返回false時(shí),那么該用戶一定是選擇了“不再提示”這一選項(xiàng)。
▲以RECORD_AUDIO,CAMERA,READ_EXTERNAL_STORAGE為例。全部權(quán)限請(qǐng)求成功,直接看妹砸。下面看圖 ヾ(?°?°?)?? 沒(méi)圖我會(huì)亂說(shuō)
▲當(dāng)部分權(quán)限受限,且用戶未選中“不再提示”,彈出對(duì)話框說(shuō)明,并進(jìn)行請(qǐng)求權(quán)限
▲當(dāng)部分權(quán)限受限,且用戶選中“不再提示”,彈出對(duì)話框說(shuō)明,引導(dǎo)用戶到設(shè)置頁(yè)手動(dòng)授權(quán)
▲主要代碼BaseActivity.java
package com.donkor.demo.permission;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
public class BaseActivity extends AppCompatActivity {
private final int mRequestCode = 1024;
private RequestPermissionCallBack mRequestPermissionCallBack;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
/**
* 權(quán)限請(qǐng)求結(jié)果回調(diào)
*
* @param requestCode
* @param permissions
* @param grantResults
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
boolean hasAllGranted = true;
StringBuilder permissionName = new StringBuilder();
for (String s : permissions) {
permissionName = permissionName.append(s + "\r\n");
}
switch (requestCode) {
case mRequestCode: {
for (int i = 0; i < grantResults.length; ++i) {
if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
hasAllGranted = false;
//在用戶已經(jīng)拒絕授權(quán)的情況下,如果shouldShowRequestPermissionRationale返回false則
// 可以推斷出用戶選擇了“不在提示”選項(xiàng),在這種情況下需要引導(dǎo)用戶至設(shè)置頁(yè)手動(dòng)授權(quán)
if (!ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[i])) {
new AlertDialog.Builder(BaseActivity.this).setTitle("PermissionTest")//設(shè)置對(duì)話框標(biāo)題
.setMessage("【用戶選擇了不再提示按鈕,或者系統(tǒng)默認(rèn)不在提示(如MIUI)。" +
"引導(dǎo)用戶到應(yīng)用設(shè)置頁(yè)去手動(dòng)授權(quán),注意提示用戶具體需要哪些權(quán)限】" +
"獲取相關(guān)權(quán)限失敗:" + permissionName +
"將導(dǎo)致部分功能無(wú)法正常使用,需要到設(shè)置頁(yè)面手動(dòng)授權(quán)")//設(shè)置顯示的內(nèi)容
.setPositiveButton("去授權(quán)", new DialogInterface.OnClickListener() {//添加確定按鈕
@Override
public void onClick(DialogInterface dialog, int which) {//確定按鈕的響應(yīng)事件
//TODO Auto-generated method stub
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getApplicationContext().getPackageName(), null);
intent.setData(uri);
startActivity(intent);
dialog.dismiss();
}
}).setNegativeButton("取消", new DialogInterface.OnClickListener() {//添加返回按鈕
@Override
public void onClick(DialogInterface dialog, int which) {//響應(yīng)事件
// TODO Auto-generated method stub
dialog.dismiss();
}
}).setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
mRequestPermissionCallBack.denied();
}
}).show();//在按鍵響應(yīng)事件中顯示此對(duì)話框
} else {
//用戶拒絕權(quán)限請(qǐng)求,但未選中“不再提示”選項(xiàng)
mRequestPermissionCallBack.denied();
}
break;
}
}
if (hasAllGranted) {
mRequestPermissionCallBack.granted();
}
}
}
}
/**
* 發(fā)起權(quán)限請(qǐng)求
*
* @param context
* @param permissions
* @param callback
*/
public void requestPermissions(final Context context, final String[] permissions,
RequestPermissionCallBack callback) {
this.mRequestPermissionCallBack = callback;
StringBuilder permissionNames = new StringBuilder();
for (String s : permissions) {
permissionNames = permissionNames.append(s + "\r\n");
}
//如果所有權(quán)限都已授權(quán),則直接返回授權(quán)成功,只要有一項(xiàng)未授權(quán),則發(fā)起權(quán)限請(qǐng)求
boolean isAllGranted = true;
for (String permission : permissions) {
if (ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_DENIED) {
isAllGranted = false;
if (ActivityCompat.shouldShowRequestPermissionRationale((Activity) context, permission)) {
new AlertDialog.Builder(BaseActivity.this).setTitle("PermissionTest")//設(shè)置對(duì)話框標(biāo)題
.setMessage("【用戶曾經(jīng)拒絕過(guò)你的請(qǐng)求,所以這次發(fā)起請(qǐng)求時(shí)解釋一下】" +
"您好,需要如下權(quán)限:" + permissionNames +
" 請(qǐng)?jiān)试S,否則將影響部分功能的正常使用。")//設(shè)置顯示的內(nèi)容
.setPositiveButton("確定", new DialogInterface.OnClickListener() {//添加確定按鈕
@Override
public void onClick(DialogInterface dialog, int which) {//確定按鈕的響應(yīng)事件
//TODO Auto-generated method stub
ActivityCompat.requestPermissions(((Activity) context), permissions, mRequestCode);
}
}).show();//在按鍵響應(yīng)事件中顯示此對(duì)話框
} else {
ActivityCompat.requestPermissions(((Activity) context), permissions, mRequestCode);
}
break;
}
}
if (isAllGranted) {
mRequestPermissionCallBack.granted();
return;
}
}
/**
* 權(quán)限請(qǐng)求結(jié)果回調(diào)接口
*/
interface RequestPermissionCallBack {
/**
* 同意授權(quán)
*/
void granted();
/**
* 取消授權(quán)
*/
void denied();
}
}
▲SplashActivity.java中Button請(qǐng)求權(quán)限主要代碼
requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO,
Manifest.permission.CAMERA,
Manifest.permission.READ_EXTERNAL_STORAGE},
new RequestPermissionCallBack() {
@Override
public void granted() {
Toast.makeText(SplashActivity.this, "權(quán)限獲取成功,執(zhí)行下一步操作", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(SplashActivity.this, MainActivity.class);
startActivity(intent);
SplashActivity.this.finish();
}
@Override
public void denied() {
Toast.makeText(SplashActivity.this, "部分權(quán)限獲取失敗,正常功能受到影響,2秒鐘之后自動(dòng)退出", Toast.LENGTH_LONG).show();
//2秒鐘之后自動(dòng)退出
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
SplashActivity.this.finish();
}
}, 2000);
}
});
資料與參考:
http://www.cnblogs.com/cr330326/p/5181283.html
http://blog.csdn.net/caroline_wendy/article/details/50587230
妹砸照片及源碼下載地址 : https://github.com/ChenYXin/PermissionDemo
關(guān)于我:
- Android開(kāi)發(fā)交流QQ群:537891203
- 郵箱:donkor@yeah.net