詳解android6.0權(quán)限

這是一篇遲到的文章。在6.0之前,在應(yīng)用安裝的時候,提示用戶所需要用到的權(quán)限列表,同意之后安裝,該app就被賦予所有的權(quán)限,我們暫且稱它為安裝時權(quán)限,安裝后,被賦予的權(quán)限也無法取消,當(dāng)然,國內(nèi)的一些rom會在系統(tǒng)級別進(jìn)行額外的一層權(quán)限管理,這個不在本文敘述范圍之內(nèi);在6.0之后,google對權(quán)限進(jìn)行了運行時的管理,而不是在安裝時候,危險權(quán)限需要在運行時申請,我們暫且稱它為運行時權(quán)限,非危險權(quán)限,在安裝時由用戶授予,這樣簡化了應(yīng)用安裝過程,因為用戶在安裝或更新應(yīng)用時不需要授予權(quán)限,也給予了用戶對app功能更多的控制

權(quán)限分組

系統(tǒng)權(quán)限主要分為兩類,正常權(quán)限危險權(quán)限

正常權(quán)限不會直接危及用戶的隱私,如果你的應(yīng)用在它的Manifest中列出了正常權(quán)限,系統(tǒng)會自動授予權(quán)限

危險權(quán)限可以讓app訪問用戶的機(jī)密數(shù)據(jù),如果你的應(yīng)用在它的Manifest列出了危險權(quán)限,用戶必須明確批準(zhǔn)你的app使用該權(quán)限

當(dāng)然,不管哪個版本的android,你應(yīng)用中所用到的所有權(quán)限,不管是正常權(quán)限還是危險權(quán)限,都需要在應(yīng)用的Manifest中申明

如果你的設(shè)備運行Android 5.1以及5.1以下版本,或者你的應(yīng)用的目標(biāo)SDK是22以及22以下版本:如果你在應(yīng)用的Manifest中申明了危險權(quán)限,用戶在安裝時必須授予權(quán)限,如果拒絕授予權(quán)限,那么系統(tǒng)就不會安裝應(yīng)用,也就是所謂的“一刀切”方式,不同意所有權(quán)限,就不能安裝應(yīng)用

如果你的設(shè)備運行Android 6.0以及6.0以上版本,或者你的目標(biāo)SDK是23以及23以上版本:應(yīng)用必須在Manifest中羅列出所有的權(quán)限,并且在程序運行時,它必須請求用戶授予每一個危險權(quán)限,此時用戶可以授予或者拒絕每一個權(quán)限,并且應(yīng)用程序可以繼續(xù)運行有限的功能,即使用戶拒絕了權(quán)限請求

注意:從Android 6.0開始(API 23),用戶可以在任何時候,對任何應(yīng)用撤銷權(quán)限,即使app申明的目標(biāo)SDK低于23

正常權(quán)限

正常權(quán)限有以下幾個特點

  1. 就是在安裝時,由用戶授予,后續(xù)運行時無需再次申請,無需再顯示提醒用戶,用戶也不能取消這些權(quán)限(在Android 6.0及以上版本例外,用戶可以通過管理界面撤銷權(quán)限)

  2. 對用戶隱私或安全沒有較大影響

正常權(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

  • INSTALL_SHORTCUT

  • INTERNET

  • KILL_BACKGROUND_PROCESSES

  • MODIFY_AUDIO_SETTINGS

  • NFC

  • READ_SYNC_SETTINGS

  • READ_SYNC_STATS

  • RECEIVE_BOOT_COMPLETED

  • REORDER_TASKS

  • REQUEST_IGNORE_BATTERY_OPTIMIZATIONS

  • 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

我們大概看一遍過去,可以總結(jié)歸納出正常權(quán)限有幾類:藍(lán)牙,網(wǎng)絡(luò)狀態(tài),NFC,指紋,鬧鈴,快捷方式,震動等常用權(quán)限

危險權(quán)限

危險權(quán)限才是運行時權(quán)限的主要處理對象,因為這些權(quán)限可能引起隱私wenti或者影響其他程序運行,android中危險權(quán)限主要以組的形式出現(xiàn),可以歸納為一下幾個分組:

  • CALENDAR

  • CAMERA

  • CONTACTS

  • LOCATION

  • MICROPHONE

  • PHONE

  • SENSORS

  • SMS

  • STORAGE

具體請看下圖:

android_permissions_group.jpg

那么問題來了

Q1.我們的應(yīng)用是否必須支持運行時權(quán)限?

Android為了應(yīng)用兼容,實際上是可以不需要支持運行時權(quán)限的,只要設(shè)置targetSdkVersion低于23就可以了,意思是我的應(yīng)用還乜有在API 23上面完全兼容,不要給我開啟運行時權(quán)限新特性,當(dāng)然了,早晚你還是要支持的,時間問題而已

Q2.如果不支持運行時權(quán)限,應(yīng)用會崩潰么?

可能會奔潰,具體要根據(jù)你使用運行時權(quán)限在代碼的什么地方,假設(shè)你應(yīng)用啟動就開始創(chuàng)建SDCard文件夾,但是在Android 6.0的設(shè)備上,系統(tǒng)或者用戶在系統(tǒng)權(quán)限管理界面禁止了你的SDCard權(quán)限,如果應(yīng)用代碼處理不當(dāng),此時重新啟動應(yīng)用就會發(fā)生奔潰

這里我們還要注意的一點是,危險權(quán)限是以組的方式授予的,怎么理解呢?按照我們上面列出的9大分組,舉個栗子,如果你申請讀取SDCard權(quán)限,在用戶授予權(quán)限后,你自動就獲得了寫入SDCard的權(quán)限,也就是說你獲得了STORAGE分組的所有權(quán)限

運行時權(quán)限申請

API使用

申請運行時權(quán)限主要使用到的API有下面三個:


# 檢測系統(tǒng)當(dāng)前是否被授予某個運行時權(quán)限,傳入?yún)?shù)是權(quán)限名稱,比如Manifest.permission.READ_EXTERNAL_STORAGE

ContextCompat.checkSelfPermission(@NonNull Context context, @NonNull String permission)

# 是否要顯示權(quán)限說明

# 1.用戶第一次被拒絕(非永久拒絕)授予某個權(quán)限后,下次再次請求該權(quán)限,這個方法會返回true,用戶有機(jī)會以某種方式對用戶進(jìn)行說明該權(quán)限用處

# 2.用戶在第一次拒絕某個權(quán)限后,下次再次申請時,授權(quán)的dialog中將會出現(xiàn)“不再提醒”選項,一旦選中勾選了,那么下次申請將不會提示用戶。

# 3.第二次請求權(quán)限時,用戶拒絕了,并選擇了“不再提醒”的選項,調(diào)用shouldShowRequestPermissionRationale()后返回false。

# 4.設(shè)備的策略禁止當(dāng)前應(yīng)用獲取這個權(quán)限的授權(quán):shouldShowRequestPermissionRationale()返回false 。

# 5.加這個提醒的好處在于,用戶拒絕過一次權(quán)限后我們再次申請時可以提醒該權(quán)限的重要性,免得再次申請時用戶勾選“不再提醒”并決絕,導(dǎo)致下次申請權(quán)限直接失敗。

ActivityCompat.shouldShowRequestPermissionRationale(@NonNull Activity activity,@NonNull String permission)

# 請求權(quán)限授予,當(dāng)然可以傳入多個權(quán)限名稱同時申請,用戶會依次彈出多個提示框申請權(quán)限,App不能配置和修改這個對話框,如果需要提示用戶這個權(quán)限相關(guān)的信息或說明,需要在調(diào)用 requestPermissions() 之前處理,該方法有兩個參數(shù):

# int requestCode,會在回調(diào)onRequestPermissionsResult()時返回,用來判斷是哪個授權(quán)申請的回調(diào)。

# String[] permissions,權(quán)限數(shù)組,你需要申請的的權(quán)限的數(shù)組。

# 由于該方法是異步的,所以無返回值,當(dāng)用戶處理完授權(quán)操作時,會回調(diào)Activity或者Fragment的onRequestPermissionsResult()方法。

ActivityCompat.requestPermissions(final @NonNull Activity activity,final @NonNull String[] permissions, final @IntRange(from = 0) int requestCode)

# 用戶拒絕或者授予用戶權(quán)限后的回調(diào)方法,該方法在Activity/Fragment中應(yīng)該被重寫,當(dāng)用戶處理完授權(quán)操作時,系統(tǒng)會自動回調(diào)該方法,該方法有三個參數(shù):

# int requestCode,在調(diào)用requestPermissions()時的第一個參數(shù)。

# String[] permissions,權(quán)限數(shù)組,在調(diào)用requestPermissions()時的第二個參數(shù)。

# int[] grantResults,授權(quán)結(jié)果數(shù)組,對應(yīng)permissions,具體值和上方提到的PackageManager中的兩個常量做比較。

onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)

我們以請求讀寫SDCard權(quán)限,來看一下一個標(biāo)準(zhǔn)的請求權(quán)限流程


# 判斷當(dāng)前系統(tǒng)版本是都是6.0以上

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

# 檢測是否已經(jīng)被用戶授予該權(quán)限

if (ContextCompat.checkSelfPermission(LaunchActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) !=           PackageManager.PERMISSION_GRANTED) {

# 沒有被授予權(quán)限

# 判斷如果已經(jīng)被用戶拒絕過一次(非永久拒絕),則顯示用戶此權(quán)限的一些說明,這里可以用toast,當(dāng)然也可以用自定義界面來顯示給用戶,如果是采用自定義界面來顯示給用戶,在有“確定”,“取消”等按鈕的情況下,下面的“請求權(quán)限”步驟要酌情調(diào)用

if (ActivityCompat.shouldShowRequestPermissionRationale(LaunchActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE)) {

Toast.makeText(LaunchActivity.this, "request read external storage", Toast.LENGTH_LONG).show();

}

# 請求權(quán)限,數(shù)組傳入多個值,可以一次請求多個權(quán)限

ActivityCompat.requestPermissions(LaunchActivity.this,

new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},

PERMISSIONS_REQUEST_EXTERNAL_STORAGE);

}

} else {

# 6.0以下版本,直接使用權(quán)限

// 做一些權(quán)限對應(yīng)的操作

}

如果是在6.0及6.0以上版本時,執(zhí)行 ActivityCompat.requestPermissions()方法請求權(quán)限后,如果用戶同意或者拒絕后,會回調(diào)onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)方法,該方法的幾個參數(shù),我們做一下解釋:

  1. requestCode 對應(yīng)的是ActivityCompat.requestPermissions請求時的requestCode,這個與Activity中常用的onActivityResult方法中的requestCode類似,用來標(biāo)識某一次或者某一個請求的回調(diào)對應(yīng)關(guān)系

  2. permissions 對應(yīng)ActivityCompat.requestPermissions()方法中請求的權(quán)限列表

  3. grantResults 對應(yīng)2中permissions的每個權(quán)限的用戶應(yīng)答結(jié)果

好了,接下來就是根據(jù)requestCode,遍歷permissions和grantResults中的結(jié)果,做對應(yīng)的操作


@Override

public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {

super.onRequestPermissionsResult(requestCode, permissions, grantResults);

switch (requestCode) {

case PERMISSIONS_REQUEST_EXTERNAL_STORAGE: {

// If request is cancelled, the result arrays are empty.

if (grantResults.length > 0

&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {

// permission was granted, yay! Do the

// contacts-related task you need to do.

} else {

// permission denied, boo! Disable the

// functionality that depends on this permission.

}

return;

}

}

}

注意事項

API問題

由于checkSelfPermission和requestPermissions從API 23才加入,低于23版本,需要在運行時判斷 或者使用Support Library v4中提供的方法

  • ContextCompat.checkSelfPermission

  • ActivityCompat.requestPermissions

  • ActivityCompat.shouldShowRequestPermissionRationale

兩個權(quán)限

運行時權(quán)限對于應(yīng)用影響比較大的權(quán)限有兩個,他們分別是

  • READ_PHONE_STATE

  • WRITE_EXTERNAL_STORAGE/READ_EXTERNAL_STORAGE

其中READ_PHONE_STATE用來獲取deviceID,即IMEI號碼。這是很多統(tǒng)計依賴計算設(shè)備唯一ID的參考。如果新的權(quán)限導(dǎo)致讀取不到,避免導(dǎo)致統(tǒng)計的異常。建議在完全支持運行時權(quán)限之前,將對應(yīng)的值寫入到App本地數(shù)據(jù)中,對于新安裝的,可以采取其他策略減少對統(tǒng)計的影響。

WRITE_EXTERNAL_STORAGE/READ_EXTERNAL_STORAGE這兩個權(quán)限和外置存儲(即sdcard)有關(guān),對于下載相關(guān)的應(yīng)用這一點還是比較重要的,我們應(yīng)該盡可能的說明和引導(dǎo)用戶授予該權(quán)限。

建議

  • 不要使用多余的權(quán)限,新增權(quán)限時要慎重

  • 使用Intent來替代某些權(quán)限,如撥打電話,選擇圖片(和你的產(chǎn)品經(jīng)理PK去吧)

  • 對于使用權(quán)限獲取的某些值,比如deviceId,盡量本地存儲,下次訪問直接使用本地的數(shù)據(jù)值

注意,由于用戶可以撤銷某些權(quán)限,所以不要使用應(yīng)用本地的標(biāo)志位來記錄是否獲取到某權(quán)限,在使用的時候要遵循流程實時判斷后使用,避免不正確的使用導(dǎo)致應(yīng)用崩潰

注意

  • 即使支持了運行時權(quán)限,也要在Manifest聲明,因為市場應(yīng)用會根據(jù)這個信息和硬件設(shè)備進(jìn)行匹配,決定你的應(yīng)用是否在該設(shè)備上顯示。

  • 防止一次請求太多的權(quán)限或請求次數(shù)太多,用戶可能對你的應(yīng)用感到厭煩,在應(yīng)用啟動的時候,最好先請求應(yīng)用必須的一些權(quán)限,非必須權(quán)限在使用的時候才請求,建議整理并按照上述分類管理自己的權(quán)限

  • 權(quán)限申請彈出的對話框不能自定義

  • 解釋你的應(yīng)用為什么需要這些權(quán)限:在你調(diào)用requestPermissions()之前,你為什么需要這個權(quán)限

  • 個人覺得Marshmallow的運行時權(quán)限對于用戶來說絕對是一個好東西,但是目前想要支持需要做的事情還是比較多的。

  • 對于一個有很多依賴的宿主應(yīng)用,想要做到支持還是有一些工作量的,因為你的權(quán)限申請受制于依賴。

最后,通過以上使用例子的代碼我們看到,雖然原理和內(nèi)容很簡單,但是流程上我們還是要寫很多代碼,判斷幾個條件,稍后我將在另外一片文章中根據(jù)請求運行時權(quán)限的特點,封裝出一個庫EPermission ,建議大家使用這個庫,簡化權(quán)限申請流程

附錄

權(quán)限結(jié)果常量

PackageManager.PERMISSION_DENIED:該權(quán)限是被拒絕的。

PackageManager.PERMISSION_GRANTED:該權(quán)限是被授權(quán)的。

權(quán)限名稱

Manifest.permission.READ_EXTERNAL_STORAGE

Manifest.permission.CAMERA

等等Manifest.permission類中對應(yīng)的常量

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容