Android 6.0動態(tài)權限申請

一赔桌、概述
從Android 6.0(API 23)開始渴逻,系統(tǒng)權限做了很大的改變惨奕。在之前,6.0以下的權限及在安裝時梨撞,會根據(jù)權限聲明產(chǎn)生一個權限列表卧波,用戶只有在同意之后才能完成app的安裝。也就是說當我們想要使用某個app時港粱,就要默默忍受一些不必要的權限(比如訪問通訊錄和短信等)。而在Android 6.0(API 23)以后锈颗,我們可以直接安裝app击吱。app的權限授予是在app運行時授予的,而不是在app安裝時授予覆醇。這樣既簡化app安裝過程永脓,當app需要我們授予不恰當?shù)臋嘞薜臅r候,我們也可以選擇拒絕授予這些權限常摧。已授予過的權限落午,我們還可以去app設置頁面去關閉授權。這站在用戶的角度上來說提高了安全性溃斋,可以防止一些應用惡意訪問用戶數(shù)據(jù)梗劫。但是對于開發(fā)者來說,也增加了不少工作量蛉威。當app要訪問敏感用戶數(shù)據(jù)(如聯(lián)系人和短信)以及某些系統(tǒng)功能(如相機和互聯(lián)網(wǎng)的權限)就必須先動態(tài)申請權限猫妙。根據(jù)功能的不同聚凹,系統(tǒng)可能會自動授予權限,或者可能會提示用戶批準請求彼哼。
和往常一樣湘今,主要是想總結(jié)一下對Android 6.0動態(tài)權限申請的學習過程以及一些需要注意的地方。
詳細請查看谷歌官方文檔:https://developer.android.com/about/versions/marshmallow/android-6.0-changes#behavior-runtime-permissions

二拴签、權限和權限組
新的權限機制更好的保護了用戶的隱私蚓哩,Google將權限分為兩類:正常權限(Normal Permissions)和危險權限(Dangerous Permission)

2.1、正常權限
這類權限一般不涉及用戶隱私喜颁,是不需要用戶進行授權的曹阔,只要app在其清單中列出了正常權限,系統(tǒng)將自動授予該權限赃份,比如設置時區(qū)芥炭,手機震動和訪問網(wǎng)絡等。

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
INSTALL_SHORTCUT
INTERNET
KILL_BACKGROUND_PROCESSES
MODIFY_AUDIO_SETTINGS
NFC
READ_SYNC_SETTINGS
READ_SYNC_STATS
RECEIVE_BOOT_COMPLETED
REORDER_TASKS
REQUEST_INSTALL_PACKAGES
SET_ALARM
SET_TIME_ZONE
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
UNINSTALL_SHORTCUT
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS

2.2渺蒿、危險權限
這類權限一般是涉及到用戶隱私的彪薛,需要用戶進行授權善延,比如讀取sdcard、訪問通訊錄和短信等易遣。

group:android.permission-group.CONTACTS
  permission:android.permission.WRITE_CONTACTS
  permission:android.permission.GET_ACCOUNTS
  permission:android.permission.READ_CONTACTS

group:android.permission-group.PHONE
  permission:android.permission.READ_CALL_LOG
  permission:android.permission.READ_PHONE_STATE
  permission:android.permission.CALL_PHONE
  permission:android.permission.WRITE_CALL_LOG
  permission:android.permission.USE_SIP
  permission:android.permission.PROCESS_OUTGOING_CALLS
  permission:com.android.voicemail.permission.ADD_VOICEMAIL

group:android.permission-group.CALENDAR
  permission:android.permission.READ_CALENDAR
  permission:android.permission.WRITE_CALENDAR

group:android.permission-group.CAMERA
  permission:android.permission.CAMERA

group:android.permission-group.SENSORS
  permission:android.permission.BODY_SENSORS

group:android.permission-group.LOCATION
  permission:android.permission.ACCESS_FINE_LOCATION
  permission:android.permission.ACCESS_COARSE_LOCATION

group:android.permission-group.STORAGE
  permission:android.permission.READ_EXTERNAL_STORAGE
  permission:android.permission.WRITE_EXTERNAL_STORAGE

group:android.permission-group.MICROPHONE
  permission:android.permission.RECORD_AUDIO

group:android.permission-group.SMS
  permission:android.permission.READ_SMS
  permission:android.permission.RECEIVE_WAP_PUSH
  permission:android.permission.RECEIVE_MMS
  permission:android.permission.RECEIVE_SMS
  permission:android.permission.SEND_SMS
  permission:android.permission.READ_CELL_BROADCASTS

我們可以通過adb shell pm list permissions -d -g進行查看


在這里我們會發(fā)現(xiàn)危險權限都是分組存在的侨歉,其分組的權限機制為:當你申請某個危險的權限時揩魂,假設你的app早已被用戶授權了同一權限組中的某個危險權限時,那么系統(tǒng)會立即授權牵舵,不需要用戶再授權。比如你的app之前已經(jīng)請求并被授予了READ_CONTACTS權限担巩,然后當它請求WRITE_CONTACTS權限時没炒,系統(tǒng)會立即授予該權限,而不會向用戶顯示權限對話框祖很。

三漾脂、代碼的實現(xiàn)
按照慣例,我們先來看看效果圖


從效果圖中可以看到笨鸡,當頁面啟動時形耗,向用戶顯示要請求的權限對話框辙浑,分別是讀取外置存儲的權限、訪問攝像頭倦踢、讀取通訊錄和讀取短信侠草。當授權完成后,在onRequestPermissionsResult方法回調(diào)中晤碘,檢查如果沒有全部授予權限時功蜓,并提示目前缺少權限,要不要到其app設置頁面授權等腮介。在這里當app需要我們授予不恰當?shù)臋嘞薜臅r候端衰,我們也可以選擇拒絕授予這些權限。已授予過的權限灭抑,我們也可以到app設置頁面去關閉授權抵代。
好了,下面我們來看看具體的實現(xiàn)吧

首先案腺,必須在應用程序清單中通過 <uses-permission>來聲明所需的權限

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.READ_SMS" />

然后就是完整的代碼實現(xiàn)啦康吵,代碼都有注釋晦嵌,在這里就不一一解釋啦

package per.lijuan.permissiondome

import android.Manifest
import android.content.DialogInterface
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.provider.Settings
import android.support.v4.app.ActivityCompat
import android.support.v4.content.ContextCompat
import android.support.v7.app.AlertDialog
import android.support.v7.app.AppCompatActivity
import android.widget.Toast
import java.util.*

class MainActivity : AppCompatActivity() {
    companion object {
        private const val KEY_PERMISSIONS_REQUEST_COUNT = "KEY_PERMISSIONS_REQUEST_COUNT"
        private const val MAX_NUMBER_REQUEST_PERMISSIONS = 1 //請求權限的最多次數(shù)
        private const val REQUEST_CODE_PERMISSIONS = 101
    }

    private var mPermissionRequestCount: Int = 0

    // 應用程序需要的權限列表
    private val sPermissions = object : ArrayList<String>() {
        init {
            add(Manifest.permission.READ_EXTERNAL_STORAGE)//讀取外置存儲的權限
            add(Manifest.permission.CAMERA)//訪問攝像頭
            add(Manifest.permission.READ_CONTACTS)//讀取通訊錄
            add(Manifest.permission.READ_SMS)//讀取短信
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        if (savedInstanceState != null) {
            mPermissionRequestCount = savedInstanceState.getInt(KEY_PERMISSIONS_REQUEST_COUNT, 0)
        }
        requestPermissionsIfNecessary()
    }

    /**
     * 檢查是否授予權限
     * 如果全部獲取, 則直接過.
     * 如果權限缺失, 則提示Dialog
     *
     * @param requestCode  請求碼
     * @param permissions  權限
     * @param grantResults 結(jié)果
     */
    override fun onRequestPermissionsResult(
            requestCode: Int,
            permissions: Array<String>,
            grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == REQUEST_CODE_PERMISSIONS && !checkAllPermissions())
            showMissingPermissionDialog()
    }

    /**
     * 檢查目前應用是否有所有我們需要的權限
     * 如果全部獲取, 則直接過.
     * 如果權限缺失, 就判斷當前請求權限的次數(shù)是否已達到最大值旱函,如果沒有則再次請求權限描滔,否則就提示Dialog
     */
    private fun requestPermissionsIfNecessary() {
        if (!checkAllPermissions()) {
            if (mPermissionRequestCount < MAX_NUMBER_REQUEST_PERMISSIONS) {
                mPermissionRequestCount += 1
                ActivityCompat.requestPermissions(
                        this,
                        sPermissions.toTypedArray(),
                        REQUEST_CODE_PERMISSIONS)
            } else {
                Toast.makeText(this,"缺失權限",Toast.LENGTH_LONG).show()
            }
        }
    }

    /**
     * 檢查應用目前是否有這些權限
     */
    private fun checkAllPermissions(): Boolean {
        var hasPermissions = true
        for (permission in sPermissions) {
            hasPermissions = hasPermissions and (ContextCompat.checkSelfPermission(
                    this, permission) == PackageManager.PERMISSION_GRANTED)
        }
        return hasPermissions
    }

    /**
     * 顯示缺失權限提示
     */
    private fun showMissingPermissionDialog() {
        val builder = AlertDialog.Builder(this)
        builder.setTitle("幫助")
        builder.setMessage("當前應用缺少必要權限含长。\n \n 請點擊 \"設置\"-\"權限\"-打開所需權限。")
        builder.setNegativeButton("取消", DialogInterface.OnClickListener(){ _, _ ->
        })
        builder.setPositiveButton("設置", DialogInterface.OnClickListener { _, _ ->
            // 跳轉(zhuǎn)到當前應用對應的設置頁面
            val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
            intent.data = Uri.parse("package:" + this.packageName)
            startActivity(intent)
        })
        builder.setCancelable(false)
        builder.show()
    }
}

這代碼還有待進一步完善颅眶,因為在的權限申請中田弥,我們看到它依賴于Activity或者Fragment的回調(diào)方法。但我覺得代碼有待封裝商叹,可以采用單獨的Activity來申請權限只泼,通過回調(diào)的方式通知業(yè)務層,這樣會方便點弥咪。

本篇文章大概就這么多,有什么疑問的酷勺,請在下面留言扳躬,有不足之處還望指導,感謝各位_

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末击胜,一起剝皮案震驚了整個濱河市潜的,隨后出現(xiàn)的幾起案子字管,更是在濱河造成了極大的恐慌,老刑警劉巖嘲叔,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件硫戈,死亡現(xiàn)場離奇詭異,居然都是意外死亡汁胆,警方通過查閱死者的電腦和手機霜幼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門罪既,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人丢间,你說我怎么就攤上這事驹针。” “怎么了饮六?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我撤防,道長寄月,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任厂抖,我火速辦了婚禮克懊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘墙懂。我一直安慰自己扮念,他們只是感情好,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著弄匕,像睡著了一般颅悉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上迁匠,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天签舞,我揣著相機與錄音,去河邊找鬼柒瓣。 笑死儒搭,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的芙贫。 我是一名探鬼主播搂鲫,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼磺平!你這毒婦竟也來了魂仍?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤擦酌,失蹤者是張志新(化名)和其女友劉穎俱诸,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赊舶,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡睁搭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了笼平。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片园骆。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖寓调,靈堂內(nèi)的尸體忽然破棺而出锌唾,到底是詐尸還是另有隱情,我是刑警寧澤夺英,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布晌涕,位于F島的核電站,受9級特大地震影響痛悯,放射性物質(zhì)發(fā)生泄漏渐排。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一灸蟆、第九天 我趴在偏房一處隱蔽的房頂上張望驯耻。 院中可真熱鬧,春花似錦炒考、人聲如沸可缚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽帘靡。三九已至,卻和暖如春瓤帚,著一層夾襖步出監(jiān)牢的瞬間描姚,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工戈次, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留轩勘,地道東北人。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓怯邪,卻偏偏與公主長得像绊寻,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內(nèi)容