Android6.0許可權的動態適配

馬果果發表於2019-03-04
        Android6.0代號棉花糖。儘管是在15年I/O大會上Google被正式釋出的了。但是看看大多數人的專案中大家的 targetSdkVersion 是不是還都用的22。大家都認為6.0+的市場佔有率還沒那麼高。那麼就請看谷歌2017年9月份公佈的版本分佈圖。

Android6.0許可權的動態適配

從資料來看確實沒那麼高O(∩_∩)O。6.0+的市場佔有率僅為50% ̄□ ̄||。只因安卓使用者的基數太大了吧。延伸至各種人群。雖然說佔比才一半但時基數大總的使用者數量還是蠻多的。這兩天剛做完6.0許可權的適配。那麼請說一下自己測試的時候踩的坑吧(*╹▽╹*)


許可權的分組

在Android6.0(M)之後,對許可權進行了分類,大致有這三種:

普通許可權
危險許可權
特殊許可權

普通許可權:也就是正常許可權,是對手機的一些正常操作,對使用者的隱私沒有太大影響的許可權,比如手機的震動,網路訪問,藍芽等許可權,這些許可權會在應用被安裝的時候預設授予,使用者不能拒絕,也不能取消。


普通許可權列表:
ACCESS_LOCATION_EXTRA_COMMANDS
ACCESS_NETWORK_STATE
ACCESS_NOTIFICATION_POLICY
ACCESS_WIFI_STATE
BLUETOOTH
BLUETOOTH_ADMIN
BROADCAST_STICKY
CHANGE_NETWORK_STATE
CHANGE_WIFI_MULTICAST_STATE
CHANGE_WIFI_STATE
DISABLE_KEYGUARD
EXPAND_STATUS_BAR
GET_PACKAGE_SIZE
INTERNET
KILL_BACKGROUND_PROCESSES
MODIFY_AUDIO_SETTINGS
NFC
READ_SYNC_SETTINGS
READ_SYNC_STATS
RECEIVE_BOOT_COMPLETED
REORDER_TASKS
REQUEST_INSTALL_PACKAGES
SET_TIME_ZONE
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS
SET_ALARM
INSTALL_SHORTCUT
UNINSTALL_SHORTCUT

複製程式碼
對於上面這些普通許可權 在Android6.0以前我們只需要在清單檔案中宣告該許可權即可。


危險許可權:其實就是執行中需要處理的許可權,也是我們最需要注意的許可權,這些許可權會關係到使用者的隱私或影響到其他應用的執行,這些危險許可權,谷歌還做了一個許可權組,以分組的形式來呈現:

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) 方法返回值有兩個:

PERMISSION_DENIED = -1:代表當前檢查的許可權沒有被授權

PERMISSION_GRANTED = 0;代表當前的檢查的許可權已經被授權

 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() {
       //在這表示使用者同意了許可權申請。
       //假如使用者拒絕了許可權申請在這兒我是沒讓他進入到應用中的效果如下
    }
})複製程式碼


Android6.0許可權的動態適配


只要有任何一個許可權使用者沒通過都會彈出這個Dialog。直到使用者全部授權。。。。

點選取消退出應用。確定按鈕去到設定介面為應用授權。。。。

下面是應用啟動的場景(很舒服2333)

Android6.0許可權的動態適配

還有個劇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)
}複製程式碼

ok。暫時是這樣做的。可以解決剛需。。。。有什麼問題後續更改哈。。。。。。。。


相關文章