Android 6.0 Marshmallow首次加入了運行時權限管理,這對用戶來說,可以更好的瞭解、控 制 app 涉及到的權限。然而對開發者來說卻是一件比較蛋疼的事情,需要兼容適配,並保證程序功能的正常運行。
什麼叫運行時權限管理呢?在Android 6.0以下的系統中,當我們在安裝應用的時候,該應用就會提示我們這個應用所需要的權限,如果你要安裝,那就必須同意賦予所有權限,但是如果不同意,那就只能取消安裝了,有點流氓。而且安裝完後,你不可以收回這個權限。
而6.0就做到了運行時權限管理,即使安裝的時候給了權限,也可以到系統設置裏,去關閉該權限。
下面分幾種情況來講,因爲運行時權限只有在Android6.0及以上的手機版本纔有,所以這裏只考慮設備版本大於6.0的手機,低版本的手機在安裝時就已經賦予了所有的權限,也不可能收回,就不考慮了,下面的情況只分targetSdkVersion:
- targetSDKVersion大於等於23的時候,那麼權限是可以被回收(revoke),這裏還要分權限,google將權限分爲兩種,一種是normal permission,另一種是dangerous permission。normal permission是指與用戶隱私無關的權限,可以理解爲無關緊要的權限,比如說訪問網絡的權限,對用戶來說沒什麼關係;dangerous permission就是會涉及到用戶隱私的權限,例如讀取用戶手機聯繫人、短信等等。如果是normal permission的話,那麼在安裝的時候就會給,而且不會開放接口讓用戶回收該權限,app會一直擁有該權限,所以不用考慮這種類型的權限。如果是dangerous
permission的話,在安裝的時候並未授予權限,系統開放接口允許用戶回收或者賦予權限。下面是某個應用的權限,第一張圖是dangerous permission,可以回收和賦予的。
點擊上面的所有權限,可以查看到該應用所有的權限。
那麼對於dangerous permission的話,在使用前需要去檢查該permission是否已經被授予
checkSelfPermission(Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED
- 1
- 1
如果該權限已經被授予,那麼可繼續執行你的代碼,如果未授予,則需要向用戶詢問是否需要授予權限,彈出的框是系統界面,界面如下:
調用代碼:
requestPermissions(new String[]{Manifest.permission.READ_PHONE_STATE}, 1);
- 1
- 1
系統詢問是否授予權限的頁面結束後會有回調
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 1) {
if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
//your implementations
}else{
ToastUtil.show(this, "權限不足,支付失敗");
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 如果targetSdkVersion是小於23的,那麼將認爲app沒有用23新權限測試過,那麼繼續使用老規則:系統在安裝的時候會默認給app賦予所有的權限,app可以照常運行。但是!但是!但是!用戶依然可以回收權限,不過此回收非彼回收。先看下關閉權限時彈的頁面:
看到了麼,如果targetSDKVersion小於23的話,在關閉權限的時候,會彈一個警告框,告訴你這是舊版的android,關閉會有問題,如果你按拒絕,那麼該權限將會關閉,而且界面上,權限的開關會顯示關,但是這個權限卻沒有被回收(nexus 5x的手機親測,當然其他的機子我也不敢打包票),checkSelfPermission返回granted。我看了下,如果targetSDKVersion等於23的話,系統日誌是:
如果targetSDKVersion是小於23的話,則日誌是:
Permission related app op changed.
不過我猜想如果你要支持runtime permission的話,還是要把targetSDKVersion設爲23。如果你的targetSDKVersion是小於23的話,那麼還是要加上checkSelfPermission,以防萬一,誰知道google會出什麼坑。
還有就是權限的話,有分組的概念,看如下圖:
如果一組中有一個被授予了,那麼組內的也會默認被授予。
而且也支持同時申請多個權限,具體情況android developer官網。
下面是stackoverflow問題的網址:
http://stackoverflow.com/questions/36328151/ive-revoke-the-android-permission-but-checkselfpermission-still-return-granted
如有問題和錯誤的地方請指出。
下面就是要講一些權限管理注意的地方。
對於權限的話,Activity和Fragment都有自己的requestPermissions和onRequestPermissionsResult回調,但是Activity是有checkSelfPermission,但是fragment是沒有的,所以fragment如果想要檢查權限,還得調用宿主activity的checkSelfPermission。
對了,平時直接調用checkSelfPermission和requestPermissions會報什麼api錯誤,雖然編譯不會通過,但是看着就是煩啊,可以調用ActivityCompat.checkSelfPermission(在supportv4包中)。
先看下Activity的requestSelfPermission這個方法:
public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
}
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
看下這裏是直接打開另一個Activity進行操作,還用了startActivityForResult,回調會通過onRequestPermissionResult,我想這個回調應該是在onActivityResult裏面處理,然後調這個onRequestPermissionResult函數的。
那麼問題來了,如果我在onResume函數中申請某一個權限,調用requestPermissions,那麼現象是什麼樣的呢?
第一次進入頁面,彈出申請權限的對話框,如果點擊同意,則正常,對話框不會再顯示,但是如果第一次點擊拒絕,則點擊拒絕後又一次彈出對話框來申請權限,如果你一直點拒絕,則對話框一直彈出,這是爲什麼呢?因爲第一次進入頁面並執行onResume函數時申請權限,進入另一個頁面,彈出對話框,如果你點擊拒絕,先是回調onRequestPermissionResult,然後再執行onResume函數,這時又會再一次去檢查權限,因爲發現無權限,則再一次請求,如是,進入一個循環之中,除非你點同意,否則是個無限循環。所以申請權限最好還是不要寫在onResume中,或者加一個標誌位判斷。
接下來再講講Fragment請求權限。
public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
if (mHost == null) {
throw new IllegalStateException("Fragment " + this + " not attached to Activity");
}
mHost.onRequestPermissionsFromFragment(this, permissions,requestCode);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
這裏看到其實是調用mHost請求權限的方法,mHost就是這個fragment的宿主Activity,所以Fragment請求權限實際上也是通過宿主Activity,當權限結果回調時,activity判斷是從Fragment中來的還是從自己Activity中來的,再進行分發結果。
Github上也有一些比較好用的權限庫:https://github.com/hotchemi/PermissionsDispatcher