動態許可權相關的幾個庫分析

渡口一艘船發表於2019-02-22

同步更新於 許可權系列

有比較多的許可權庫 由淺入深先看簡單的開開胃

1、PermissionGen

PermissionGen

該庫比較早 看提交是3年前了 通過看該庫的readme可以瞭解這個庫的使用方式,通過鏈式呼叫比較易於配置

核心類圖如下:

image.png

將許可權涉及到核心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

MPermissions

這個是鴻洋在PermissionGen基礎上做了一些優化,作者說是使用apt解決了執行時反射的問題,優化了對外提供的api,因為申請只需要三個引數,拋棄了使用原本類庫的單例的方式,直接一個幾個靜態方法,簡單整潔暴力。

看了一下程式碼,主要是核心類MPermissions操作介面,間接操作實現該介面的APT生成類(依賴翻轉,面向介面程式設計)

image.png

可以學習的一些點:

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

相關文章