Android6.0引入了全新的權(quán)限管理方式,也就是運(yùn)行時權(quán)限围段,至于什么是運(yùn)行時權(quán)限顾翼,我們先看一下6.0以前的權(quán)限處理。
6.0以前的權(quán)限
6.0以前的系統(tǒng)奈泪,我們在安裝一個應(yīng)用的時候會默認(rèn)賦予所有權(quán)限适贸。
安裝的時候會提示應(yīng)用需要獲取的所有權(quán)限难菌,選擇安裝則會全部獲取刽锤,如果要拒絕獲取權(quán)限,只能放棄安裝應(yīng)用贴汪。用戶無法選擇獲取或者放棄某些權(quán)限冯遂。
6.0的運(yùn)行時權(quán)限
什么是運(yùn)行時權(quán)限蕊肥?舉個栗子,以某個需要拍照的應(yīng)用為例蛤肌,當(dāng)運(yùn)行時權(quán)限生效時壁却,其Camera權(quán)限不是在安裝后賦予批狱,而是在應(yīng)用運(yùn)行的時候進(jìn)行請求權(quán)限(比如當(dāng)用戶按下”相機(jī)拍照“按鈕后)看到的效果則是這樣的,提示用戶需要權(quán)限展东,用戶選擇允許赔硫,才能獲取到該權(quán)限。
一個問題:我們必須要支持運(yùn)行時權(quán)限嗎盐肃?
如果我們不想啟用運(yùn)行時權(quán)限其實很簡單爪膊,我們只要,把targetSdkVersion設(shè)置為設(shè)置低于23就可以了恼蓬,系統(tǒng)會認(rèn)為我們的應(yīng)用還不支持新特性惊完,會按照棉花糖以前的版本進(jìn)行處理。這樣的處理不會有任何的問題处硬,但有一點(diǎn)小槐,棉花糖對每一個應(yīng)用都有一個權(quán)限管理界面,是這樣
如果用戶手動關(guān)閉了我們應(yīng)用的某些權(quán)限荷辕,問題就出現(xiàn)了凿跳,運(yùn)行應(yīng)用時可能會出現(xiàn)崩潰。下面這個例子
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)行時,deviceId就是null控嗜,如果程序后續(xù)處理不當(dāng),就會出現(xiàn)崩潰骡显。所以說該來的還是要來的疆栏,我們需要處理好運(yùn)行時權(quán)限問題。
權(quán)限分類
android系統(tǒng)的權(quán)限很多但不是所有的權(quán)限都是敏感權(quán)限惫谤,棉花糖將android系統(tǒng)權(quán)限分為四類壁顶。
1.正常權(quán)限(Normal Protection)
2.危險權(quán)限(Dangerous)
3.特殊權(quán)限(Particular)
4.其他權(quán)限(幾乎使用不到)
1.正常權(quán)限
這一類權(quán)限是對用戶隱私影響較小,沒有什么安全問題溜歪,這類權(quán)限會像6.0以前的系統(tǒng)一樣若专,安裝就獲取到這些權(quán)限,沒有用戶提醒蝴猪,也不能被取消调衰。下面是正常權(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
對于這些權(quán)限自阱,我們只需要在Manifest中指定嚎莉,應(yīng)用安裝就會獲取。
2.危險權(quán)限
危險權(quán)限才是運(yùn)行時權(quán)限的主要處理對象沛豌,這些權(quán)限可能會有隱私問題萝喘,或者影響其他應(yīng)用的運(yùn)行,危險權(quán)限可以分為以下幾組:
- CALENDAR
- CAMERA
- CONTACTS
- LOCATION
- MICROPHONE
- PHONE
- SENSORS
- SMS
- STORAGE
對于各組權(quán)限對應(yīng)的具體權(quán)限如下:
關(guān)于權(quán)限我們需要下面幾個API
- int checkSelfPermission(String permission) 用來檢測應(yīng)用是否已經(jīng)具有權(quán)限
- void requestPermissions(String[] permissions, int requestCode) 進(jìn)行請求單個或多個權(quán)限
- void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
請求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);
}
}
通常情況下,我們會得到這樣的一個對話框
我們可以在onRequestPermissionsResult中獲取用戶的選擇情況進(jìn)行相應(yīng)的處理阁簸。但如果用戶選擇了否爬早,我們再次申請的時候就會多一個checkbox
如果用戶選擇了不在詢問,然后拒絕启妹,我們的應(yīng)用基本上就獲取不到這個權(quán)限了筛严,shouldShowRequestPermissionRationale這個API可以幫我們判斷接下來的對話框是否包含”不再詢問“選擇框,我們可以這樣使用饶米。這樣如果我們第一次申請權(quán)限失敗后桨啃,在申請權(quán)限的時候就會彈出提示Toast,這個使用一定要向用戶說明我們?yōu)槭裁匆暾堖@個權(quán)限檬输,來做什么照瘾。
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");
}
對于同時申請多個權(quán)限我們可以
String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_PHONE_STATE};
requestPermissions(permissions, REQUEST_CODE);
效果是這樣,同時申請多個權(quán)限可以避免彈出多個對話框造成不好的視覺影響丧慈。
3.特殊權(quán)限
特殊權(quán)限是指特別敏感的權(quán)限析命,這里主要是指兩個。
SYSTEM_ALERT_WINDOW逃默,設(shè)置懸浮窗
WRITE_SETTINGS 修改系統(tǒng)設(shè)置
關(guān)于上面兩個特殊權(quán)限的授權(quán)鹃愤,做法是使用startActivityForResult
啟動授權(quán)界面來完成,下面是請求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
啟動隱式Intent - 使用
"package:" + getPackageName()
攜帶App的包名信息 - 使用
Settings.canDrawOverlays
方法判斷授權(quán)結(jié)果
WRITE_SETTINGS 使用的則是 Action Settings.ACTION_MANAGE_WRITE_SETTINGS软吐,
使用Settings.System.canWrite
方法檢測授權(quán)結(jié)果