的權限管理遵循的是“最小特權原則”,即所有的應用程序都被賦予了最小權限。一個應用程序如果沒有聲明任何權限,就沒有任何特權。
權限的歷史
2013年你需要權限來執行此操作,蘋果公司發布IOS7系統。其中一項令開發者頭疼的修改點:隱私中增加相冊、錄音等權限,App如需使用相應權限,需要申請并由用戶同意(IOS7以前,可以直接訪問相冊),針對此點,很多App在首次啟動時一通彈窗,申請各式各樣的權限。
后來蘋果為改善用戶體驗,在App Store審核時要求App必須在使用前一刻才能申請權限,有效改善了此類問題。比如一款直播App,當你啟動App時并不需要相機、錄音權限,等到你開播時才需要申請這兩個權限。這一場景,其實就類似今天要提到的動態授權。
早期:無遮攔
.0系統以前,在安裝App前,會羅列出App申請的所有權限。如果繼續安裝,視為用戶同意賦予App所需權限。這種機制是無遮攔的,在嘗試安裝App時,彈窗羅列了App申請的全部權限。只能對所需權限進行查看,無法拒絕授權,可選擇取消安裝或繼續安裝。
這種方式,對于開發者極為友好,僅需在中配置App所需權限即可,代碼就可以直接調用了。但是對于用戶來說,這種方法存在極大的安全隱患。
發展:第三方安全App
為解決部分敏感權限被不合理使用,國內部分公司的安全類App,開始監控應用獲取手機敏感權限并做出提示。如360手機衛士、騰訊手機管家等產品,當監測到有App嘗試使用短信權限、定位等敏感權限,會告知用戶,并可以拒絕賦予權限。剛開始,還比較順利。但隨著手機廠商逐漸開始修改ROM,第三方安全App的兼容、性能問題逐步爆發。
升級:廠商行動
再稍后一些,手機廠商開始行動,紛紛將第三方軟件的權限提示功能直接做入ROM。并把安全作為產品的賣點進行打造。
目前:谷歌升級權限管理
2015年推出的 6.0,加入了危險權限管理。因手機廠商對ROM的修改,部分6.0以上機器并不支持此項特性。到了此階段,App需要在對權限代碼進行修改后,才能正常使用對應權限。簡單理解為3步:
權限的使用和適配
零、權限的基礎知識
系統基于Linux內核,系統中的權限分為3類:
一、不適用動態權限
動態權限特性,僅從 6.0開始擁有你需要權限來執行此操作,所以,可以簡單粗暴的通過不提升(
如果不改變任何代碼,直接將提升到26,然后運行App,做同樣操作時會發生異常甚至崩潰,產生這個崩潰的原因,是在 6.0及以上,未獲取權限的情況下直接執行了需要權限的操作。
二、動態權限適配1、在使用前權限前,檢測權限
首先,我們需要判斷自己是否擁有權限。判斷時間點為執行需要權限的對應操作前。如我們在獲取IMEI前,需要判斷是否擁有權限。
我們可以調用.()方法檢測授權狀態,返回的結果為中的兩個常量:(已授權)和(未授權)。
2、已授權的情況下,執行相應的操作
當已授權時,就可以執行原有的操作了。代碼如下:
// 檢測PHONE_STATE 如果已授權
if (ContextCompat.checkSelfPermission(this,Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
//做你想做的
}
3、未授權時,申請權限
如果App未獲得授權,我們就需要向用戶申請授權。可以調用()方法來請求授權。代碼如下:
// 檢測PHONE_STATE 如果未授權
if (ContextCompat.checkSelfPermission(this,Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
//申請權限
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_PHONE_STATE), PERMISSIONS_REQUEST_PHONE_STATE)
}
()中的第三個參數是一個int型請求碼,方便回調處理。
調用申請授權方法后,ROM會調起一個系統級彈窗,這個開發者無法定制。當用戶點擊同意后,系統會記錄,下次再判斷權限時就會返回已授權狀態;當App卸載時,記錄會被清除。
4、重寫函數,處理授權彈窗的結果
直接在或中重寫()函數,來處理權限申請結果。()的第三個參數,將在這里被用到。代碼如下:
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == PERMISSIONS_REQUEST_READ_CONTACTS) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//todo
} else {
//todo
}
}
}