您好,登錄后才能下訂單哦!
這篇文章主要講解了“為什么說在Android中請求權限從來都不是一件簡單的事情”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“為什么說在Android中請求權限從來都不是一件簡單的事情”吧!
假設我正在開發一個拍照功能,拍照功能通常都需要用到相機權限和定位權限,也就是說,這兩個權限是我實現拍照功能的先決條件,一定要用戶同意了這兩個權限我才能繼續進行拍照。
那么怎樣去申請這兩個權限呢?Android 提供的運行時權限 API 相信每個人都很熟悉了,我們自然而然可以寫出如下代碼:
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA, Manifest.permission.ACCESS_FINE_LOCATION), 1) } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) when (requestCode) { 1 -> { var allGranted = true for (result in grantResults) { if (result != PackageManager.PERMISSION_GRANTED) { allGranted = false } } if (allGranted) { takePicture() } else { Toast.makeText(this, "您拒絕了某項權限,無法進行拍照", Toast.LENGTH_SHORT).show() } } } } fun takePicture() { Toast.makeText(this, "開始拍照", Toast.LENGTH_SHORT).show() } }
可以看到,這里先是通過調用 requestPermissions() 方法請求相機權限和定位權限,然后在 onRequestPermissionsResult() 方法里監聽授權的結果。如果用戶同意了這兩個權限,那么我們就可以去進行拍照了,如果用戶拒絕了任意一個權限,那么彈出一個 Toast 提示,告訴用戶某項權限被拒絕了,從而無法進行拍照。
這種寫法麻煩嗎?這個就仁者見仁智者見智了,有些朋友可能覺得這也沒多少行代碼呀,有什么麻煩的。但我個人認為還是比較麻煩的,每次需要請求運行時權限時,我都會覺得很心累,不想寫這么啰嗦的代碼。
不過我們暫時不從簡易性的角度考慮,從正確性的角度上來講,這種寫法對嗎?我認為是有問題的,因為我們在權限被拒絕時只是彈了一個 Toast 來提醒用戶,并沒有提供后續的操作方案,用戶如果真的拒絕了某個權限,應用程序就無法繼續使用了。
因此,我們還需要提供一種機制,當權限被用戶拒絕時,可以再次重新請求權限。
現在我對代碼進行如下修改:
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) requestPermissions() } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) when (requestCode) { 1 -> { var allGranted = true for (result in grantResults) { if (result != PackageManager.PERMISSION_GRANTED) { allGranted = false } } if (allGranted) { takePicture() } else { AlertDialog.Builder(this).apply { setMessage("拍照功能需要您同意相機和定位權限") setCancelable(false) setPositiveButton("確定") { _, _ -> requestPermissions() } }.show() } } } } fun requestPermissions() { ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA, Manifest.permission.ACCESS_FINE_LOCATION), 1) } fun takePicture() { Toast.makeText(this, "開始拍照", Toast.LENGTH_SHORT).show() } }
這里我將請求權限的代碼提取到了一個 requestPermissions() 方法當中,然后在 onRequestPermissionsResult() 里判斷,如果用戶拒絕了某項權限,那么就彈出一個對話框,告訴用戶相機和定位權限是必須的,然后在 setPositiveButton 的點擊事件中調用 requestPermissions() 方法重新請求權限。
可以看到,現在我們對權限被拒絕的場景進行了更加充分的考慮。
那么現在這種寫法,是不是就將請求運行時權限的各種場景都考慮周全了呢?其實還沒有,因為 Android 權限系統還提供了一種非常 “惡心” 的機制,叫拒絕并不再詢問。
當某個權限被用戶拒絕了一次,下次我們如果再申請這個權限的話,界面上會多出一個拒絕并不再詢問的選項。只要用戶選擇了這一項,那么完了,我們之后都不能再去請求這個權限了,因為系統會直接返回我們權限被拒絕。
這種機制對于用戶來說非常友好,因為它可以防止一些惡意軟件流氓式地無限重復申請權限,從而嚴重騷擾用戶。但是對于開發者來說,卻讓我們苦不堪言,如果我的某項功能就是必須依賴于這個權限才能運行,現在用戶把它拒絕并不再詢問了,我該怎么辦?
當然,絕大多數的用戶都不是傻 X,當然知道拍照功能需要用到相機權限了,相信 99% 的用戶都會點擊同意授權。但是我們可以不考慮那剩下 1% 的用戶嗎?不可以,因為你們公司的測試就是那 1% 的用戶,他們會進行這種傻 X 式的操作。
也就是說,即使只為了那 1% 的用戶,為了這種不太可能會出現的操作方式,我們在程序中還是得要將這種場景充分考慮進去。
那么,權限被拒絕且不再詢問了,我們該如何處理呢?比較通用的處理方式就是提醒用戶手動去設置當中打開權限,如果想做得再好一點,可以提供一個自動跳轉到當前應用程序設置界面的功能。
下面我們就來針對這種場景進行完善,如下所示:
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) requestPermissions() } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) when (requestCode) { 1 -> { val denied = ArrayList<String>() val deniedAndNeverAskAgain = ArrayList<String>() grantResults.forEachIndexed { index, result -> if (result != PackageManager.PERMISSION_GRANTED) { if (ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[index])) { denied.add(permissions[index]) } else { deniedAndNeverAskAgain.add(permissions[index]) } } } if (denied.isEmpty() && deniedAndNeverAskAgain.isEmpty()) { takePicture() } else { if (denied.isNotEmpty()) { AlertDialog.Builder(this).apply { setMessage("拍照功能需要您同意相冊和定位權限") setCancelable(false) setPositiveButton("確定") { _, _ -> requestPermissions() } }.show() } else { AlertDialog.Builder(this).apply { setMessage("您需要去設置當中同意相冊和定位權限") setCancelable(false) setPositiveButton("確定") { _, _ -> val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) val uri = Uri.fromParts("package", packageName, null) intent.data = uri startActivityForResult(intent, 1) } }.show() } } } } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) when (requestCode) { 1 -> { requestPermissions() } } } fun requestPermissions() { ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA, Manifest.permission.ACCESS_FINE_LOCATION), 1) } fun takePicture() { Toast.makeText(this, "開始拍照", Toast.LENGTH_SHORT).show() } }
現在代碼已經變得比較長了,我還是帶著大家來梳理一下。
這里我在 onRequestPermissionsResult() 方法中增加了 denied 和 deniedAndNeverAskAgain 兩個集合,分別用于記錄拒絕和拒絕并不再詢問的權限。如果這兩個集合都為空,那么說明所有權限都被授權了,這時就可以直接進行拍照了。
而如果 denied 集合不為空,則說明有權限被用戶拒絕了,這時候我們還是彈出一個對話框來提醒用戶,并重新申請權限。而如果 deniedAndNeverAskAgain 不為空,說明有權限被用戶拒絕且不再詢問,這時就只能提示用戶去設置當中手動打開權限,我們編寫了一個 Intent 來執行跳轉邏輯,并在 onActivityResult() 方法,也就是用戶從設置回來的時候重新申請權限。
可以看到,當我們第一次拒絕權限的時候,會提醒用戶,相機和定位權限是必須的。而如果用戶繼續置之不理,選擇拒絕并不再詢問,那么我們將提醒用戶,他必須手動開戶這些權限才能繼續運行程序。
到現在為止,我們才算是把一個 “簡單” 的權限請求流程用比較完善的方式處理完畢。然而代碼寫到這里真的還算是簡單嗎?每次申請運行時權限,都要寫這么長長的一段代碼,你真的受得了嗎?
這也就是我編寫 PermissionX 這個開源庫的原因,在 Android 中請求權限從來都不是一件簡單的事情,但它不應該如此復雜。
PermissionX 將請求運行時權限時那些應該考慮的復雜邏輯都封裝到了內部,只暴露最簡單的接口給開發者,從而讓大家不需要考慮上面我所討論的那么多場景。
而我們使用 PermissionX 來實現和上述一模一樣的功能,只需要這樣寫就可以了:
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) PermissionX.init(this) .permissions(Manifest.permission.CAMERA, Manifest.permission.ACCESS_FINE_LOCATION) .onExplainRequestReason { scope, deniedList -> val message = "拍照功能需要您同意相冊和定位權限" val ok = "確定" scope.showRequestReasonDialog(deniedList, message, ok) } .onForwardToSettings { scope, deniedList -> val message = "您需要去設置當中同意相冊和定位權限" val ok = "確定" scope.showForwardToSettingsDialog(deniedList, message, ok) } .request { _, _, _ -> takePicture() } } fun takePicture() { Toast.makeText(this, "開始拍照", Toast.LENGTH_SHORT).show() } }
可以看到,請求權限的代碼一下子變得極其精簡。
我們只需要在 permissions() 方法中傳入要請求的權限名,在 onExplainRequestReason() 和 onForwardToSettings() 回調中填寫對話框上的提示信息,然后在 request() 回調中即可保證已經得到了所有請求權限的授權,調用 takePicture() 方法開始拍照即可。
通過這樣的直觀對比大家應該能感受到 PermissionX 所帶來的便利了吧?上面那段長長的請求權限的代碼我真的是為了給大家演示才寫的,而我再也不想寫第二遍了。
另外,本篇文章主要只是演示了一下 PermissionX 的易用性,并不涉及其中具體的諸多用法,如 Android 11 兼容性,自定義對話框樣式等等。如果大家感興趣的話,更多用法請參考下面的鏈接。
Android 運行時權限終極方案,用 PermissionX 吧
PermissionX 現在支持 Java 了!還有 Android 11 權限變更講解
PermissionX 重磅更新,支持自定義權限提醒對話框
在項目中引入 PermissionX 也非常簡單,只需要添加如下的依賴即可:
dependencies { ... implementation 'com.permissionx.guolindev:permissionx:1.3.1' }
感謝各位的閱讀,以上就是“為什么說在Android中請求權限從來都不是一件簡單的事情”的內容了,經過本文的學習后,相信大家對為什么說在Android中請求權限從來都不是一件簡單的事情這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。