導語
android碎片化相信是每一個android開發(fā)者的痛丑搔。機型適配也是難以繞過去的坎厦瓢。這其中Android動態(tài)權限檢測適配,相信對于很多開發(fā)者來說啤月,都是被按在地上摩擦摩擦煮仇。本文就針對Android權限動態(tài)檢測提出一種解決方案。
一谎仲、Android權限介紹
談起Android權限機制浙垫,很多人都會想到Google在Android 6.0 提出運行時權限管理機制(Android Runtime Permission)。針對運行時權限管理機制在這里我們不做過多描述郑诺。相信對于大多數(shù)開發(fā)者而言夹姥,真正的痛點在于建立在6.0之前的權限機制的權限檢測。之所以被認為是痛點辙诞,我們會在之后給大家介紹辙售。不同于Android Runtime Permission,6.0之前的權限機制飞涂,所申請只需要在AndroidManifest.xml列舉出來旦部,這無疑給惡意軟件提供了可趁之機。
二较店、動態(tài)權限檢測
針對權限檢測志鹃,我們根據(jù)權限機制的不同,劃分為以下兩類:
(1) targetSdkVersion >= 23
當設備處于6.0或6.0以上時采用的運行時權限機制泽西,權限檢測變得很簡單曹铃,只需要調用ContextCompat::checkSelfPermission()
,瀟瀟灑灑一行代碼即可檢測對應的權限是否被賦予,簡直不要太簡單捧杉。
(2) tagetSdkVersion <23
當設備是6.0以下系統(tǒng)時陕见,使用的老的權限機制。不像大家想象的味抖,應用所需要的權限只需要在清單文件里面列出就可以一勞永逸评甜。如果真有這么簡單,那恐怕連一點裝逼的空間都沒有啦仔涩。事實上忍坷,android設備提供應用權限開關,達到用戶控制整個應用的權限的賦予。對于應用本身而言佩研,應用的很多功能是建立在擁有某一或者某些權限的基礎上柑肴。一旦用戶收回某一功能的具體權限,對于該功能來說無疑是一種災難旬薯。開發(fā)者建立了良好的容災機制那么只是會使某一功能異常晰骑,影響用戶體驗,然而對于那些沒有
建立起好的容災處理的代碼而言绊序,嚴重的可能導致應用閃退硕舆。到了線上飄紅的地步,領導請喝茶恐怕是跑不了骤公。
針對建立在老的權限機制基礎上的動態(tài)權限檢測的方案抚官,很多人第一時間就會想到我們是否可以像6.0以上設備一樣使用ContextCompat::checkSelfPermission()
。對此我們特地做了測試阶捆,然而在我們測試過程發(fā)現(xiàn)只有清單文件里面申明了該權限耗式,那么不管用戶是否關閉或者開啟這一權限對應的開關,該方法都會返回true趁猴。這無疑宣告了該方案的根本無法滿足我們的需求刊咳。為了弄明白該方法為何會出現(xiàn)這一情況,我們從源碼入手儡司,去驗證對應的邏輯娱挨。
在圖中可以看到ActivityCompat::requestPermissions()方法體里面會對當前api版本中做判斷,當api<23時捕犬,會調用PackageManager.checkPermission(),為了進一步了解我們接著去看PackageManager.checkPermission()方法體內有什么邏輯跷坝。
從圖上框出的文字中可以了解到判斷權限是否賦予是去判斷對應的package是否含有該權限,換句更容易理解的話來說就是如果該權限在清單文件中聲明了碉碉,即代表該權限被賦予柴钻。由此綜合前面的代碼片段來看當api<23時,使用
ContextCompat::checkSelfPermission()
是無法滿足我們的需求垢粮。那么除了使用
ContextCompat::checkSelfPermission()
還有什么辦法可以滿足我們的代碼需求贴届。這里我們就不得不提起我們要介紹給大家的解決方案——AppOpsManager。
三蜡吧、權限檢測解決方案——AppOpsManager
AppOpsManager究竟是什么呢毫蚓?Google在Android開發(fā)者手冊對AppPosManager的描述為
API for interacting with "application operation" tracking.
這玩意說白了就是應用程序操作管理。AppOpsManager是在api 19引入昔善,即Android 4.3元潘。
通過查看Android開發(fā)者手冊,以及查看AppOpsManager的源碼我們會發(fā)現(xiàn)checkOp()的多態(tài)方法君仆,其中checkOp(String op,int uid,String packageName)主要在api>23可以使用翩概,而checkOp(int op,int uid,String packageName)則沒有這一限制牲距。既然有了思路,那么就來介紹一下AppOpsManager針對權限控制應該主要那些東西钥庇。查看AppOpsManager的文檔牍鞠,你會看到這樣一段文字:
This API is not generally intended for third party application developers; most features are only available to system applications.
這么一段文字扯了啥呢,其實主要表達的是AppOpsManager在設計之初并不是面向開發(fā)者上沐。這一點其實在這個類的很多地方就體現(xiàn)出來了,同時也是我們需要注意的楞艾。在這個類中参咙,有很多權限對應的常量被隱藏起來,如下圖所示:
與此同時硫眯,checkOp(int op,int uid,String packageName)蕴侧,這個方法本身也是hide,也就說我們無法通過AppOpsManager這個類正常調用該方法两入,以及訪問該屬性净宵。走到這一步,然后再告訴大家裹纳,這個方法也走不通的話择葡,我估計拍磚的肯定不少。那么處理這一問題剃氧,對于我們來說敏储,這個時候當然少不了反射。你敢隱藏朋鞍,只要你存在已添,我都要把你挖出來。以下為相關的代碼片段:
private boolean isPermissionGranted(String permissionCode) {
try {
Object object = getSystemService(Context.APP_OPS_SERVICE);
if (object == null) {
return false;
}
Class localClass = object.getClass();
Class[] arrayOfClass = new Class[3];
arrayOfClass[0] = Integer.TYPE;
arrayOfClass[1] = Integer.TYPE;
arrayOfClass[2] = String.class;
Method method = localClass.getMethod("checkOp", arrayOfClass);
if (method == null) {
return false;
}
Object[] arrayOfObject = new Object[3];
arrayOfObject[0] = Integer.valueOf(permissionCode);
arrayOfObject[1] = Integer.valueOf(Binder.getCallingUid());
arrayOfObject[2] = getPackageName();
int m = ((Integer) method.invoke(object, arrayOfObject)).intValue();
return m == AppOpsManager.MODE_ALLOWED;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
結語
權限動態(tài)檢測有多種解決方案滥酥,AppOpsManager只是其中一種維護成本相對較少的方案更舞。其中在做權限動態(tài)檢測時,看到了大家提供了很多針對某一權限的腦洞極大的方案坎吻,不得不感慨這得被虐了死去活來才能得出的經(jīng)驗教訓缆蝉。同時有部分反應AppOpsManager這一解決方案在某些機型上依舊不能適配的問題,只能表示,
最后瘦真,AppOpsManager這個類還有潛在的彩蛋返奉,通過這個工具可以打造一些裝逼利器山橄,大家有興趣可以去挖掘侍咱。