Android 開發者必知必會的權限管理知識

本文來自於騰訊Bugly公衆號(weixinBugly),未經做者贊成,請勿轉載,原文地址:https://mp.weixin.qq.com/s/OQRHEufCUXBA3d3DMZXMKQhtml

導語

本文主要講解了Android 權限管理方面幾個點:java

  1. Android 權限背景知識;
  2. 權限檢查及權限兼容;
  3. 跳轉到app管理權限頁面

1、Android 權限背景知識

提到Android 權限管理,業內人士都知道Google 在Android 6.0時提出了運行時權限管理機制,在Android 6.0以前,所申請的權限只須要在AndroidManifest.xml列舉就能夠,從而容易致使一些安全隱患,所以,在Android 6.0 時,Google 爲了更好的保護用戶隱私提出了新的權限管理機制(官網 :Working with System Permissions),同時將其分爲兩大類:android

(1)Normal Permissionsgit

Normal Permissions 通常不涉及用戶隱私,是不須要用戶進行受權的,好比手機震動、訪問網絡等;github

(2)Dangerous Permissionshell

Dangerous Permission通常是涉及到用戶隱私的,須要用戶進行受權(動態申請),好比讀取SIM卡狀態、訪問通信錄、SD卡讀寫等。api

經過 adb shell pm list permissions -d -g 能夠查看 Dangerous Permission (以權限組形式)安全

Dangerous Permission group微信

如上圖所示 :Dangerous Permission 通常以 Permission group 形式存在,只要 Permission group中某一個 permission 被Granted,則整個Permission group下的權限均被Granted (目前是這樣,之後規則說不定會變)。網絡

2、權限檢查及權限兼容

本節主要介紹介紹如何進行權限檢查及權限兼容,主要分爲如下幾類:

(1)targetSdkVersion>=23,終端設備是6.0(api 23)以上系統;

安裝的時候不會得到權限,在運行時向用戶申請對應權限。這部分權限檢查比較簡單,不涉及權限兼容,使用官方方案就能夠 ,使用 Context::checkSelfPermisson ,建議使用ContextCompat::checkSelfPermisson檢查權限 便可 ,通常檢查流程 以下:

  1. 判斷是否有對應權限
    (ContextCompat::checkSelfPermisson)

  2. 判斷是否須要解釋對應權限用途(ActivityCompat::shouldShowRequestPermissionRationale)
    若是須要解釋,則現實自定義權限界面便可

  3. 不須要解釋的話,直接請求對應權限
    (ActivityCompat::requestPermissions)

上述狀況較爲簡單,在此再也不贅述。

(2)targetSdkVersion<23,終端設備是6.0(api 23)以上系統;

使用的是老的權限機制,在app 安裝時會詢問AndroidManifest.xml文件中的權限,可是用戶能夠在設置列表中關閉相關權限,這種狀況可能會對app正常運行形成必定影響。

(3) 終端設備系統小於6.0(api 23)

你們可能要問,終端設備系統小於6.0狀況還須要考慮嗎,確定是用的老的權限管理機制,在app 安裝時會詢問AndroidManifest.xml文件中的權限,用戶關閉不了,真的是這樣嗎 ?

答案是否認的,在實測中發現,目前有很多國產Rom 手機在6.0以前就有關閉權限的開關。這種狀況也是咱們兼容的對象。

下面將會以本身開發過程當中遇到的問題進行展開 ,目前企鵝FM支持免流了,須要使用READ_PHONE_STATE權限 (讀取SIM卡狀態),因爲以前未對改權限是否關閉沒有進行相關判斷,所以收到了不少例由於上述權限關閉,致使免流失敗的狀況。

適配過程以下 :

(1)使用 try catch 來檢查權限是否關閉

想法很簡單,若是改權限被用戶禁止了,那確定會異常,所以能夠在catch 中作文章,結果發現這一招根本沒有用,爲啥了 ?由於使用 READ_PHONE_STATE 權限的方法內部已經try catch ,外面沒法捕獲,所以該方法失效。

(2)ContextCompat::checkSelfPermisson

既然在6.0 可使用Context::checkSelfPermisson進行權限檢查,那可否使用support v4 中的ContextCompat::checkSelfPermisson 方法了,試一下,發如今api 23 如下失效,爲了探究緣由,查看了ActivityCompat::requestPermissons 內部實現,以下

ActivityCompat::requestPermissons

內部權限檢查方法在api 23 如下,使用的是 PackageManager::checkPermission,再去查看PackageManager::checkPermission方法,以下:發現只要權限在AndroidManifest.xml中註冊過,均會認爲該權限granted ,所以上述方法在api 23 如下也失效。

PackageManager::checkPermission

查閱相關資料和請教組內同事,發現Support V4 下面有一個專門檢查權限的工具類PermissionChecker。

(3)PermissionChecker

查看PermissionChecker源碼發現 ,PermissionChecker內部實際上使用的是AppOpsManagerCompt,而AppOpsManager是在api 19 加入進入的(AppOpsManager後面介紹

PermissionChecker::checkPermission

進而查看AppOpsManagerCompat 內部實現

AppOpsManagerCompat::permissionToOp

IMPL實現以下:

IMPL實現

從上圖能夠看出:在api 23如下, AppOpsManagerImpl::permissionToOp 直接返回爲null ,這直接致使api 23如下權限檢查將會返回 granted ,所以,該方法在api 23 下,權限檢查方法也會失效。

(4)AppOpsManager

API 19以上 ,Google 官方提供了 AppOpsManager 類來檢查權限,看到這個api 時,腦海浮現出 「天無絕人之路啊」,裏面有兩個比較重要的方法 :AppOpsManager::checkOp(int op ,int uid ,String packageName) (hide方法)和AppOpsManager::checkOp(String op,int uid ,String packageName)(public 方法 ,api 23 以上可用),不經思考,直接寫出了以下兩個方法

1)AppOpsManager::checkOp(int op ,int uid ,String packageName)

須要使用反射 :

AppOpsManager::checkOp(int op ,int uid ,String packageName)

2)AppOpsManager::checkOp(String op,int uid ,String packageName)

API >= 23 纔可使用 :

AppOpsManager::checkOp(String op,int uid ,String packageName)

在實測中發現,api 低於23時 ,OP_READ_PHONE_STATE =51 找不到,致使反射失敗。

仔細察看了一下 6.0 (API 23 )_NUM_OP = 62,以下,爲什麼找不到51 了

6.0 (API 23 )_NUM_OP = 62

難不成 每一個版本還不同,查看其餘版本,驗證了這個想法:

**5.1.1 (API 22 )_NUM_OP = 48**

5.1.1

Op個數限制 (須要找到對應源碼說明**)詳情 參看 :

查看Android 對應版本 _NUM_OP (目前最高支持5.1.1版本)

此時,OP_READ_PHONE_STATE = 51 在6.0(API 23)如下,經過反射是找不到的,所以對於READ_PHONE_STATE權限檢查僅限於6.0及6.0以上。

同時也仔細看了一下AppOpsManager 類介紹,並非爲開發者設計的,不過其餘權限兼容可使用這種方法,前提是 要看OP_*是在什麼版本纔有的,需作兼容方案 。

public class AppOpsManager

extends Object

API for interacting with "application operation" tracking.

This API is not generally intended for third party application developers; most features are only available to system applications. Obtain an instance of it through Context.getSystemService with Context.APP_OPS_SERVICE.

(5)最後查看了幾個第三方權限庫(暫未看完)

  1. PermissionsDispatcher

  2. AndPermission

3、跳轉到app管理權限頁面

既然在這裏講解跳轉到app 管理權限頁面的方法,可想而之,事情絕對不太簡單。Android 碎片化不只在存在於ui適配 ,一樣也存在於這裏,致使咱們沒法使用同一種方式跳轉到app管理權限頁面(適配,Android 開發永遠的痛)。

那有沒有辦法能夠簡化適配工做,減小開發量,方法固然有,不過須要咱們本身去總結和探索的,目前已有方法:

(1)直接跳轉到系統設置頁

Intent intent =newIntent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.fromParts("package",getPackageName(), null));

try{
    startActivity(intent);
}catch(Exception exception) {
    exception.printStackTrace();
}

記得要添加上 try catch ,不加可能會crash。這種方式就不須要適配各個廠商的不一樣版本rom,缺點是,用戶只能跳轉到系統設置頁,而後去找對應app 的權限管理(總會有一些用戶找不到

(2)站在前人的肩上

引用前人經驗:Android各大手機品牌手機跳轉到權限管理界面 (未一一驗證,畢竟沒那麼多手機)

那是否是前人經驗必定對了,那就不必定了,在當時多是對的,在如今可能就行不通了,如今以MIUI跳轉到app 權限管理頁面爲例進行說明。

1)MIUI 6/7

Intent localIntent = new Intent("miui.intent.action.APP_PERM_EDITOR");
localIntent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity");
localIntent.putExtra("extra_pkgname", context.getPackageName());

try{
    startActivity(intent);
}catch(Exception exception) {
    exception.printStackTrace();
}

2)MIUI 8

Intent localIntent = new Intent("miui.intent.action.APP_PERM_EDITOR");
localIntent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.PermissionsEditorActivity");
localIntent.putExtra("extra_pkgname", context.getPackageName());

try{
    startActivity(intent);
}catch(Exception exception) {
    exception.printStackTrace();
}

對比1)和2)發現,在MIUI 6/7 和MIUI 8 上面,權限管理頁面的activity名字不同了,所以使用MIUI6/7的方法在MIUI8上就會失效,若是沒有加上try catch ,就會直接crash。

對於上述變化,做爲一個開發者通常都是不知道的,即使經過反饋發現了這個問題,也有可能不知道對應的activity是什麼,此刻要麼搜索網上有沒有相似解決方案,要麼求助於對應rom 開發廠商的開發者論壇 (有時解決迴應速度至關慢),那有沒有更好的辦法了,方法詳見(3)部分。

(3)查看某個ROM的某個版本的權限管理頁面的activity

這裏以華爲p8爲例簡要說明,詳細步驟以下:

1)經過設置找到對應app的權限管理頁面,以下:

企鵝fm在華爲p8上的權限管理頁面

2)找到對應頁面的activity

方法一:經過add 工具查看棧頂Activity

adb shell dumpsys activity | grep "mFocusedActivity"

企鵝fm在華爲p8上的權限管理頁面對應的activity

更爲詳細的堆棧信息

詳細的堆棧信息

方法二:使用Activity Tracer工具

使用方法:可參見我以前的文章 :Android開發—— 小工具,大效率

使用Activity Tracer查看權限管理頁面對應的activity

文中引用連接:

  1. Working with System Permissions
    https://developer.android.com/training/permissions/index.html
  2. 查看Android 對應版本 _NUM_OP
    http://grepcode.com/search?query=android.app.AppOpsManager&n=
  3. PermissionsDispatcher
    https://github.com/hotchemi/PermissionsDispatcher
  4. AndPermission
    https://github.com/yanzhenjie/AndPermission
  5. Android各大手機品牌手機跳轉到權限管理界面
    http://www.jianshu.com/p/b5c494dba0bc
  6. Android開發—— 小工具,大效率
    http://www.jianshu.com/p/672d64fdc486

更多精彩內容歡迎關注騰訊 Bugly的微信公衆帳號:

騰訊 Bugly是一款專爲移動開發者打造的質量監控工具,幫助開發者快速,便捷的定位線上應用崩潰的狀況以及解決方案。智能合併功能幫助開發同窗把天天上報的數千條 Crash 根據根因合併分類,每日日報會列出影響用戶數最多的崩潰,精準定位功能幫助開發同窗定位到出問題的代碼行,實時上報能夠在發佈後快速的瞭解應用的質量狀況,適配最新的 iOS, Android 官方操做系統,鵝廠的工程師都在使用,快來加入咱們吧!

相關文章
相關標籤/搜索