并沒有測試所有機型价淌,官方 ROM 和 小米是 ok 的业踏。
針對:
1. 權限首次申請
2. 被拒絕后的申請
3. Don't ask again 勾選后的申請
三種情形的申請方案。
1. 檢查權限
ContextCompat.checkSelfPermission()
e.g. 請求日歷的寫入權限
// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.WRITE_CALENDAR);
// permissionCheck 為 PackageManager.PERMISSION_GRANTED 或 PERMISSION_DENIED
2. 請求權限
2.1 解釋應用為什么需要權限
ActivityCompat.shouldShowRequestPermissionRationale(Activity activity, String permission)
如果請求的權限用戶已經(jīng)拒絕過灌灾,但沒有點擊 “Don't ask again”,就會返回true
卖毁,表示需要顯示權限請求解釋提示
如果用戶點擊了 Don't ask again羡藐,是返回 false 的贩毕,還需要配合權限檢查為 DENIED,才說明該權限 為 Don't ask again
2.2 請求權限
ActivityCompat.requestPermissions(Activity activity, String[] permissions,int requestCode);
e.g.
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.READ_CONTACTS)) {
// Show an expanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
}
3. 處理權限請求響應
通過
onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
方法來判斷權限請求的結果
形參中permissions
和grantResults
是一一對應的仆嗦,被拒絕的權限可以通過shouldShowRequestPermissionRationale
方法來判斷是否為Dont ask again
從而提示用戶手動前往應用設置頁開啟
e.g.
@Override
public void onRequestPermissionsResult(int requestCode,
String[] permissions, int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the
// contacts-related task you need to do.
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
return;
}
// other 'case' lines to check for other
// permissions this app might request
}
}
附1:一個簡單的 demo
/**
* <pre>
* @author Jiun
* @date :2018/09/05/18:58
* desc : 文件描述
* version: 當前版本號
* </pre>
*/
public class PermissionHandler {
private static final int REQ_CODE = 0x10;
private WeakReference<Activity> mActivityWeakReference;
private static final String[] CAMERA = new String[]{Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE};
private String[] mPermissionArrRequest;
/**
* 可用來記錄最新申請的權限辉阶,進行更細致的判斷處理,暫時未用到
*/
private String mPermissionLatestRequest;
private PermissionHandler(Activity activity, @NonNull String[] permissions) {
mActivityWeakReference = new WeakReference<>(activity);
mPermissionArrRequest = permissions;
mPermissionLatestRequest = "";
}
public static PermissionHandler getInstance(Activity activity, String[] permissions) {
return new PermissionHandler(activity, permissions);
}
public static PermissionHandler getCameraInstance(Activity activity) {
return new PermissionHandler(activity, CAMERA);
}
public boolean checkPermission() {
final Activity activity = mActivityWeakReference.get();
if (activity == null) {
return false;
}
for (String permission : mPermissionArrRequest) {
mPermissionLatestRequest = permission;
if (isNotGranted(activity, permission)) {
if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
DialogUtil.showConfirmDialog(activity, "請授權下面的權限申請瘩扼,否則該功能可能無法使用", new DialogUtil.OnDialogConfirmClickListener() {
@Override
public void onConfirm(Dialog dialog) {
ActivityCompat.requestPermissions(activity, mPermissionArrRequest, REQ_CODE);
dialog.cancel();
}
}).show();
} else {
ActivityCompat.requestPermissions(activity, mPermissionArrRequest, REQ_CODE);
}
return false;
}
}
return true;
}
/**
* 需要在 Activity#onRequestPermissionResult() 中調用此方法
*/
public void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case REQ_CODE: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0) {
for (int i = 0; i < grantResults.length; i++) {
int grantResult = grantResults[i];
if (grantResult == PackageManager.PERMISSION_GRANTED) {
// TODO(Hjy): 2018/9/5 授權成功谆甜,比如說 在 checkPermission 中把未授權的權限保存起來,在授權結果中進行比對集绰,看是否全部授權成功规辱,來設置授權的回調
} else if (permissions.length > i) {
if (checkIsDeniedForeverAndShowDialog(permissions[i])) {
return;
}
}
}
} else {
for (String permission : permissions) {
if (checkIsDeniedForeverAndShowDialog(permission)) {
return;
}
}
}
return;
}
default:
}
}
private boolean checkIsDeniedForeverAndShowDialog(String permission) {
final Activity activity = mActivityWeakReference.get();
if (activity == null) {
return true;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& isDeniedForever(activity, permission)) {
DialogUtil.showCommonDialog(activity, "權限申請失敗或被拒絕,請手動前往授權", new DialogUtil.OnDialogConfirmClickListener() {
@Override
public void onConfirm(Dialog dialog) {
dialog.dismiss();
startAppDetailsSettings(activity);
}
}).show();
return true;
}
return false;
}
private boolean isNotGranted(Activity activity, String permission) {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(activity, permission);
}
@RequiresApi(api = Build.VERSION_CODES.M)
private boolean isDeniedForever(Activity activity, String permission) {
return isNotGranted(activity, permission) && !activity.shouldShowRequestPermissionRationale(permission);
}
/**
* 打開系統(tǒng)設置界面
*/
private void startAppDetailsSettings(Activity activity) {
Intent intent = new Intent("android.settings.APPLICATION_DETAILS_SETTINGS");
intent.setData(Uri.parse("package:" + activity.getPackageName()));
ComponentName componentName = intent.resolveActivity(activity.getPackageManager());
if (componentName != null) {
activity.startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
}
}
使用步驟(請求相機用到的權限為例):
- 在合適的地方獲取實例
PermissionHandler mPermissionHandler = PermissionHandler.getCameraInstance(activity);
- 權限檢查并請求
if (mPermissionHandler.checkPermission()) {
// 打開相機拍照
}
- 獲取 權限請求回調結果
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (mPermissionHandler != null) {
mPermissionHandler.onRequestPermissionResult(requestCode, permissions, grantResults);
}
}
附2:危險權限和權限組
附3:如何區(qū)分 i.e. 和 e.g.
上學的時候就很好奇栽燕,終于想起來搜搜了:joy:
1. 理解含義:
"i.e."是拉丁語 id est 的縮寫罕袋,意為"that is"(即), "e.g." 是拉丁語exempli gratia 的縮寫碍岔,意為"for the sake of example"(如例子)浴讯。
2. 把用法和好記的短語聯(lián)系起來:
記拉丁語可能比較難,就把"i.e."記為 "in essence"(本質上)蔼啦、 "in other words"(換句話)榆纽, "e.g." 記成 "example given"(給出例子) 。
3. 使用"i.e."來作解釋:
"i.e." 是用來在一句話之后換種方式解釋或描述之前說的內容捏肢。
The elephant is a pachyderm, i.e., an animal with thick skin and nails resembling hooves.(大象是厚皮類動物奈籽,即皮膚厚、有蹄子的動物)
I went to my least favorite place (i.e., the dentist).(我去我最喜歡的地方了鸵赫,即牙醫(yī)診所)
4. 注意:
"i.e."之后一般的是延伸的定義衣屏,也可以是個比喻。如果你把 "i.e." 替換為 "in other words"句子就通順奉瘤,如果是 "for example" 就不行了。
5. 試著把簡寫換成原意煮甥,聽起來通順的話就可能用對了:
比如 "I like quiet activities (e.g., reading)"(我喜歡安靜的活動(例如閱讀))盗温, "I like quiet activities (for example, reading)". 如果使用 i.e.,則用 "in other words"比 "that is."更好用成肘。
6. 在一個或多個例子前使用 "e.g." 想想 "e.g."后面要接的東西作為一個類別卖局, 然后看看這個類別里可以放什么東西:
Buy some vegetables, e.g., carrots.(買點蔬菜,比如胡蘿卜)
I like power metal (e.g., Firewind, Iced Earth, Sonata Arctica).(我喜歡能量金屬樂双霍,例如焚風樂隊砚偶、冰封大地樂隊批销、極光奏鳴曲樂團)
注意這里"i.e."說出來不通順。 "Carrots"(胡蘿卜)不是所有的蔬菜染坯,而只是一種而已均芽。如果要用 "i.e.",你可以說 "Buy some vegetables, i.e., the edible part of any plant."(買點蔬菜单鹿,即植物可食部分)同樣地掀宋,樂隊名也只是樂曲分格的一部分,如果要用 "i.e." 則"I like power metal, i.e., fast metal with symphonic elements and epic themes."(我喜歡能量金屬樂仲锄,即快速金屬樂搭配交響樂元素和史詩主題的樂曲風格)
7. 兩者都用括號或逗號:
要指示分開的從句劲妙,可以前面加個逗號,或者用括號括起來儒喊,兩者上述例子都用到镣奋。如果用括號要在兩個縮寫前就用上,寫出相應的例子或解釋以后把括號關上怀愧。
美式英語中侨颈,在 "i.e."、 "e.g."后加個逗號掸驱,英式則不用加肛搬。
小提示
- 還是擔心用法用錯,則最好不用它們毕贼,甚至寫作中也不用温赔。你要表示“例如”時,就寫“for example”鬼癣,表示“即”的時候陶贼,用“that is”。這樣也不會多寫多少待秃,也不出錯拜秧。
- 更好的"i.e." 和 "e.g."對比的例子,就是 Chili Palmer (John Travolta) 和Ray "Bones" Barboni (Dennis Farina)在 1995 電影 Get Shorty (《黑道當家》)里的對白
- 沒必要在"e.g." 后的一長串行表中使用 "etc." 章郁,因為"e.g." 本身就表示不完整的列表参淫。
- 說話的時候最好不用"i.e." 、 "e.g." 崭倘,而說"that is" 祥楣、 "in other words" 代表"i.e." , "for example" 培廓、 "for instance" 代表 "e.g."