谷歌在2015年8月份時候,發佈了Android 6.0版本,代號叫作「棉花糖」(Marshmallow ),其中的很大的一部分變化,是在用戶權限受權上,或許是感受以前默認受權的不合理,如今6.0出來,使得用戶權限受權變得合理。這可能也是參考IOS系統的,只有在用戶須要使用權限的時候,纔去受權請求,這樣作的目的是提升用戶體驗,固然,用戶感受好了,受苦的是咱們開發人員,原來的規則不適用了,如今咱們去適應新的規則,畢竟是靠谷歌這顆大樹吃飯的嘛。html
在Android 6.0版本以前,權限都是一條龍服務的,只要用戶安裝完,AndroidManifest清單上申請的權限都會被系統默認受權,而且受權後也撤銷不了。這樣的弊端在哪裏呢?有些權限可能用戶以爲不須要,好比他不想有通知的權限,不想受到通知的干擾,那麼他就不能屏蔽通知,就是不須要的權限,他去不掉,自主權不在他那邊。還有一些狀況是,一些惡意程序,會利用這個權限默認受權,進行惡意獲取用戶數據和攻擊。因此Android 6.0版本,一方面讓用戶更加容易的控制本身的隱私,一方面須要從新適配應用權限。android
採用新的權限模型,只有在須要權限的時候,才告知用戶是否受權,是在runtime時候受權,而不是在原來安裝的時候 ,同時默認狀況下每次在運行時打開頁面時候,須要先檢查是否有所須要的權限申請。這樣的用戶的自主性提升不少,好比用戶能夠給APP賦予攝像的權限,可是能夠拒絕記錄設備位置的權限,就是怕位置信息上傳等等。git
在API 23中,權限知足的標準流程:github
但這裏有個問題,那就是在系統受權彈窗環節,提醒框會有個再也不提示的複選框,若是用戶點擊不太提示,並拒絕受權,那麼再下次受權的時候,系統受權彈窗的提示框就不會在提示,因此咱們頗有必要須要自定義權限彈窗提示框,那麼流程圖就變成以下了。app
在圖中,咱們能夠看到整個權限裏,能夠分爲系統權限和特殊權限受權。系統權限中,又分爲normal和dangerous類型。async
normal:這個權限類型並不直接威脅到用戶的隱私,能夠直接在manifest清單裏註冊,系統會幫咱們默認受權的。ide
dangerous:這個能夠直接給app訪問用戶一些敏感的數據,不只須要在manifest清單裏註冊,同時在使用的時候,須要向系統請求受權。ui
值得注意一點,這裏有特殊權限受權的區別,分別是SYSTEM_ALERT_WINDOW 和 WRITE_SETTINGS,雖然這兩個權限也是屬於dangerous權限類型,可是這兩個受權請求方式和其餘dangerous權限是不同的,須要特殊處理 。this
normal列表:spa
dangerous列表:
Permission Group | Permissions |
---|---|
CALENDAR |
|
CAMERA |
|
CONTACTS |
|
LOCATION |
|
MICROPHONE |
|
PHONE |
|
SENSORS |
|
SMS |
|
STORAGE |
固然,谷歌也是考慮到若是是以API 23以前的版本編譯的APP,在6.0的系統上運行,仍是能兼容的。若是是23以前的版本編譯的APP,則權限模型走的是老的模式,也就是在一條龍的模式,在manifest清單註冊,系統會幫咱們默認受權,而且也能運行。
但若是是以API 23的版本編譯的APP,那走的的必須是新的權限模型,在遇到dangerous權限的時候,必須進行受權的,若是沒有進行受權話,系統則會提示崩潰信息。因此,基於6.0系統的開發的程序, 在打開頁面的時候,必需要考慮這個頁面是否用到須要受權的權限,頁面檢測是否已經受權過了沒。
實現方式
第一步:檢測權限
// Assume thisActivity is the current activityint permissionCheck = ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.WRITE_CALENDAR);
第二步:請求權限
// Here, thisActivity is the current activityif (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) { // Should we show an explanation? if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity, Manifest.permission.READ_CONTACTS)) { // Show an expanation to the user *asynchronously* -- don't block // this thread waiting for the user's response! After the user // sees the explanation, try again to request the permission. } else { // No explanation needed, we can request the permission. ActivityCompat.requestPermissions(thisActivity, new String[]{Manifest.permission.READ_CONTACTS}, MY_PERMISSIONS_REQUEST_READ_CONTACTS); // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an // app-defined int constant. The callback method gets the // result of the request. } }
第三步:處理權限請求結果
@Overridepublic void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { case MY_PERMISSIONS_REQUEST_READ_CONTACTS: { // If request is cancelled, the result arrays are empty. if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // permission was granted, yay! Do the // contacts-related task you need to do. } else { // permission denied, boo! Disable the // functionality that depends on this permission. } return; } // other 'case' lines to check for other // permissions this app might request } }
其中要特別注意的是,若是是針對
「android.permission.SYSTEM_ALERT_WINDOW」
「android.permission.WRITE_SETTINGS」
這兩個權限,實現方式跟上以前在工做中,碰到一種狀況,若是是運行在6.0的版本上是須要走新的權限模型,若是是運行在老的版本上,則須要進行一個判斷,此時碰到一個問題是,在谷歌官方推薦中,在判斷app運行的系統是否在Android M上時,它的判斷是以下: Build.VERSION.CODENAME.equals("MNC"); 但是在我實際適配中,發現這句卻無效的,就改用Build.VERSION.SDK_INT >= 23。的,須要另外特殊處理。
針對SYSTEM_ALERT_WINDOW權限,須要向系統發送一個ACTION_MANAGE_OVERLAY_PERMISSION
.這樣一個動做,同時能夠用Settings.canDrawOverlays() 方法進行判斷以前是否已經受權過了。
針對WRITE_SETTINGS權限,須要向系統發送一個ACTION_MANAGE_WRITE_SETTINGS 這樣一個動做,同時能夠用
Settings.System.canWrite()
.
方法進行判斷以前是否已經受權過了。
具體代碼:
Intent intent = new Intent(android.provider.Settings.ACTION_MANAGE_WRITE_SETTINGS); intent.setData(Uri.parse("package:" + getPackageName())); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent);
以前在工做中,碰到一種狀況,若是是運行在6.0的版本上是須要走新的權限模型,若是是運行在老的版本上,則須要進行一個判斷,此時碰到一個問題是,在谷歌官方推薦中,在判斷app運行的系統是否在Android M上時,它的判斷是以下: Build.VERSION.CODENAME.equals("MNC"); 但是在我實際適配中,發現這句卻無效的,就改用Build.VERSION.SDK_INT >= 23。
首先要知道6.0版本權限模型跟原來版本是不一樣的,再也不是統一在manifest中默認系統受權,而是有須要的時候,向系統請求受權,提升用戶體驗。
瞭解權限檢測流程,一點注意點是若是系統權限彈窗提示框被再也不提醒了,須要咱們自定義提示彈窗,引導用戶去受權。
明白權限類型,分爲normal和dangerous類型,同時,在dangerous中還須要注意一點,SYSTEM_ALERT_WINDOW 和 WRITE_SETTINGS這兩個特殊受權請求方式,跟通常受權請求方式不一樣。
在判斷APP是否運行在Android M上,能夠用版本號來判斷,能夠準確點。
有個具體demo:https://github.com/SpikeKing/wcl-permission-demo
流程圖:http://blog.csdn.net/caroline_wendy/article/details/50587230