參考:
一. 權限說明:
1. 權限種類:
Android中權限分為正常權限(即,不會對用戶隱私或設備操作造成很大風險的權限)和危險權限(即,可能影響用戶隱私或設備正常操作的權限):
正常權限涵蓋應用需要訪問其沙盒外部數據或資源,但對用戶隱私或其他應用操作風險很小的區域。例如,設置時區的權限就是正常權限。如果應用聲明其需要正常權限,系統會自動向應用授予該權限。如需當前正常權限的完整列表,請參閱正常權限。
危險權限涵蓋應用需要涉及用戶隱私信息的數據或資源,或者可能對用戶存儲的數據或其他應用的操作產生影響的區域。例如,能夠讀取用戶的聯系人屬于危險權限。如果應用聲明其需要危險權限,則用戶必須明確向應用授予該權限。
危險權限和權限組:
2. 權限的授予:
如果您的應用在其清單中列出正常權限,系統會自動授予這些權限。如果您的應用在其清單中列出危險權限,系統會要求用戶明確授予這些權限。Android 發出請求的方式取決于系統版本:
如果設備運行的是 Android 6.0(API 級別 23)或更高版本,并且應用的 targetSdkVersion
是 23 或更高版本,則應用在運行時向用戶請求權限。用戶可隨時調用權限,因此應用在每次運行時均需檢查自身是否具備所需的權限。如果設備運行的是 Android 5.1(API 級別 22)或更低版本,并且應用的 targetSdkVersion
是 22 或更低版本,則系統會在用戶安裝應用時要求用戶授予權限。如果將新權限添加到更新的應用版本,系統會在用戶更新應用時要求授予該權限。用戶一旦安裝應用,他們撤銷權限的唯一方式是卸載應用。
二. 動態權限授予的幾個重要的方法:
注意:在Activity里調用時Context和Activity參數可以忽略
int ContextCompat.checkSelfPermission (Context context, String permission)參數:(上下文, 需要判斷的權限[如:Manifest.permission.READ_CONTACTS])
該方法用來檢測應用是否具有某權限,返回值是PackageManager.PERMISSION_GRANTED(已授權)或PackageManager.PERMISSION_GRANTED(未授權)void requestPermissions (Activity, permissions, int requestCode)參數:(上下文,需要申請的權限的列表(String數組)[如:new String[]{Manifest.permission.READ_CONTACTS}],自己定義的requestCode[如:REQUEST_READ_CONTACTS(自定義int值)](主要是回調的時候用來判斷授權種類))
該方法用來請求權限,請求過程會顯示一個Dialog(不可自定義),無論用戶是否確認授權都會調用回調方法onRequestPermissionsResult()
void onRequestPermissionsResult (int requestCode, String[] permissions, int[] grantResults)參數:(requestPermissions方法傳過來的參數,要請求的權限列表,各權限的授權情況(與需要權限的數組對應))
該方法是用戶請求權限之后的回調方法,用戶可以在這里做判斷進行需要的操作。boolean shouldShowRequestPermissionRationale (Activity activity, String permission)參數:(上下文,需要判斷的權限)
該方法判斷一個權限是否被拒絕過,Android官方對此方法的解釋是用該方法判斷是否需要給用戶進行為什么需要權限的提示。
三. 動態權限的申請
- manifest中注冊權限:(如需要訪問通訊錄權限)
<uses-permission android:name="android.permission.READ_CONTACTS" />
- 詢問是否有該權限:
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
switch (checkSelfPermission(Manifest.permission.READ_CONTACTS)){
case PackageManager.PERMISSION_GRANTED:
Log.i(TAG, "onCreate: Granted...");
break;
case PackageManager.PERMISSION_DENIED:
Log.i(TAG, "onCreate: Denied...");
Log.i(TAG, "onCreate: 是否被拒絕過:"+shouldShowRequestPermissionRationale(Manifest.permission.READ_CONTACTS));
requestPermissions(new String[]{Manifest.permission.READ_CONTACTS},REQUEST_READ_CONTACTS);//請求權限
break;
}
}
- 當沒有該權限時請求授予權限:
requestPermissions(new String[]{Manifest.permission.READ_CONTACTS},REQUEST_READ_CONTACTS);
- 重載回調方法:
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
/*判斷授權種類*/
if(requestCode == REQUEST_READ_CONTACTS){
/*授權成功*/
if(grantResults.length>0 && grantResults[0]==PackageManager.PERMISSION_GRANTED){
Log.i(TAG, "onRequestPermissionsResult: 權限請求通過");
}
/*授權失敗*/
else {
Log.i(TAG, "onRequestPermissionsResult: 權限請求未通過");
}
}
}
四. 源碼:
package cn.foxnickel.androidpermission;
import android.Manifest;
import android.annotation.TargetApi;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
public static int REQUEST_READ_CONTACTS=100;
private final String TAG = getClass().getSimpleName();
@TargetApi(Build.VERSION_CODES.M)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
switch (checkSelfPermission(Manifest.permission.READ_CONTACTS)){
case PackageManager.PERMISSION_GRANTED:
Log.i(TAG, "onCreate: Granted...");
break;
case PackageManager.PERMISSION_DENIED:
Log.i(TAG, "onCreate: Denied...");
Log.i(TAG, "onCreate: 是否被拒絕過:"+shouldShowRequestPermissionRationale(Manifest.permission.READ_CONTACTS));
requestPermissions(new String[]{Manifest.permission.READ_CONTACTS},REQUEST_READ_CONTACTS);
break;
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
/*判斷授權種類*/
if(requestCode == REQUEST_READ_CONTACTS){
/*授權成功*/
if(grantResults.length>0 && grantResults[0]==PackageManager.PERMISSION_GRANTED){
Log.i(TAG, "onRequestPermissionsResult: 權限請求通過");
}
/*授權失敗*/
else {
Log.i(TAG, "onRequestPermissionsResult: 權限請求未通過");
}
}
}
}
五. 封裝(轉自吳小龍博客)
BaseActivity:
public class BaseActivity extends AppCompatActivity {
public static int REQUEST_PERMISSION = 100;
public onPermissionCallbackListener onPermissionCallbackListener;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@TargetApi(M)
public void requestRuntimePermission(String permission, onPermissionCallbackListener onPermissionCallbackListener) {
this.onPermissionCallbackListener = onPermissionCallbackListener;
switch (checkSelfPermission(permission)) {
case PackageManager.PERMISSION_GRANTED:
// 已有授權
Log.i("wxl", "已有授權");
if (this.onPermissionCallbackListener != null)
onPermissionCallbackListener.onGranted();
break;
case PackageManager.PERMISSION_DENIED:
// 1、沒有權限:尚未請求過權限;
// 2、或者請求授權被拒絕,用shouldShowRequestPermissionRationale判斷用戶是否拒絕過,如果返回true,表示用戶拒絕過,
// 再次請求權限,將會出現“不再詢問”,勾上“不再詢問”,只能選擇拒絕,再次進入,shouldShowRequestPermissionRationale始終false
// 3、或者曾經授權過,但用戶在設置中禁用權限
Log.i("wxl", "是否拒絕過=" + shouldShowRequestPermissionRationale(permission));
requestPermissions(new String[]{permission}, REQUEST_PERMISSION);
break;
default:
break;
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == REQUEST_PERMISSION) {
if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 授權請求被通過,讀取通訊錄
Log.i("wxl", "onRequestPermissionsResult=授權請求被通過");
if (onPermissionCallbackListener != null)
onPermissionCallbackListener.onGranted();
} else {
Log.i("wxl", "onRequestPermissionsResult=授權請求不被通過");
if (onPermissionCallbackListener != null)
onPermissionCallbackListener.onDenied();
}
}
}
}
/*這里新建了接口onPermissionCallbackListener,為了回調方便:*/
public interface onPermissionCallbackListener {
void onGranted();
void onDenied();
}
在需要使用到權限之前調用requestRuntimePermission方法,如通訊錄權限:
requestRuntimePermission(READ_CONTACTS, new onPermissionCallbackListener() {
@Override
public void onGranted() {
Log.i("wxl", "授權請求通過");
}
@Override
public void onDenied() {
Log.i("wxl", "授權請求拒絕");
}
});
六. 總結
- 先詢問是否已經得到授權,若已經授權,則直接進行操作,若未授權,則請求權限,然后在授權回調中進行操作。
- 可以在MainActivity中進行授權管理,也可以封裝授權管理到BaseActivity
- 多個權限可以直接在String數組中加入(壞處:公用一個requestCode,不方便回調的判斷),也可以重新詢問是否授權。
七.補充
- 申請多個權限時可以先判斷有沒有該權限,如果沒有就加到List中去,然后將List轉換為數組,然后調用
ActivityCompat.requestPermissions(Context,String []permissions,PERMISSIONS_REQ);
申請全部權限。
代碼:
List<String> permissionList = new ArrayList<>();//申請權限的List
/*判斷*/
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.M){
if(checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)!=PackageManager.PERMISSION_GRANTED){
Log.i(TAG, "onCreate: finelocation denied");
permissionList.add(Manifest.permission.ACCESS_FINE_LOCATION);
}
if(checkSelfPermission(Manifest.permission.READ_PHONE_STATE)!=PackageManager.PERMISSION_GRANTED){
Log.i(TAG, "onCreate: phonestate denied");
permissionList.add(Manifest.permission.READ_PHONE_STATE);
}
if(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)!=PackageManager.PERMISSION_GRANTED){
Log.i(TAG, "onCreate: write storage denied");
permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
if(checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION)!=PackageManager.PERMISSION_GRANTED){
Log.i(TAG, "onCreate: coarse location denied");
permissionList.add(Manifest.permission.ACCESS_COARSE_LOCATION);
}
/*申請*/
if(!permissionList.isEmpty()){
String permissions[] = permissionList.toArray(new String[permissionList.size()]);
ActivityCompat.requestPermissions(this,permissions,PERMISSIONS_REQ);
}else {
requestLocation();
}
}else {
requestLocation();
}
- 另一種申請多個權限的寫法(自己定義申請權限的函數)
- 調用requestPermission方法申請權限
/*判斷SDK版本,6.0以上就申請權限,將要申請的權限傳給requestPermission方法*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermission(Manifest.permission.SEND_SMS, Manifest.permission.READ_PHONE_STATE, Manifest.permission.CALL_PHONE, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.ACCESS_COARSE_LOCATION);
}
- requestPermission方法
@TargetApi(Build.VERSION_CODES.M)
private void requestPermission(String... permissions) {
List<String> permissionList = new ArrayList<>();//要申請的權限列表
/**
* 判斷權限是否已經擁有,若未擁有則添加到List中
*/
for (String permission : permissions) {
switch (checkSelfPermission(permission)) {
case PackageManager.PERMISSION_GRANTED:
Log.i(TAG, "onCreate: Granted...");
break;
case PackageManager.PERMISSION_DENIED:
Log.i(TAG, "onCreate: Denied...");
permissionList.add(permission);
break;
}
}
//申請未擁有的權限
if (!permissionList.isEmpty()) {
requestPermissions(permissionList.toArray(new String[permissionList.size()]),REQUEST_PERMISSIONS);
}
}