同步更新於 許可權系列
有比較多的許可權庫 由淺入深先看簡單的開開胃
1、PermissionGen
該庫比較早 看提交是3年前了 通過看該庫的readme可以瞭解這個庫的使用方式,通過鏈式呼叫比較易於配置
核心類圖如下:
將許可權涉及到核心api封裝到框架之中,幹掉冗餘函式,使用者只關心功能函式即可。
涉及到的比較核心的一個點就是,通過註解的方式直接回撥許可權成功、失敗的函式
When it succeeded in obtaining permission
@PermissionSuccess(requestCode = 100)
public void doSomething(){
Toast.makeText(this, "Contact permission is granted", Toast.LENGTH_SHORT).show();
}複製程式碼
When it failed in obtaining permission
@PermissionFail(requestCode = 100)
public void doFailSomething(){
Toast.makeText(this, "Contact permission is not granted", t.LENGTH_SHORT).show();
}複製程式碼
核心的處理邏輯在PermissionGen#requestPermissions(Object object, int requestCode, String[] permissions)中,最終通過反射調取 PermissionFail和PermissionSucess註解的函式
通過這種方式幹掉了冗餘程式碼,讓使用者專注於業務的開發
private static void requestPermissions(Object object, int requestCode, String[] permissions){
if(!Utils.isOverMarshmallow()) {
doExecuteSuccess(object, requestCode);
return;
}
List<String> deniedPermissions = Utils.findDeniedPermissions(getActivity(object), permissions);
if(deniedPermissions.size() > 0){
if(object instanceof Activity){
((Activity)object).requestPermissions(deniedPermissions.toArray(new String[deniedPermissions.size()]), requestCode);
} else if(object instanceof Fragment){
((Fragment)object).requestPermissions(deniedPermissions.toArray(new String[deniedPermissions.size()]), requestCode);
} else {
throw new IllegalArgumentException(object.getClass().getName() + " is not supported");
}
} else {
doExecuteSuccess(object, requestCode);
}
}複製程式碼
private static void executeMethod(Object activity, Method executeMethod) {
if(executeMethod != null){
try {
if(!executeMethod.isAccessible()) executeMethod.setAccessible(true);
executeMethod.invoke(activity, null);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}複製程式碼
可以看出反射函式只能無參函式 其中拿到activity或者fragment的註解方法在Utils的方法中,這裡可以學習一下反射的一些用法,比如查詢對應註解的方法
public static <A extends Annotation> Method findMethodWithRequestCode(Class clazz,
Class<A> annotation, int requestCode) {
for(Method method : clazz.getDeclaredMethods()){
if(method.isAnnotationPresent(annotation)){
if(isEqualRequestCodeFromAnntation(method, annotation, requestCode)){
return method;
}
}
}
return null;
}複製程式碼
小結:
這個庫比較簡單有一些優缺點:
優點:初步封裝了許可權的核心函式的,省去了冗餘程式碼;可以學習反射的一些用法
缺點:
(1)大量使用反射(可以使用apt),
(2)api設計不是很合理
鏈式呼叫往往是平級呼叫方法,但是現在這個明顯是有先後順序的,一不留神容易寫錯
2、MPermissions
這個是鴻洋在PermissionGen基礎上做了一些優化,作者說是使用apt解決了執行時反射的問題,優化了對外提供的api,因為申請只需要三個引數,拋棄了使用原本類庫的單例的方式,直接一個幾個靜態方法,簡單整潔暴力。
看了一下程式碼,主要是核心類MPermissions操作介面,間接操作實現該介面的APT生成類(依賴翻轉,面向介面程式設計)
可以學習的一些點:
2.1 APT 編譯前找不到類
面向介面程式設計,APT程式碼生成程式碼實現這個介面就可以,
許可權類的核心能力通過介面提供
public interface PermissionProxy<T> {
void grant(T source, int requestCode);
void denied(T source, int requestCode);
void rationale(T source, int requestCode);
boolean needShowRationale(int requestCode);
}
複製程式碼
功能類跟介面層互動即可
2.2 APT技術
通過APT生成一些重複的模板程式碼非常有用,關於APT技術在之前的部落格多有提及就不再贅述了。
不過APT也有它的弊端就是APT 的工作原理是在編譯 class 時,通過 Annotation 生成原始碼,而在 Android 構建的過程中,像 滴滴出行乘客端 這樣的整合方式,所有的模組都是 AAR 的方式來整合構建的,沒有機會去同時編譯所有的 class,這時候就需要新的手段來生成程式碼了,比如構建gradle的task在編譯前掃描全域性結合javapoet生成程式碼
在這個庫裡可以學習一下apt processor的異常處理有助於我們定位程式碼
private void error(Element element, String message, Object... args) {
if (args.length > 0) {
message = String.format(message, args);
}
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, message, element);
}
複製程式碼
小結
作者僅僅是通過APT生成了模板程式碼,但是還是使用到反射生成實現代理介面的實現類
private static PermissionProxy findPermissionProxy(Object activity) {
try {
Class clazz = activity.getClass();
Class injectorClazz = Class.forName(clazz.getName() + SUFFIX);
return (PermissionProxy) injectorClazz.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
throw new RuntimeException(String.format("can not find %s , something when compiler.", activity.getClass().getSimpleName() + SUFFIX));
}
複製程式碼
這個APT代理的思想還是值得我們去借鑑的
3、PermissionsDispatcher
這個庫目前有7726個start,而且有自己的網站指導說明,儼然是動態許可權三方庫的一哥,看來這個庫需要好好分析一下
從官方這個庫使用起來也非常簡單,仔細看了下程式碼發現大同小異,跟前面兩個庫使用的方式很像,通過註解的方式標記 許可權申請成功、許可權事情失敗、許可權彈窗說明等函式呼叫,唯一多的是:
1、需要使用動態權的 Activity
or Fragment
(we support both) to handle permissions加上RuntimePermissions註解
這個庫更多是將所有的邏輯處理放在了許可權生成類 xxxPermissionDispatcher類,(生成程式碼承擔了很多的邏輯處理)而不像MPermission庫中僅僅將使用者使用的功能程式碼,比如許可權成功、失敗之類的函式規整到生成類中,核心邏輯還是通過開發者自己程式碼處理,這樣更容易寫這個庫,使用者閱讀程式碼也比較方便
個人更加傾向於 MPermission