Android-6.0 棉花糖權(quán)限的那點(diǎn)事

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)限。

pre-marshmallow-permission.jpg

安裝的時(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)限。

marshmallow-permission.png

一個(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)限管理界面,是這樣

6a195423jw1ezwqnmjhcdj20u01hc40k.jpg

如果用戶手動(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)限如下:

6a195423jw1ezwpc11cs0j20hr0majwm.jpg

關(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ì)話框

marshmallow-permission.png

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

6a195423jw1ezwtz1ljjgj20u01hcad8.jpg

如果用戶選擇了不在詢問(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é)影響。

6a195423jw1ezxulzbeu2j20iq0ggt9y.jpg

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é)果

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容