導(dǎo)語(yǔ)
感覺分析的挺全面手幢,故轉(zhuǎn)載歌溉,有小細(xì)節(jié)刪改,原文請(qǐng)點(diǎn)擊:
原文在這里
本文主要講解了Android 權(quán)限管理方面幾個(gè)點(diǎn):
- Android 權(quán)限背景知識(shí)瓶颠;
- 權(quán)限檢查及權(quán)限兼容拟赊;
- 跳轉(zhuǎn)到app管理權(quán)限頁(yè)面
一、Android 權(quán)限背景知識(shí)
提到Android 權(quán)限管理粹淋,業(yè)內(nèi)人士都知道Google 在Android 6.0時(shí)提出了運(yùn)行時(shí)權(quán)限管理機(jī)制吸祟,在Android 6.0之前,所申請(qǐng)的權(quán)限只需要在AndroidManifest.xml列舉就可以桃移,從而容易導(dǎo)致一些安全隱患屋匕,因此,在Android 6.0 時(shí)谴轮,Google 為了更好的保護(hù)用戶隱私提出了新的權(quán)限管理機(jī)制(官網(wǎng) :Working with System Permissions)炒瘟,同時(shí)將其分為兩大類:
(1)Normal Permissions
Normal Permissions 一般不涉及用戶隱私吹埠,是不需要用戶進(jìn)行授權(quán)的第步,比如手機(jī)震動(dòng)、訪問網(wǎng)絡(luò)等缘琅;
(2)Dangerous Permission
Dangerous Permission一般是涉及到用戶隱私的粘都,需要用戶進(jìn)行授權(quán)(動(dòng)態(tài)申請(qǐng)),比如讀取SIM卡狀態(tài)刷袍、訪問通訊錄翩隧、SD卡讀寫等。
通過 adb shell pm list permissions -d -g 可以查看 Dangerous Permission (以權(quán)限組形式)
如上圖所示 :Dangerous Permission 一般以 Permission group 形式存在呻纹,只要 Permission group中某一個(gè) permission 被Granted堆生,則整個(gè)Permission group下的權(quán)限均被Granted (目前是這樣,以后規(guī)則說不定會(huì)變)雷酪。
二淑仆、權(quán)限檢查及權(quán)限兼容
本節(jié)主要介紹介紹如何進(jìn)行權(quán)限檢查及權(quán)限兼容,主要分為以下幾類:
(1)targetSdkVersion>=23哥力,終端設(shè)備是6.0(api 23)以上系統(tǒng)蔗怠;
安裝的時(shí)候不會(huì)獲得權(quán)限墩弯,在運(yùn)行時(shí)向用戶申請(qǐng)對(duì)應(yīng)權(quán)限。這部分權(quán)限檢查比較簡(jiǎn)單寞射,不涉及權(quán)限兼容渔工,使用官方方案就可以 ,使用 Context::checkSelfPermisson 桥温,建議使用ContextCompat::checkSelfPermisson檢查權(quán)限 即可 引矩,一般檢查流程 如下:
1 判斷是否有對(duì)應(yīng)權(quán)限
(ContextCompat::checkSelfPermisson)
2 判斷是否需要解釋對(duì)應(yīng)權(quán)限用途(ActivityCompat::shouldShowRequestPermissionRationale)
如果需要解釋,則現(xiàn)實(shí)自定義權(quán)限界面即可
3 不需要解釋的話侵浸,直接請(qǐng)求對(duì)應(yīng)權(quán)限
(ActivityCompat::requestPermissions)
上述情況較為簡(jiǎn)單脓魏,在此不再贅述。
(2)targetSdkVersion<23通惫,終端設(shè)備是6.0(api 23)以上系統(tǒng)茂翔;
使用的是老的權(quán)限機(jī)制,在app 安裝時(shí)會(huì)詢問AndroidManifest.xml文件中的權(quán)限履腋,但是用戶可以在設(shè)置列表中關(guān)閉相關(guān)權(quán)限珊燎,這種情況可能會(huì)對(duì)app正常運(yùn)行造成一定影響。
(3) 終端設(shè)備系統(tǒng)小于6.0(api 23)
大家可能要問遵湖,終端設(shè)備系統(tǒng)小于6.0情況還需要考慮嗎悔政,肯定是用的老的權(quán)限管理機(jī)制,在app 安裝時(shí)會(huì)詢問AndroidManifest.xml文件中的權(quán)限延旧,用戶關(guān)閉不了谋国,真的是這樣嗎 ?
答案是否定的迁沫,在實(shí)測(cè)中發(fā)現(xiàn)芦瘾,目前有不少國(guó)產(chǎn)Rom 手機(jī)在6.0之前就有關(guān)閉權(quán)限的開關(guān)。這種情況也是我們兼容的對(duì)象集畅。
下面將會(huì)以自己開發(fā)過程中遇到的問題進(jìn)行展開 近弟,目前企鵝FM支持免流了,需要使用READ_PHONE_STATE權(quán)限 (讀取SIM卡狀態(tài))挺智,由于之前未對(duì)改權(quán)限是否關(guān)閉沒有進(jìn)行相關(guān)判斷祷愉,因此收到了很多例因?yàn)樯鲜鰴?quán)限關(guān)閉,導(dǎo)致免流失敗的情況赦颇。
適配過程如下 :
(1)使用 try catch 來檢查權(quán)限是否關(guān)閉
想法很簡(jiǎn)單二鳄,如果改權(quán)限被用戶禁止了,那肯定會(huì)異常媒怯,因此可以在catch 中做文章订讼,結(jié)果發(fā)現(xiàn)這一招根本沒有用,為啥了 沪摄?因?yàn)槭褂?READ_PHONE_STATE 權(quán)限的方法內(nèi)部已經(jīng)try catch 躯嫉,外面無(wú)法捕獲纱烘,因此該方法失效。
(2)ContextCompat::checkSelfPermisson
既然在6.0 可以使用Context::checkSelfPermisson進(jìn)行權(quán)限檢查祈餐,那能否使用support v4 中的ContextCompat::checkSelfPermisson 方法了擂啥,試一下,發(fā)現(xiàn)在api 23 以下失效帆阳,為了探究原因哺壶,查看了ActivityCompat::requestPermissons 內(nèi)部實(shí)現(xiàn),如下
內(nèi)部權(quán)限檢查方法在api 23 以下蜒谤,使用的是 PackageManager::checkPermission山宾,再去查看PackageManager::checkPermission方法,如下:發(fā)現(xiàn)只要權(quán)限在AndroidManifest.xml中注冊(cè)過鳍徽,均會(huì)認(rèn)為該權(quán)限granted 资锰,因此上述方法在api 23 以下也失效。
查閱相關(guān)資料和請(qǐng)教組內(nèi)同事阶祭,發(fā)現(xiàn)Support V4 下面有一個(gè)專門檢查權(quán)限的工具類PermissionChecker绷杜。
(3)PermissionChecker
查看PermissionChecker源碼發(fā)現(xiàn) ,PermissionChecker內(nèi)部實(shí)際上使用的是AppOpsManagerCompt濒募,而AppOpsManager是在api 19 加入進(jìn)入的(AppOpsManager后面介紹)
進(jìn)而查看AppOpsManagerCompat 內(nèi)部實(shí)現(xiàn)
AppOpsManagerCompat::permissionToOp
IMPL實(shí)現(xiàn)如下:
從上圖可以看出:在api 23以下鞭盟, AppOpsManagerImpl::permissionToOp 直接返回為null ,這直接導(dǎo)致api 23以下權(quán)限檢查將會(huì)返回 granted 瑰剃,因此齿诉,該方法在api 23 下,權(quán)限檢查方法也會(huì)失效晌姚。
(4)AppOpsManager
API 19以上 粤剧,Google 官方提供了 AppOpsManager 類來檢查權(quán)限,看到這個(gè)api 時(shí)舀凛,腦海浮現(xiàn)出 “天無(wú)絕人之路啊”俊扳,里面有兩個(gè)比較重要的方法 :AppOpsManager::checkOp(int op ,int uid ,String packageName) (hide方法)和AppOpsManager::checkOp(String op,int uid ,String packageName)(public 方法 ,api 23 以上可用)猛遍,不經(jīng)思考,直接寫出了如下兩個(gè)方法
1)AppOpsManager::checkOp(int op ,int uid ,String packageName)
需要使用反射
2)AppOpsManager::checkOp(String op,int uid ,String packageName)
API >= 23 才可以使用 :
同時(shí)也仔細(xì)看了一下AppOpsManager 類介紹号坡,并不是為開發(fā)者設(shè)計(jì)的懊烤,不過其他權(quán)限兼容可以使用這種方法,前提是 要看OP_*是在什么版本才有的宽堆,需做兼容方案 腌紧。
(5)最后查看了幾個(gè)第三方權(quán)限庫(kù)(暫未看完)
PermissionsDispatcher
AndPermission
三、跳轉(zhuǎn)到app管理權(quán)限頁(yè)面
既然在這里講解跳轉(zhuǎn)到app 管理權(quán)限頁(yè)面的方法畜隶,可想而之壁肋,事情絕對(duì)不太簡(jiǎn)單号胚。Android 碎片化不僅在存在于ui適配 ,同樣也存在于這里浸遗,導(dǎo)致我們無(wú)法使用同一種方式跳轉(zhuǎn)到app管理權(quán)限頁(yè)面(適配猫胁,Android 開發(fā)永遠(yuǎn)的痛)。
那有沒有辦法可以簡(jiǎn)化適配工作跛锌,減少開發(fā)量弃秆,方法當(dāng)然有,不過需要我們自己去總結(jié)和探索的髓帽,目前已有方法:
(1)直接跳轉(zhuǎn)到系統(tǒng)設(shè)置頁(yè)
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 菠赚,不加可能會(huì)crash。這種方式就不需要適配各個(gè)廠商的不同版本rom郑藏,缺點(diǎn)是衡查,用戶只能跳轉(zhuǎn)到系統(tǒng)設(shè)置頁(yè),然后去找對(duì)應(yīng)app 的權(quán)限管理(總會(huì)有一些用戶找不到)
(2)站在前人的肩上
引用前人經(jīng)驗(yàn):Android各大手機(jī)品牌手機(jī)跳轉(zhuǎn)到權(quán)限管理界面 (未一一驗(yàn)證必盖,畢竟沒那么多手機(jī))
那是不是前人經(jīng)驗(yàn)一定對(duì)了峡捡,那就不一定了,在當(dāng)時(shí)可能是對(duì)的筑悴,在現(xiàn)在可能就行不通了们拙,現(xiàn)在以MIUI跳轉(zhuǎn)到app 權(quán)限管理頁(yè)面為例進(jìn)行說明。
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();
}
對(duì)比1)和2)發(fā)現(xiàn)阁吝,在MIUI 6/7 和MIUI 8 上面砚婆,權(quán)限管理頁(yè)面的activity名字不一樣了,因此使用MIUI6/7的方法在MIUI8上就會(huì)失效突勇,如果沒有加上try catch 装盯,就會(huì)直接crash。
對(duì)于上述變化甲馋,作為一個(gè)開發(fā)者一般都是不知道的埂奈,即便通過反饋發(fā)現(xiàn)了這個(gè)問題,也有可能不知道對(duì)應(yīng)的activity是什么定躏,此刻要么搜索網(wǎng)上有沒有類似解決方案账磺,要么求助于對(duì)應(yīng)rom 開發(fā)廠商的開發(fā)者論壇 (有時(shí)解決回應(yīng)速度相當(dāng)慢),那有沒有更好的辦法了痊远,方法詳見(3)部分垮抗。
(3)查看某個(gè)ROM的某個(gè)版本的權(quán)限管理頁(yè)面的activity
這里以華為p8為例簡(jiǎn)要說明,詳細(xì)步驟如下:
1)通過設(shè)置找到對(duì)應(yīng)app的權(quán)限管理頁(yè)面碧聪,如下:
企鵝fm在華為p8上的權(quán)限管理頁(yè)面
2)找到對(duì)應(yīng)頁(yè)面的activity
方法一:通過add 工具查看棧頂Activity
adb shell dumpsys activity | grep "mFocusedActivity"
詳細(xì)的堆棧信息見命令窗口