Android出於系統穩定性以及用戶隱私方面的考慮,將應用程序訪問權限限制在各自的沙盒內。程序能夠隨意訪問所在沙盒內部的資源或者信息,訪問沙盒外部的則必須明確的申請相關訪問權限。應用程序所須要的權限須要在AndroidManifest.xml
文件中申明。如:html
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.douyoumi.permission"> <uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <application ...> ... </application> </manifest>
(本文出處:http://www.jianshu.com/p/0beb... android
系統權限根據敏感程度分爲普通權限和危險權限兩類。兩類權限都須要在AndroidManifest.xml
文件中申明。在Android 5.1 (API level 22) 及其如下版本上,系統在APP安裝時要求用戶受權全部權限,不然APP不能安裝;而在Android 6.0及其以上版本上,系統在APP安裝時受權全部普通權限,危險權限須要在使用時動態讓用戶受權。這使得Android的權限管理更加靈活,用戶能夠根據須要在設置應用中對應用的各個危險權限授予不一樣的權限。Android系統的權限管理不知道被多少人吐槽過,這一改進無疑是加分項。git
Android 6.0上使用在AndroidManifest.xml中已經申明的危險權限時須要用戶受權。針對權限請求相關操做系統提供了三個API。下面結合Android官方的開發指導對這幾個API作下說明。github
checkSelfPermission() 檢查是否已經具備了相關權限。任什麼時候候APP都要在執行須要危險權限的操做前去檢查是否具備相關權限,即便剛剛執行過這項操做,由於用戶頗有可能去設置應用中關閉了相關權限。app
shouldShowRequestPermissionRationale() 判斷是否須要向用戶解釋,爲何須要這些權限。有時候用戶會不理解應用程序爲何須要這些權限。如,相機應用申請攝像頭使用權限用戶容易理解,可是相機應用申請地理位置使用權限可能會讓用戶產生疑惑,由於用戶頗有能不知道相機須要保存每張照片的拍攝地點。這時候咱們就須要作適當的解釋說明了。這個方法只有在APP請求過某一權限且用戶禁止APP使用該權限的時候返回true。在用戶受權了權限和禁止權限時勾選了「Don't ask again」選項的狀況下都會返回false。Android官方開發指導還提到一點,爲避免給用戶帶來糟糕的用戶體驗,這裏的解釋說明應該是異步的,不要阻塞用戶的操做。時下不少適配了6.0的APP在這點上處理的都不盡如人意,有的根本沒有解釋說明,有的是彈出對話框,用戶體驗都不是很好。下文會給出了一個完美的解決方案。框架
requestPermissions() 申請相關權限。調用這個方法後會彈出一個系統對話框來向用戶申請權限,APP不能自定義這個對話框的內容,這也就增長了上面提到的解釋說明的必要性。這裏還有一點也須要交代一下。從上面危險權限列表中也能夠看出,這些權限都是有分組的。如,READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE權限就是屬於STORAGE組的。分門別類不只僅是爲了方便容易閱讀,組內權限在申請上也是有關聯的。異步
在申請組內某個權限時,彈出的系統對話框會顯示組名,而不是指明所申請的權限名。如,申請READ_EXTERNAL_STORAGE權限時,系統對話框提示請求「訪問sd卡」權限,但不會說明是請求的sd卡讀權限;async
申請權限時,若是組內有別的權限已經得到了用戶受權,系統再也不彈出詢問對話框,而是自動受權該權限。如,在申請WRITE_EXTERNAL_STORAGE權限時用戶已經受權了READ_EXTERNAL_STORAGE權限,系統則會自動受權WRITE_EXTERNAL_STORAGE權限,再也不詢問用戶;ide
即便有前一條規則存在,在使用每一條權限時都必須(不是應該)調用requestPermissions()
方法來申請權限。如,在已經獲取了READ_EXTERNAL_STORAGE權限的狀況下,使用WRITE_EXTERNAL_STORAGE權限時依然須要調用requestPermissions()
方法來申請,不然就會由於權限問題致使寫sd卡失敗。gradle
注意:危險權限在AndroidManifest.xml文件中也必須申明,不然動態申請會失敗。
下面代碼是Android官方開發指導中權限申請大體框架。它使用了Android Support Library中的方法。雖然Android 6.0之後的framework中都有這些方法,可是對於開發者來講使用Android Support Library中的方法更簡單,不用檢查sdk版本能夠兼容低版本。
// Here, thisActivity is the current activity if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { // Should we show an explanation? if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity, Manifest.permission.READ_EXTERNAL_STORAGE)) { // Show an expanation to the user *asynchronously* -- don't blockthis 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_EXTERNAL_STORAGE}, MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE); // MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE is anapp-defined int constant. // The callback method gets the result of the request. } }
requestPermissions()申請有結果後會回調onRequestPermissionsResult()
方法,這種方式對於Android開發者必定不會陌生,由於與startActivityForResult()結果回調onActivityResult()方法相似。以下重載onRequestPermissionsResult()
方法便可。
@Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { case MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE: { // 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 } }
因爲咱們使用了AndroidSupport Library庫記得在build.gradle文件中添加依賴。
dependencies { ... compile 'com.android.support:appcompat-v7:23.2.1' }
以上就是Android官方開發指導關於動態權限申請的所有內容了。系統的API已經很簡潔了,可是用在項目中時依然會出現不少重複代碼,按慣例是要封裝一下。在github上搜索"Android permissions"會有不少開源庫。Google在github上也開源了一個關於動態權限的封裝庫easypermissions。看了幾個庫後,我的以爲easypermissions寫的最好,可是也有不足的地方。如,解釋爲何須要權限的地方這個庫也彈出一個對話框阻塞了用戶操做,不符合開發規範、還有用戶禁止權限時勾選了「Don't ask again」後依然會彈出對話框請求權限,用戶體驗很差。因此我在easypermissions庫的基礎封裝了一個EasyPermissionsEx庫。說是一個庫,其實只有一個300行左右的類,你能夠直接拷貝到你的項目中使用。
前文也提到過在解釋爲何須要權限或者在用戶永久禁止權限後引導用戶去設置應用開啓權限時,使用對話框會給用戶帶來很差的體驗。EasyPermissionsEx採用的解決方案是使用snackbar來提醒用戶。總體效果以下。snackbar是一種輕量級的用戶交互,它不會阻塞用戶當前的操做,是從底部彈出一個bar來提示用戶,相似toast,可是snackbar又有一個button容許用戶操做。關於snackbar更多信息能夠跳轉到官方文檔瞭解。
Snackbar在Android Design Support Library庫中記得在build.gradle文件中添加依賴。
dependencies { ... compile 'com.android.support:design:23.4.0' }
EasyPermissionsEx權限請求邏輯以下。關於EasyPermissionsEx更多詳情能夠去github查看wiki,也能夠直接看代碼,由於它總共也就300行代碼。