在Android6.0之前担忧,應用安裝時系統(tǒng)會列出AndroidManifest清單上申請的所有權限犹菱,用戶必須全部接受才能繼續(xù)安裝截歉,并且這些權限授權之后無法撤銷。這對于開發(fā)者來說是比較方便的无午,需要什么權限只需要在AndroidManifest申請即可,不需要考慮權限被拒絕等各種場景祝谚。但是對于用戶來說卻沒有辦法自主選擇屏蔽他不想授予的權限宪迟,也容易給惡意程序利用。
Android6.0之后交惯,權限的申請由安裝時變成了運行時次泽。開發(fā)者仍然需要在AndroidManifest里面列出所需要的所有權限,但用戶安裝時不需要對這些權限進行授權席爽,而是在運行時需要用到某個權限時才詢問用戶是否授權箕憾,用戶可以選擇接受或者拒絕。另外即使用戶接受了拳昌,也可以在權限管理中進行撤銷袭异。如果直接使用用戶沒有授權的權限會導致crash,因此炬藤,開發(fā)時需要考慮這些場景冈止,并作出處理蛹找。
對于一些老的應用來說缺脉,也不用太擔心肚医,如果app的targetSdkVersion低于23,將繼續(xù)使用舊有規(guī)則羹膳∷ィ看到這有人可能覺得干脆將所有的targetSdkVersion設置為23以下不就好了,也不用那么麻煩考慮權限的問題陵像。但是要注意就珠,在6.0的系統(tǒng)上,即使安裝時取得了所有的權限醒颖,用戶仍然可以之后在權限管理中撤銷授權妻怎。因此,我們還是需要與時俱進泞歉,將targetSdkVersion升級到23并好好處理權限問題逼侦。
Android的權限總的來說分為三種,分別是normal類型腰耙、dangerous類型和special類型:
1榛丢、normal類型的權限不會威脅到用戶的隱私,可以直接在AndroidManifest里面注冊挺庞,在安裝時就被授權晰赞,不需要每次使用時都檢查權限,并且用戶不能取消。主要包括:
android.permission.ACCESS_LOCATION_EXTRA_COMMANDS
android.permission.ACCESS_NETWORK_STATE
android.permission.ACCESS_NOTIFICATION_POLICY
android.permission.ACCESS_WIFI_STATE
android.permission.ACCESS_WIMAX_STATE
android.permission.BLUETOOTH
android.permission.BLUETOOTH_ADMIN
android.permission.BROADCAST_STICKY
android.permission.CHANGE_NETWORK_STATE
android.permission.CHANGE_WIFI_MULTICAST_STATE
android.permission.CHANGE_WIFI_STATE
android.permission.CHANGE_WIMAX_STATE
android.permission.DISABLE_KEYGUARD
android.permission.EXPAND_STATUS_BAR
android.permission.FLASHLIGHT
android.permission.GET_ACCOUNTS
android.permission.GET_PACKAGE_SIZE
android.permission.INTERNET
android.permission.KILL_BACKGROUND_PROCESSES
android.permission.MODIFY_AUDIO_SETTINGS
android.permission.NFC
android.permission.READ_SYNC_SETTINGS
android.permission.READ_SYNC_STATS
android.permission.RECEIVE_BOOT_COMPLETED
android.permission.REORDER_TASKS
android.permission.REQUEST_INSTALL_PACKAGES
android.permission.SET_TIME_ZONE
android.permission.SET_WALLPAPER
android.permission.SET_WALLPAPER_HINTS
android.permission.SUBSCRIBED_FEEDS_READ
android.permission.TRANSMIT_IR
android.permission.USE_FINGERPRINT
android.permission.VIBRATE
android.permission.WAKE_LOCK
android.permission.WRITE_SYNC_SETTINGS
com.android.alarm.permission.SET_ALARM
com.android.launcher.permission.INSTALL_SHORTCUT
com.android.launcher.permission.UNINSTALL_SHORTCUT
2宾肺、dangerous類型的權限可以直接訪問用戶的敏感數(shù)據溯饵,不僅需要在AndroidManifest里面注冊侵俗,還需要在使用時請求授權锨用。主要包括:
可以看到dangerous類型的權限進行了分組,同一組的任何一個權限被授權了隘谣,其他權限也自動被授權增拥。例如,一旦WRITE_CONTACTS被授權了寻歧,app也有READ_CONTACTS和GET_ACCOUNTS權限了掌栅。
dangerous類型的權限申請主要調用這幾個方法:
Context.checkSelfPermission(String permission) 檢查是否被授予了某個權限
Activity.requestPermissions(String[] permissions, int requestCode) 申請一組權限
Activity.onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) 權限申請結果回調
由于這些方法都是在api23引入的,所以需要在使用時先進行版本判斷码泛。下面以相機為例說明怎樣申請權限:
public void checkCameraPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// 23及以后的版本需要檢測權限
int hasCameraPermission = checkSelfPermission(Manifest.permission.CAMERA);
if (hasCameraPermission != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.CAMERA}, PERMISSION_REQUEST_CODE_CAMERA);
} else {
cameraPermissionGranted(true);
}
} else {
// 23之前的版本權限在安裝時已經獲取
cameraPermissionGranted(true);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PERMISSION_REQUEST_CODE_CAMERA) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
cameraPermissionGranted(true);
} else {
cameraPermissionGranted(false);
}
}
}
另外猾封,v4包中也提供了兼容方法ContextCompat.checkSelfPermission()和ActivityCompat.requestPermissions()可以避免版本判斷,唯一的區(qū)別就是需要帶上額外的參數(shù)Context或Activity噪珊,其他都一樣:
private void checkCameraPermission(Activity activity) {
int hasCameraPermission = ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA);
if (hasCameraPermission != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.CAMERA}, PERMISSION_REQUEST_CODE_CAMERA);
} else {
cameraPermissionGranted(true);
}
}
權限的判斷和申請其實比較簡單晌缘,對于開發(fā)者來說更重要的其實是權限被接受或拒絕后的不同處理,也就是上面的cameraPermissionGranted(boolea granted)方法痢站。比如如果你的應用必須使用相機磷箕,那么在相機權限申請拒絕后可以彈框提示用戶,直到用戶授權后才能進入使用界面阵难。又或者應用并非必須使用相機岳枷,也可以從相冊加載圖片,那么當用戶拒絕授權時呜叫,只需要禁掉相機部分的功能即可空繁。
3、special類型的權限包括WRITE_SETTINGS和SYSTEM_ALERT_WINDOW朱庆,Android單獨制作了一個activity作為這兩個權限的用戶授權界面家厌,必須通過指定intent,然后通過startActivity(intent)的方式來申請椎工。
special類型的權限申請主要用到以下幾個方法:
Settings.System.canWrite(Context context) 檢查是否被授予了WRITE_SETTINGS權限
Settings.canDrawOverlays(Context context) 檢查是否被授予了SYSTEM_ALERT_WINDOW權限
startActivityForResult(Intent intent, in requestCode) 打開用戶授權界面
onActivityResult(int requestCode, int resultCode, Intent data) 權限申請結果回調
申請WRITE_SETTINGS權限的代碼如下:
public void checkWriteSettingsPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.System.canWrite(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS, Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, PERMISSION_REQUEST_CODE_WRITE_SETTINGS);
} else {
writeSettingsPermissionGranted(true);
}
} else {
writeSettingsPermissionGranted(true);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PERMISSION_REQUEST_CODE_WRITE_SETTINGS) {
// 判斷是否有WRITE_SETTINGS權限
if (Settings.System.canWrite(this)) {
writeSettingsPermissionGranted(true);
}else {
writeSettingsPermissionGranted(false);
}
}
}
申請SYSTEM_ALERT_WINDOW的代碼如下:
public void checkSystemAlertWindowPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.canDrawOverlays(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, PERMISSION_REQUEST_CODE_SYSTEM_ALERT_WINDOW);
} else {
systemAlertWindowPermissionGranted(true);
}
} else {
systemAlertWindowPermissionGranted(true);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PERMISSION_REQUEST_CODE_SYSTEM_ALERT_WINDOW) {
if (Settings.canDrawOverlays(this)) {
systemAlertWindowPermissionGranted(true);
} else {
systemAlertWindowPermissionGranted(false);
}
}
}