Android6.0運行時權限處理
Android6.0的發佈介紹了一種新的權限機制。用戶可以在運行時直接管理應用程序的權限,這個功能提升了權限控制的可見性和可控性,同時簡化了安裝和自動升級過程,用戶可以單獨撤銷或者授予應用程序某項權限,對應用擁有更多的控制權。
應用程序target是Android 6.0及以上(API level 23),要確保在運行時檢查和請求權限,在6.0以後有些權限屬於危險權限,清單中聲明權限,不能保證權限授權通過,爲了確定你的app是否授予某個權限,通過checkSelfPermission()方法判斷,請求權限使用requestPermissions()方法。即使你的app不是target Android 6.0,你也應該在新的權限機制下測試你的應用。
本文參考博文:
Hongyang:Android 6.0 運行時權限處理完全解析
1. Android權限管理
Google將權限分爲兩類,一類是普通權限(Normal Permissions),這類權限一般不涉及用戶隱私,也不需要用戶進行授權,如網絡訪問,手機震動等,另外一類是危險權限(Dangerous Permission),涉及用戶隱私,需要用戶授權,如對sd卡讀取、訪問用戶手機通訊錄、撥打電話等。
關於Android的危險權限:Dangerous Permissions:
group:android.permission-group.CONTACTS
permission:android.permission.WRITE_CONTACTS
permission:android.permission.GET_ACCOUNTS
permission:android.permission.READ_CONTACTS
group:android.permission-group.PHONE
permission:android.permission.READ_CALL_LOG
permission:android.permission.READ_PHONE_STATE
permission:android.permission.CALL_PHONE
permission:android.permission.WRITE_CALL_LOG
permission:android.permission.USE_SIP
permission:android.permission.PROCESS_OUTGOING_CALLS
permission:com.android.voicemail.permission.ADD_VOICEMAIL
group:android.permission-group.CALENDAR
permission:android.permission.READ_CALENDAR
permission:android.permission.WRITE_CALENDAR
group:android.permission-group.CAMERA
permission:android.permission.CAMERA
group:android.permission-group.SENSORS
permission:android.permission.BODY_SENSORS
group:android.permission-group.LOCATION
permission:android.permission.ACCESS_FINE_LOCATION
permission:com.google.android.gms.permission.CAR_SPEED
permission:android.permission.ACCESS_COARSE_LOCATION
group:android.permission-group.STORAGE
permission:android.permission.READ_EXTERNAL_STORAGE
permission:android.permission.WRITE_EXTERNAL_STORAGE
group:android.permission-group.MICROPHONE
permission:android.permission.RECORD_AUDIO
group:android.permission-group.SMS
permission:android.permission.READ_SMS
permission:android.permission.RECEIVE_WAP_PUSH
permission:android.permission.RECEIVE_MMS
permission:android.permission.RECEIVE_SMS
permission:android.permission.SEND_SMS
permission:android.permission.READ_CELL_BROADCASTS
危險權限我們可以通過adb命令查看:
adb shell pm list permissions -d -g
查看權限以及詳細信息:
adb shell pm list permissions -d -g -f
查看到危險的權限是一組一組的,如果某一組裏面的某個危險權限已被用戶授權,那麼系統會立即授權其他的權限,而不需要再向用戶授權,比如你的app已經授權READ_SMS,那麼當你的app需要SEND_SMS,系統會自動授權。彈出的dialog的文本是對權限組的說明,而非單個權限說明。且dialog不可以自定義。
2. 權限相關處理
2.1 添加權限
在AndroidManifest.xml中正常添加權限。
2.2 檢查權限
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.CALL_PHONE)
!= PackageManager.PERMISSION_GRANTED) {
// 向用戶申請權限
} else {
// 執行權限通過後的事件
}
這裏涉及到一個API,ContextCompat.checkSelfPermission(),主要用於檢測某個權限是否已經被授予,方法返回值爲PackageManager.PERMISSION_DENIED或者PackageManager.PERMISSION_GRANTED。當返回DENIED就需要進行申請授權了。查看源碼:
源碼:
/**
* Determine whether <em>you</em> have been granted a particular permission.
* 確定你是否獲得了一個特定的權限
*
* @param permission The name of the permission being checked.
* 參數permission:你要檢測的權限
*
* @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if you have the
* permission, or {@link android.content.pm.PackageManager#PERMISSION_DENIED} if not.
* 返回值PackageManager.PERMISSION_GRANTED授權已成功
* PackageManager.PERMISSION_DENIED 授權被拒絕
*
* @see android.content.pm.PackageManager#checkPermission(String, String)
*/
public static int checkSelfPermission(@NonNull Context context, @NonNull String permission) {
if (permission == null) {
throw new IllegalArgumentException("permission is null");
}
return context.checkPermission(permission, android.os.Process.myPid(), Process.myUid());
}
關於返回值,同樣到源碼裏面可以查看:
/**
* Permission check result: this is returned by {@link #checkPermission}
* if the permission has been granted to the given package.
*/
public static final int PERMISSION_GRANTED = 0;
/**
* Permission check result: this is returned by {@link #checkPermission}
* if the permission has not been granted to the given package.
*/
public static final int PERMISSION_DENIED = -1;
檢查完權限之後,如果系統沒有授權成功,需要向用戶申請權限。
2.3 申請權限
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CALL_PHONE},
MY_PERMISSIONS_REQUEST_CALL_PHONE);
該方法是異步的,第一個參數是Context;第二個參數是需要申請的權限的字符串數組;第三個參數爲requestCode,主要用於回調的時候檢測。
2.4 處理權限申請回調
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == MY_PERMISSIONS_REQUEST_CALL_PHONE) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 權限申請成功,可以執行事件了
} else {
// Permission Denied 權限被拒絕
// 提示對話框等
}
return;
}
}
對於權限的申請結果,首先驗證requestCode定位到你的申請,然後驗證grantResults對應於申請的結果,這裏的數組對應於申請時的第二個權限字符串數組。如果你同時申請兩個權限,那麼grantResults的length就爲2,分別記錄你兩個權限的申請結果。
2.5 權限徹底禁止
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.CALL_PHONE))
}
當第一次向用戶詢問權限時,用戶拒絕,而後再申請的彈窗會提示以後不再提示選項,如果用戶勾選並拒絕權限,表示用戶徹底拒絕權限,那麼ActivityCompat.shouldShowRequestPermissionRationale將返回false,一般我們進行判斷用戶是徹底拒絕,那麼想要再申請權限,可以提示用戶到設置去修改。
3. 案例實踐
public class MainActivity extends AppCompatActivity {
private static final int CALL_PHONE_RESQUESTCODE = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnCall = (Button) findViewById(R.id.btnCall);
btnCall.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
testCall();
}
});
}
public void testCall() {
// 判斷權限是否未被系統授權
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.CALL_PHONE)
!= PackageManager.PERMISSION_GRANTED) {
Log.i("TAG", "is" + ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE));
// 重新請求權限
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CALL_PHONE},
CALL_PHONE_RESQUESTCODE);
} else {
// 否則直接執行事件了
callPhone();
}
}
public void callPhone() {
Intent intent = new Intent(Intent.ACTION_CALL);
Uri data = Uri.parse("tel:" + "10010");
intent.setData(data);
startActivity(intent);
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == CALL_PHONE_RESQUESTCODE) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
callPhone();
} else {
// Permission Denied,權限被拒絕
// Toast.makeText(MainActivity.this, "Permission Denied", Toast.LENGTH_SHORT).show();
// 完全拒絕,可以提示用戶跳到設置頁面
if (!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE)) {
showDialog();
}
}
return;
}
}
public void showDialog() {
new AlertDialog.Builder(this)
.setMessage("權限被拒絕,請到權限管理裏面設置!")
.setPositiveButton("去設置", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", MainActivity.this.getPackageName(), null);
intent.setData(uri);
MainActivity.this.startActivity(intent);
}
})
.setNegativeButton("取消", null)
.create().show();
}
}
基本的處理過程到此結束。