Android6.0引入了全新的權(quán)限管理方式,也就是運(yùn)行時(shí)權(quán)限,至于什么是運(yùn)行時(shí)權(quán)限,我們先看一下6.0以前的權(quán)限處理。
6.0以前的權(quán)限
6.0以前的系統(tǒng),我們?cè)诎惭b一個(gè)應(yīng)用的時(shí)候會(huì)默認(rèn)賦予所有權(quán)限。

安裝的時(shí)候會(huì)提示應(yīng)用需要獲取的所有權(quán)限,選擇安裝則會(huì)全部獲取,如果要拒絕獲取權(quán)限,只能放棄安裝應(yīng)用。用戶無(wú)法選擇獲取或者放棄某些權(quán)限。
6.0的運(yùn)行時(shí)權(quán)限
什么是運(yùn)行時(shí)權(quán)限?舉個(gè)栗子,以某個(gè)需要拍照的應(yīng)用為例,當(dāng)運(yùn)行時(shí)權(quán)限生效時(shí),其Camera權(quán)限不是在安裝后賦予,而是在應(yīng)用運(yùn)行的時(shí)候進(jìn)行請(qǐng)求權(quán)限(比如當(dāng)用戶按下”相機(jī)拍照“按鈕后)看到的效果則是這樣的,提示用戶需要權(quán)限,用戶選擇允許,才能獲取到該權(quán)限。

一個(gè)問(wèn)題:我們必須要支持運(yùn)行時(shí)權(quán)限嗎?
如果我們不想啟用運(yùn)行時(shí)權(quán)限其實(shí)很簡(jiǎn)單,我們只要,把targetSdkVersion設(shè)置為設(shè)置低于23就可以了,系統(tǒng)會(huì)認(rèn)為我們的應(yīng)用還不支持新特性,會(huì)按照棉花糖以前的版本進(jìn)行處理。這樣的處理不會(huì)有任何的問(wèn)題,但有一點(diǎn),棉花糖對(duì)每一個(gè)應(yīng)用都有一個(gè)權(quán)限管理界面,是這樣

如果用戶手動(dòng)關(guān)閉了我們應(yīng)用的某些權(quán)限,問(wèn)題就出現(xiàn)了,運(yùn)行應(yīng)用時(shí)可能會(huì)出現(xiàn)崩潰。下面這個(gè)例子
TelephonyManager telephonyManager = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
String deviceId = telephonyManager.getDeviceId();
if (deviceId.equals(mLastDeviceId)) {//This may cause NPE
//do something
}
如果用戶撤消了獲取DeviceId的權(quán)限,那么再次運(yùn)行時(shí),deviceId就是null,如果程序后續(xù)處理不當(dāng),就會(huì)出現(xiàn)崩潰。所以說(shuō)該來(lái)的還是要來(lái)的,我們需要處理好運(yùn)行時(shí)權(quán)限問(wèn)題。
權(quán)限分類
android系統(tǒng)的權(quán)限很多但不是所有的權(quán)限都是敏感權(quán)限,棉花糖將android系統(tǒng)權(quán)限分為四類。
1.正常權(quán)限(Normal Protection)
2.危險(xiǎn)權(quán)限(Dangerous)
3.特殊權(quán)限(Particular)
4.其他權(quán)限(幾乎使用不到)
1.正常權(quán)限
這一類權(quán)限是對(duì)用戶隱私影響較小,沒(méi)有什么安全問(wèn)題,這類權(quán)限會(huì)像6.0以前的系統(tǒng)一樣,安裝就獲取到這些權(quán)限,沒(méi)有用戶提醒,也不能被取消。下面是正常權(quán)限列表。
ACCESS_LOCATION_EXTRA_COMMANDS
ACCESS_NETWORK_STATE
ACCESS_NOTIFICATION_POLICY
ACCESS_WIFI_STATE
BLUETOOTH
BLUETOOTH_ADMIN
BROADCAST_STICKY
CHANGE_NETWORK_STATE
CHANGE_WIFI_MULTICAST_STATE
CHANGE_WIFI_STATE
DISABLE_KEYGUARD
EXPAND_STATUS_BAR
GET_PACKAGE_SIZE
INTERNET
KILL_BACKGROUND_PROCESSES
MODIFY_AUDIO_SETTINGS
NFC
READ_SYNC_SETTINGS
READ_SYNC_STATS
RECEIVE_BOOT_COMPLETED
REORDER_TASKS
REQUEST_INSTALL_PACKAGES
SET_TIME_ZONE
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS
SET_ALARM
INSTALL_SHORTCUT
UNINSTALL_SHORTCUT
對(duì)于這些權(quán)限,我們只需要在Manifest中指定,應(yīng)用安裝就會(huì)獲取。
2.危險(xiǎn)權(quán)限
危險(xiǎn)權(quán)限才是運(yùn)行時(shí)權(quán)限的主要處理對(duì)象,這些權(quán)限可能會(huì)有隱私問(wèn)題,或者影響其他應(yīng)用的運(yùn)行,危險(xiǎn)權(quán)限可以分為以下幾組:
- CALENDAR
- CAMERA
- CONTACTS
- LOCATION
- MICROPHONE
- PHONE
- SENSORS
- SMS
- STORAGE
對(duì)于各組權(quán)限對(duì)應(yīng)的具體權(quán)限如下:

關(guān)于權(quán)限我們需要下面幾個(gè)API
- int checkSelfPermission(String permission) 用來(lái)檢測(cè)應(yīng)用是否已經(jīng)具有權(quán)限
- void requestPermissions(String[] permissions, int requestCode) 進(jìn)行請(qǐng)求單個(gè)或多個(gè)權(quán)限
- void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
請(qǐng)求Camera的權(quán)限
private static final int REQUEST_PERMISSION_CAMERA_CODE = 1;
@Override
public void onClick(View v) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!(checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED)) {
if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
Toast.makeText(this, "Please grant the permission this time", Toast.LENGTH_LONG).show();
}
requestCameraPermission();
}
}
}
private void requestCameraPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[]{Manifest.permission.CAMERA}, REQUEST_PERMISSION_CAMERA_CODE);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_PERMISSION_CAMERA_CODE) {
int grantResult = grantResults[0];
boolean granted = grantResult == PackageManager.PERMISSION_GRANTED;
Log.i(LOGTAG, "onRequestPermissionsResult granted=" + granted);
}
}
通常情況下,我們會(huì)得到這樣的一個(gè)對(duì)話框

我們可以在onRequestPermissionsResult中獲取用戶的選擇情況進(jìn)行相應(yīng)的處理。但如果用戶選擇了否,我們?cè)俅紊暾?qǐng)的時(shí)候就會(huì)多一個(gè)checkbox

如果用戶選擇了不在詢問(wèn),然后拒絕,我們的應(yīng)用基本上就獲取不到這個(gè)權(quán)限了,shouldShowRequestPermissionRationale這個(gè)API可以幫我們判斷接下來(lái)的對(duì)話框是否包含”不再詢問(wèn)“選擇框,我們可以這樣使用。這樣如果我們第一次申請(qǐng)權(quán)限失敗后,在申請(qǐng)權(quán)限的時(shí)候就會(huì)彈出提示Toast,這個(gè)使用一定要向用戶說(shuō)明我們?yōu)槭裁匆暾?qǐng)這個(gè)權(quán)限,來(lái)做什么。
if (!(checkSelfPermission(Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED)) {
if (shouldShowRequestPermissionRationale(Manifest.permission.READ_CONTACTS)) {
Toast.makeText(this, "Please grant the permission this time", Toast.LENGTH_LONG).show();
}
requestReadContactsPermission();
} else {
Log.i(LOGTAG, "onClick granted");
}
對(duì)于同時(shí)申請(qǐng)多個(gè)權(quán)限我們可以
String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_PHONE_STATE};
requestPermissions(permissions, REQUEST_CODE);
效果是這樣,同時(shí)申請(qǐng)多個(gè)權(quán)限可以避免彈出多個(gè)對(duì)話框造成不好的視覺(jué)影響。

3.特殊權(quán)限
特殊權(quán)限是指特別敏感的權(quán)限,這里主要是指兩個(gè)。
SYSTEM_ALERT_WINDOW,設(shè)置懸浮窗
WRITE_SETTINGS 修改系統(tǒng)設(shè)置
關(guān)于上面兩個(gè)特殊權(quán)限的授權(quán),做法是使用startActivityForResult啟動(dòng)授權(quán)界面來(lái)完成,下面是請(qǐng)求SYSTEM_ALERT_WINDOW權(quán)限。
private static final int REQUEST_CODE = 1;
private void requestAlertWindowPermission() {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_CODE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (Settings.canDrawOverlays(this)) {
Log.i("AlertWindowPermission", "onActivityResult granted");
}
}
}
}
需要注意:
- 使用Action
Settings.ACTION_MANAGE_OVERLAY_PERMISSION啟動(dòng)隱式Intent - 使用
"package:" + getPackageName()攜帶App的包名信息 - 使用
Settings.canDrawOverlays方法判斷授權(quán)結(jié)果
WRITE_SETTINGS 使用的則是 Action Settings.ACTION_MANAGE_WRITE_SETTINGS,使用Settings.System.canWrite方法檢測(cè)授權(quán)結(jié)果