Android版本6.0引入了許多新功能,極大地改善了用戶體驗,同時也給程序員帶來了新的負擔,本文是武林技術頻道小編介紹的詳解Android 6.0權限的動態適配,可供所需朋友參考。
前言
Android6.0代號棉花糖。盡管是在15年I/O大會上Google被正式發布的了。但是看看大多數人的項目中大家的 targetSdkVersion 是不是還都用的22。大家都認為6.0+的市場占有率還沒那么高。那么就請看谷歌2017年9月份公布的版本分布圖。

從數據來看確實沒那么高O(∩_∩)O。6.0+的市場占有率僅為50% ̄□ ̄||。只因安卓用戶的基數太大了吧。延伸至各種人群。雖然說占比才一半但時基數大總的用戶數量還是蠻多的。這兩天剛做完6.0權限的適配。那么請說一下自己測試的時候踩的坑吧(*?▽?*)
權限管理系統的變化
在Android6.0(M)之前,在用戶安裝應用的時候會產生一個權限列表,只有用戶允許這些權限后,應用才可以正常的安裝,這就會產生一個問題,這些權限對用戶是不具有感知性的,也就是說用戶都不知道你要這些權限干什么,我明明裝的是一個閱讀類型的應用,你卻要我撥打電話的權限,你想干嘛呢?當然絕大部分的開發者是善意的,但也避免不了一些特殊人群利用這些“漏洞”做一些不好的事情。
而在Android6.0(M)之后,用戶是可以不管權限直接安裝應用的,當應用需要調用某些權限的時候,會給予用戶一個通知與說明,我要這些權限干什么,這樣下來可以讓用戶有更加清醒的權限分配意識,也在一定程度上更加人性化的保護了用戶的隱私,避免了“權限一刀切”。
權限的分組
在Android6.0(M)之后,對權限進行了分類,大致有這三種:
普通權限:也就是正常權限,是對手機的一些正常操作,對用戶的隱私沒有太大影響的權限,比如手機的震動,網絡訪問,藍牙等權限,這些權限會在應用被安裝的時候默認授予,用戶不能拒絕,也不能取消。
普通權限列表:
ACCESS_LOCATION_EXTRA_COMMANDSACCESS_NETWORK_STATEACCESS_NOTIFICATION_POLICYACCESS_WIFI_STATEBLUETOOTHBLUETOOTH_ADMINBROADCAST_STICKYCHANGE_NETWORK_STATECHANGE_WIFI_MULTICAST_STATECHANGE_WIFI_STATEDISABLE_KEYGUARDEXPAND_STATUS_BARGET_PACKAGE_SIZEINTERNETKILL_BACKGROUND_PROCESSESMODIFY_AUDIO_SETTINGSNFCREAD_SYNC_SETTINGSREAD_SYNC_STATSRECEIVE_BOOT_COMPLETEDREORDER_TASKSREQUEST_INSTALL_PACKAGESSET_TIME_ZONESET_WALLPAPERSET_WALLPAPER_HINTSTRANSMIT_IRUSE_FINGERPRINTVIBRATEWAKE_LOCKWRITE_SYNC_SETTINGSSET_ALARMINSTALL_SHORTCUTUNINSTALL_SHORTCUT
對于上面這些普通權限 在Android6.0以前我們只需要在清單文件中聲明該權限即可。
危險權限:其實就是運行中需要處理的權限,也是我們最需要注意的權限,這些權限會關系到用戶的隱私或影響到其他應用的運行,這些危險權限,谷歌還做了一個權限組,以分組的形式來呈現:

由于運行權限機制的出現,我們需要對新開發的應用去做適配。
當你的應用targetSdkVersion小于23的時候,當應用用于6.0以上的系統時候,它也會默認采用以前的權限管理機制。當你的targetSdkVersion大于等于23的時候且在Andorid6.0(M)系統上,它才會采用新的這套權限管理機制。
所以如果你想逃開這個“麻煩”,只要把targetSdkVersion的版本設置為低于23就可以了,不過不建議采用這種方案,該來的總是要來的,隨著國產手機ROM的更新,比如小米,華為等也開始有部分機型進行了系統升級,所以這是種趨勢。
說了這么多,那么來看下怎么進行Android6.0(M)的權限管理適配吧,其實很簡單,只需要記住下面幾個API方法就可以:(API23之后提供)
int checkSelfPermission(String permission) 用來檢測應用是否已經具有權限void requestPermissions(String[] permissions, int requestCode) 進行請求單個或多個權限void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) 請求權限結果回調
checkSelfPermission(String permission) 方法返回值有兩個:
requestPermissions(String[] permissions, int requestCode)?
參數一:要請求的權限組 ? 權限2請求碼
onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)請求的回調。
參數3對應 對應permissions的權限請求結果(PERMISSION_GRANTED或者PERMISSION_DENIED)
看完關鍵的三個方法接下來上我的油條:
object MQPermissionUtil { private var mRequestCode = -1 private val isOverMarshmallow: Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M private var mOnPermissionListener: OnPermissionListener? = null fun requestPermissions(activity: Activity, requestCode: Int, permissions: Array<String>, isCancelFinish: Boolean, onPermissionGrantedListener: OnPermissionGrantedListener) { requestPermissionsResult(activity, requestCode, permissions, object : OnPermissionListener { override fun onPermissionGranted() { onPermissionGrantedListener.onPermissionGranted() } override fun onPermissionDenied() { if (isCancelFinish) { showTipsDialogWel(activity) } else { showTipsDialog(activity) } } }) } private fun requestPermissionsResult(activity: Activity, requestCode: Int, permissions: Array<String>, callback: OnPermissionListener) { mOnPermissionListener = callback if (checkPermissions(activity, *permissions)) { if (mOnPermissionListener != null) mOnPermissionListener!!.onPermissionGranted() } else { val deniedPermissions = getDeniedPermissions(activity, *permissions) if (deniedPermissions.isNotEmpty()) { mRequestCode = requestCode ActivityCompat.requestPermissions(activity, deniedPermissions .toTypedArray(), requestCode) } } } fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) { if (requestCode == mRequestCode) { if (verifyPermissions(grantResults)) { if (mOnPermissionListener != null) mOnPermissionListener!!.onPermissionGranted() } else { if (mOnPermissionListener != null) mOnPermissionListener!!.onPermissionDenied() } } } private fun startAppSettings(context: Context) { val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) intent.data = Uri.parse("package:" + context.packageName) context.startActivity(intent) } private fun verifyPermissions(grantResults: IntArray): Boolean { if (grantResults.isEmpty()) return false // 循環判斷每個權限是否被拒絕 for (grantResult in grantResults) { if (grantResult != PackageManager.PERMISSION_GRANTED) { return false } } return true } private fun getDeniedPermissions(context: Context, vararg permissions: String): List<String> { val deniedPermissions = ArrayList<String>() for (permission in permissions) { if (ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_DENIED) { deniedPermissions.add(permission) } } return deniedPermissions } private fun checkPermissions(context: Context, vararg permissions: String): Boolean { if (isOverMarshmallow) { for (permission in permissions) { if (ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_DENIED) { return false } } } return true } fun showTipsDialog(activity: Activity) { AlertDialog.Builder(activity) .setTitle("提示信息") .setMessage("當前應用缺少必要權限,無法正常使用,請單擊【確定】按鈕前往設置中心進行權限授權。") .setNegativeButton("取消", null) .setPositiveButton("確定") { _, _ -> activity.finish() startAppSettings(activity) }.show() } fun showTipsDialogWel(activity: Activity) { AlertDialog.Builder(activity) .setTitle("提示信息") .setMessage("當前應用缺少必要權限,無法正常使用,請單擊【確定】按鈕前往設置中心進行權限授權。") .setNegativeButton("取消") { _, _ -> activity.finish() } .setPositiveButton("確定") { _, _ -> activity.finish() startAppSettings(activity) }.show() } interface OnPermissionGrantedListener { fun onPermissionGranted() } interface OnPermissionListener { fun onPermissionGranted() fun onPermissionDenied() }}寫的不好。大家自行修改吧。
Activity中的使用在onCreate中一開始調用一下代碼:
MangoPermissionUtil.requestPermissions(this@IndexActivity, Constant.PERMISSION_OPERATION_CODE_SCAN, arrayOf(Manifest.permission.CAMERA), false, object : MangoPermissionUtil.OnPermissionGrantedListener { override fun onPermissionGranted() { //在這表示用戶同意了權限申請。 //假如用戶拒絕了權限申請在這兒我是沒讓他進入到應用中的效果如下 }})
只要有任何一個權限用戶沒通過都會彈出這個Dialog。直到用戶全部授權。。。。
點擊取消退出應用。確定按鈕去到設置界面為應用授權。。。。
下面是應用啟動的場景(很舒服2333)

還有個劇TM惡心的問題這些所有的邏輯在除了小米6.xxx的設備上跑是沒問題的。必須全部授權才能進入應用。但是小米6.xxx的設備上當我第一次拒絕了權限申請之后。第二次進入應用判斷權限的時候它竟然在checkPermisssion的方法中給我返回了PERMISSION_GRANTED這就比較尷尬了。這樣我是可以進入掉權限請求成功的回調。但是我進去之后確實沒權限啊。對應權限相關的操作一樣不能執行。。。不得不說小米的6.xxx設備是真的坑。。。。
還有一點油條用的時候還要在當前申請的Activity中調用一下來執行到油條中自定義的回調
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) { MangoPermissionUtil.onRequestPermissionsResult(requestCode, permissions, grantResults) super.onRequestPermissionsResult(requestCode, permissions, grantResults)}最后
對于一些比較特別的權限,比如文件的讀寫權限,一般在我們第一次開啟APP的時候就要去獲取了,假設我們一開始沒有獲取到這個權限,那么如果我的首頁有輪播廣告圖,這個廣告圖是網絡獲取的,做了三級緩存,這樣就會到導致磁盤緩存無法寫入。這邊提供一個解決方法,就是在你引導APP啟動的時候,就引導用戶去獲取權限,當用戶拒絕的時候,應該給出彈出框并跳轉對應的應用權限管理界面(需要對不同機型進行設置)。
可以參考微信的做法:
啟動app,在閃屏頁的時候向用戶提出權限的申請
相信本文介紹的詳解Android 6.0權限的動態適配,對于大家學習這些方面知識都有幫助,感興趣的伙伴千萬別錯過了哦!
新聞熱點
疑難解答