關(guān)于Android 6.0權(quán)限的動態(tài)適配詳解
前言
Android6.0代號棉花糖。盡管是在15年I/O大會上Google被正式發(fā)布的了。但是看看大多數(shù)人的項目中大家的 targetSdkVersion 是不是還都用的22。大家都認為6.0+的市場占有率還沒那么高。那么就請看谷歌2017年9月份公布的版本分布圖。

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

由于運行權(quán)限機制的出現(xiàn),我們需要對新開發(fā)的應用去做適配。
當你的應用targetSdkVersion小于23的時候,當應用用于6.0以上的系統(tǒng)時候,它也會默認采用以前的權(quán)限管理機制。當你的targetSdkVersion大于等于23的時候且在Andorid6.0(M)系統(tǒng)上,它才會采用新的這套權(quán)限管理機制。
所以如果你想逃開這個“麻煩”,只要把targetSdkVersion的版本設(shè)置為低于23就可以了,不過不建議采用這種方案,該來的總是要來的,隨著國產(chǎn)手機ROM的更新,比如小米,華為等也開始有部分機型進行了系統(tǒng)升級,所以這是種趨勢。
說了這么多,那么來看下怎么進行Android6.0(M)的權(quán)限管理適配吧,其實很簡單,只需要記住下面幾個API方法就可以:(API23之后提供)
int checkSelfPermission(String permission) 用來檢測應用是否已經(jīng)具有權(quán)限void requestPermissions(String[] permissions, int requestCode) 進行請求單個或多個權(quán)限void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) 請求權(quán)限結(jié)果回調(diào)
checkSelfPermission(String permission) 方法返回值有兩個:
- PERMISSION_DENIED = -1:代表當前檢查的權(quán)限沒有被授權(quán)
- PERMISSION_GRANTED = 0;代表當前的檢查的權(quán)限已經(jīng)被授權(quán)
requestPermissions(String[] permissions, int requestCode)
參數(shù)一:要請求的權(quán)限組 權(quán)限2請求碼
onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)請求的回調(diào)。
參數(shù)3對應 對應permissions的權(quán)限請求結(jié)果(PERMISSION_GRANTED或者PERMISSION_DENIED)
看完關(guān)鍵的三個方法接下來上我的油條:
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
// 循環(huán)判斷每個權(quán)限是否被拒絕
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("當前應用缺少必要權(quán)限,無法正常使用,請單擊【確定】按鈕前往設(shè)置中心進行權(quán)限授權(quán)。")
.setNegativeButton("取消", null)
.setPositiveButton("確定") { _, _ ->
activity.finish()
startAppSettings(activity)
}.show()
}
fun showTipsDialogWel(activity: Activity) {
AlertDialog.Builder(activity)
.setTitle("提示信息")
.setMessage("當前應用缺少必要權(quán)限,無法正常使用,請單擊【確定】按鈕前往設(shè)置中心進行權(quán)限授權(quán)。")
.setNegativeButton("取消") { _, _ -> activity.finish() }
.setPositiveButton("確定") { _, _ ->
activity.finish()
startAppSettings(activity)
}.show()
}
interface OnPermissionGrantedListener {
fun onPermissionGranted()
}
interface OnPermissionListener {
fun onPermissionGranted()
fun onPermissionDenied()
}
}
寫的不好。大家自行修改吧。
Activity中的使用在onCreate中一開始調(diào)用一下代碼:
MangoPermissionUtil.requestPermissions(this@IndexActivity, Constant.PERMISSION_OPERATION_CODE_SCAN, arrayOf(Manifest.permission.CAMERA), false, object : MangoPermissionUtil.OnPermissionGrantedListener {
override fun onPermissionGranted() {
//在這表示用戶同意了權(quán)限申請。
//假如用戶拒絕了權(quán)限申請在這兒我是沒讓他進入到應用中的效果如下
}
})

只要有任何一個權(quán)限用戶沒通過都會彈出這個Dialog。直到用戶全部授權(quán)。。。。
點擊取消退出應用。確定按鈕去到設(shè)置界面為應用授權(quán)。。。。
下面是應用啟動的場景(很舒服2333)

還有個劇TM惡心的問題這些所有的邏輯在除了小米6.xxx的設(shè)備上跑是沒問題的。必須全部授權(quán)才能進入應用。但是小米6.xxx的設(shè)備上當我第一次拒絕了權(quán)限申請之后。第二次進入應用判斷權(quán)限的時候它竟然在checkPermisssion的方法中給我返回了PERMISSION_GRANTED這就比較尷尬了。這樣我是可以進入掉權(quán)限請求成功的回調(diào)。但是我進去之后確實沒權(quán)限啊。對應權(quán)限相關(guān)的操作一樣不能執(zhí)行。。。不得不說小米的6.xxx設(shè)備是真的坑。。。。
還有一點油條用的時候還要在當前申請的Activity中調(diào)用一下來執(zhí)行到油條中自定義的回調(diào)
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
MangoPermissionUtil.onRequestPermissionsResult(requestCode, permissions, grantResults)
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
最后
對于一些比較特別的權(quán)限,比如文件的讀寫權(quán)限,一般在我們第一次開啟APP的時候就要去獲取了,假設(shè)我們一開始沒有獲取到這個權(quán)限,那么如果我的首頁有輪播廣告圖,這個廣告圖是網(wǎng)絡獲取的,做了三級緩存,這樣就會到導致磁盤緩存無法寫入。這邊提供一個解決方法,就是在你引導APP啟動的時候,就引導用戶去獲取權(quán)限,當用戶拒絕的時候,應該給出彈出框并跳轉(zhuǎn)對應的應用權(quán)限管理界面(需要對不同機型進行設(shè)置)。
可以參考微信的做法:
啟動app,在閃屏頁的時候向用戶提出權(quán)限的申請
- 存儲空間權(quán)限,關(guān)閉微信
- 電話權(quán)限,關(guān)閉微信
- 位置權(quán)限,關(guān)閉微信
- 進入app:
- 發(fā)照片時,申請照片權(quán)限
- 發(fā)語音時,申請麥克風權(quán)限
- 用戶每次點擊拒絕,都彈出自定義對話框,提示用戶設(shè)置權(quán)限
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
- Android 如何實現(xiàn)動態(tài)申請權(quán)限
- Android 拍照選擇圖片并上傳功能的實現(xiàn)思路(包含權(quán)限動態(tài)獲取)
- 詳解Android開發(fā)錄音和播放音頻的步驟(動態(tài)獲取權(quán)限)
- Android 6.0動態(tài)權(quán)限及跳轉(zhuǎn)GPS設(shè)置界面的方法
- 安卓Android6.0權(quán)限動態(tài)獲取操作示例
- android6.0權(quán)限動態(tài)申請框架permissiondispatcher的方法
- Android 6.0動態(tài)權(quán)限申請教程
- Android6.0動態(tài)申請權(quán)限所遇到的問題小結(jié)
- Android中不支持動態(tài)申請權(quán)限的原因
- Android6.0獲取動態(tài)權(quán)限代碼示例
相關(guān)文章
flutter自定義InheritedProvider實現(xiàn)狀態(tài)管理詳解
這篇文章主要為大家介紹了flutter自定義InheritedProvider實現(xiàn)狀態(tài)管理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11
Android Studio查看Android 5.x源碼的步驟詳解
Google為Android開發(fā)者帶來Android Studio,用來取代Eclipse。從Android Studio出現(xiàn)起,整機開發(fā)和Android源碼閱讀和編輯一定能用上它。這篇文章小編就帶大家學習下如何使用Android Studio查看Android 5.x源碼,有需要的可以參考借鑒。2016-09-09
Flutter自定義下拉刷新時的loading樣式的方法詳解
Flutter中的下拉刷新,我們通常RefreshIndicator,可以通過color或strokeWidth設(shè)置下拉刷新的顏色粗細等樣式,但如果要自定義自己的widget,RefreshIndicator并沒有暴露出對應的屬性,那如何修改呢,文中給大家介紹的非常詳細,需要的朋友可以參考下2024-01-01
Android中實現(xiàn)Runnable接口簡單例子
這篇文章主要介紹了Android中實現(xiàn)Runnable接口簡單例子,著重點在如何實現(xiàn)run()方法,需要的朋友可以參考下2014-06-06
Androidstudio調(diào)用攝像頭拍照并保存照片
這篇文章主要為大家詳細介紹了Androidstudio調(diào)用攝像頭拍照并保存照片,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-03-03
基于flutter?sound插件實現(xiàn)錄音與播放功能
這篇文章主要介紹了基于flutter?sound插件實現(xiàn)錄音與播放功能,介紹了如何錄音,如何播放本地和遠程音頻文件,以及如何實現(xiàn)動畫,在錄制完音頻文件后如何上傳,這些都是我們平常使用這個功能會遇到的問題。在使用的過程中遇到的問題也有列出,需要的朋友可以參考下2022-05-05

