亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

通過AOP的思想 打造萬能動態權限申請框架Demo完全解析

發布時間:2020-05-29 16:15:14 來源:網絡 閱讀:461 作者:Android丶VG 欄目:移動開發
AOP優雅權限框架詳解(以及更多面試題)

https://github.com/xiangjiana/Android-MS

gradle配置
  • 在project的 build.gradle 添加 aspectJ gradle插件

    }
    dependencise {
         classpath 'com.android.tools.build:gradle:3.5.0'
    
        //1_1.grade-android-plugin-aspectjx 
        classpath 'com.hujiang.aspectjx:gradle-android-plugun-aspectjx:2.0.5'
    
       //2_1.android-maven-gradle-plugin
       classpath 'com.github.dcendents:android-maven-gradler-plugin:2.1'//ADD
       //NOTE: Do not place your application dependencise here; they belong 
      //in the individual module build.gradle files
    }
  • permission model 的 build.gradle 引入 aspect類庫
    通過AOP的思想 打造萬能動態權限申請框架Demo完全解析
  • app module 的build.gradle中啟用aspectJ插件,并且引入 permissionmodule
    通過AOP的思想 打造萬能動態權限申請框架Demo完全解析
Java代碼
  • appmodule 是使用框架的地方

    上面我說到了,使用框架思想,消除了Activity,Fragment,Service,普通類 在申請權限時的差異性,可以全部以普通類的方式來申請權限并且處理回調。所以這里展示 Activity,Fragment,Service 的動態權限申請寫法。

    普通類
    public class LocationUtil {
    private String TAG ="LocationUtil";
    @PermissionNeed(
    permissions ={Manifest.permission.ACCESS_FINE_LOCATION,
    requestCode = PermissionRequestCodeConst.REQUEST_CODE_LOCATION)
    public void getLocation() {
    Log.e(TAG,"申請位置權限之后,我要獲取經緯度");
    }
    /**
    * 這里寫的要特別注意,denied方法,必須是帶有一個int參數的方法,下面的也一樣
    * @param requestCode
    */
    @permissionDenied
    public void denied(int requestcode) {
    Log.e(TAG, "用戶不給阿'');
    }
    @permisssionDeniedForever
    public void denidFoever(int requestcode) {
    Log.e(TAG,''用戶永久拒絕'');
    }
    }
    Activity
    
    pubilc class MainActivity extends AppcompatActivity {
    private static final string TAG = ''permissionAspectTag'';
    @override
    prtected void onCreate(Bundle saveInstanceState) {
    super.onCreate(saveInstanceState);
    setcontenceView(R.layout.activity_main);

    findViewById(R.id.btn_location).setonclicklistener(v ->getlocationpermission()
    findviewById(R.id.btn_contact).setonclicklistener(v ->getcontactpermission()); }
    @permissionNeed(
    br/>}
    @permissionNeed(
    CONTACTS,Mainfest.permission.WRITE,Manifest.permission.GET_ACCOUNTS},
    requestcode = permissionsRequestcodeconst.REQUEST_CODE_CONTACT
    private void getcontactpermission() {log.d(TAG,''getcontactpermission'');
    }
    @PermissionNeed(
    br/>log.d(TAG,''getcontactpermission'');
    }
    @PermissionNeed(
    requestCode = PermissionRequestCodeConst.REQUEST_CODE_LOCATION)
    private void getLocationPermission() {
    Log.d(TAG,"getLocationPermission");

    }@PermissionDenied
    br/>@PermissionDenied
    switch (requestCode) {
    case PermissionRequestCodeConst.REQUEST_CODE_CONTACT
    Log.d(TAG,"聯系人權限被拒絕");
    break;
    case PermissionRequestCodeConst.REQUEST_CODE_LOCATION:
    Log.d(TAG,"位置權限被拒絕");
    break;
    default:
    break;

    }

    }

    ##### Fragment

    public class MyFragment extends Fragment {@Nullable
    br/>@Nullable
    public View onCreateView(LayoutInflater inflater,@Nullable ViewGroup container,Bundle savedInstanceState) {
    getLocation();
    return super.onCreateView(inflater,container, savedInstanceState);

    }
    private String TAG ="LocationUtil";

    @PermissionNeed(
    permissions ={Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.ACCESS_COARSE_LOCATION},
    requestCode = PermissionRequestCodeConst.REQUEST_CODE_LOCATION)

    public void getLocation() {
    Log.e(TAG,"申請位置權限之后,我要獲取經緯度");
    }
    /**

    • 這里寫的要特別注意,denied方法,必須是帶有一個int參數的方法,下面的也一樣
    • @param requestCode*/
      @PermissionDenied
      br/>*/
      @PermissionDenied
      }
      @PermissionDeniedForever
      br/>Log.e(TAG,"用戶不給啊");
      }
      @PermissionDeniedForever
      Log.e(TAG,"用戶永久拒絕");
      }

    }

    ##### Service

    public class MyService extends Service {@Nullable
    br/>@Nullable
    public IBinder onBind(Intent intent) {
    return null;}
    @Override
    br/>}
    @Override
    getLocation();
    return super.onStartCommand(intent, flags, startId);
    }
    private String TAG ="LocationUtil";

    @PermissionNeed(
    permissions ={Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.ACCESS_COARSE_LOCATION},
    requestCode = PermissionRequestCodeConst.REQUEST_CODE_LOCATION)

    public void getLocation() {
    Log.e(TAG,"申請位置權限之后,我要獲取經緯度");
    }
    /*
    這里寫的要特別注意,denied方法,必須是帶有一個int參數的方法,下面的也一樣

    • @param requestCode*/
      @PermissionDenied
      br/>*/
      @PermissionDenied
      }
      @PermissionDeniedForever
      br/>Log.e(TAG,"用戶不給啊");
      }
      @PermissionDeniedForever
      Log.e(TAG,"用戶永久拒絕");
      }

    }

    
    > 經過觀察,Activity,Fragment,Service,和普通類,都是定義了一個或者多個被 `@PermissionNeed`注解修飾的方法, 如果是多個,還要在 `@PermissionDenied`和 `@PermissionDeniedForever`修飾的方法 中switch處理 `requestCode`(參考上方Activity),以應對申請多次申請不同權限的結果 。
    也許除了這4個地方之外,還有別的地方需要申請動態權限,但是既然我們消除了差異性,就可以全部以普通類的方式來申請權限以及處理回調。這才叫從根本上解決問題。

這里有個坑:@PermissionDenied@PermissionDeniedForever 修飾的方法,必須有且僅有一個int類型參數, 返回值隨意.

  • zpermission module
    這里包含了框架的核心代碼,現在一步一步講解

類結構圖
通過AOP的思想 打造萬能動態權限申請框架Demo完全解析
3個注解@PermissionDenied @PermissionDeniedForever @PermissionNeed

 /**
 * 被此注解修飾的方法,會在方法執行之前去申請相應的權限,只有用戶授予權限,被修飾的方法體才會執行
 */
  @Target(ElementType.METHOD)//此注解用于修飾方法
  @Retention(RetentionPolicy.RUNTIME)//注解保留到運行時,因為可能需要反射執行方法(上面說了修飾的是方法!)
  public @interface PermissionNeed {

    String[] permissions();//需要申請的權限,支持多個,需要傳入String數組

    int requestCode()default 0;//此次申請權限之后的返回碼
  }  
  /**
  * 被此注解修飾的方法,會在權限申請失敗時被調用
  */
  @Target(ElementType.METHOD)
  @Retention(RetentionPolicy.RUNTIME)
  public @interface PermissionDenied {
  }
  /**
  * 被此注解修飾的方法,會在用戶永久禁止權限之后被調用
  */
  @Target(ElementType.METHOD)
  @Retention(RetentionPolicy.RUNTIME)
  public @interface PermissionDeniedForever {
  }

處理權限回調結果的接口 IPermissionCallback

  /**
 * 權限申請結果接口
 */
  public interface IPermissionCallback {

  /**
  * 授予權限
  */
  void granted(int requestCode);

  /**
  * 這次拒絕,但是并沒有勾選"以后不再提示"
  */
  void denied(int requestCode);

  /**
  * 勾選"以后不再提示",并且拒絕
  */
  void deniedForever(int requestCode);
}

以上都是事先要預備好的東西,接下來進入核心

PermissionAspect

  @Aspect
  public class permissinbAspect {
     private static final String TAG = "PermissionAspectTag";
     private final String pointcutExpression="execution(@com.zhou.zpermission.annotation.PermissionNeed * *(..)) && @annotation(permissionNeed)";

     @Pointcut(value = pointcutExpression)
     public void requestPermission(PermissionNeed permissionNeed) {
        Log.d(TAG,"pointCut 定義切入點");
     }
     @Around("requestPermission(permissionNeed)")
        Log.d(TAG,"pointCut 定義切入點");
   ....
 }

此段代碼解讀如下:

  • 使用 @Aspect注解來修飾類 , @Aspect是來自 AspectJ框架的注解,被它修飾的類,在編譯時會被認為是一個切面類

  • 使用 @Pointcut 注解來修飾方法 requestPermission(),被它修飾的方法會被認為是一個切入點.所謂切入點,就是 面向切面編程時,我們無侵入式地插入新的邏輯,總要找到一個確切的位置,我們要知道程序執行到哪一行的時候,輪到我們出場了!切入點,一定是方法, 不能是隨意哪一段代碼!

切入點可以是以下類型,不同的類型有不同的語法,我目前使用的是 method execution ,也就是 函數執行時。這意味著,當切入點的方法即將開始執行的時候,我們插入的邏輯將會被執行。與之類似的有一個 method call ,這個不一樣,這個是在切入點的方法 被調用時,也就是說,當偵測到該方法被外界調用的時候,而非方法自己執行。這兩者有細微差別。至于其他的類型,暫且按下不詳述。
通過AOP的思想 打造萬能動態權限申請框架Demo完全解析
除了類型之外,這里還有一個重點,那就是 MethodSignature的概念,這個類似于 jni里的方法簽名,是為了標記一個或者一類方法, AspectJ框架通過這個方法簽名,來確定 JVM的所有class對象中,有哪些方法需要被插入 新的邏輯。
具體的簽名的語法規則為:
通過AOP的思想 打造萬能動態權限申請框架Demo完全解析
通過AOP的思想 打造萬能動態權限申請框架Demo完全解析
看不懂? 看不懂就對了,舉個例子:
execution(@com.zhou.zpermission.annotation.PermissionNeed**(..))&&@annotation(permissionNeed)

這是Demo中我這么寫的,現在逐步解析:

  • execution 表示方法執行時作為切入點
  • @com.zhou.zpermission.annotation.PermissionNeed 表示 切入點的方法必須有這個注解修飾
  • **(..)) 這個比較詭異,我們知道,一個方法寫完整一點可能是這個樣子 private void test(int a)

但是如果我們不計較 訪問權限,不計較返回值類型,也不計較 函數名,甚至不計較參數列表的話,就可以寫成這個樣子 **(..)) . 表示任意方法

除此之外,還有后半截 &&@annotation(permission),它的含義為:

切入點方法需要接收來自 注解的參數。
即 切入點 @Pointcut 規定切入點的時候,只識別被 @com.zhou.zpermission.annotation.PermissionNeed 標記的方法,但是這個 @com.zhou.zpermission.annotation.PermissionNeed注解,是有自己的參數值的,所以,必須傳入這個值給到切入方法 requestPermission(PermissionNeedpermissionNeed) 去使用。
有點繞!一張圖說清楚:
通過AOP的思想 打造萬能動態權限申請框架Demo完全解析
圖中3個字符串必須一摸一樣,不然編譯就會報錯,而且報錯原因還不明確。

使用 @Around 注解來修飾 方法 doPermission(),被它修飾的方法會被認為是一個 切入策略。

Around注解的參數為:
"requestPermission(permissionNeed)", 也就是 pointcut修飾的方法名(形參名)

在我們已經定義好切入點

requestPermission(PermissionNeedpermissionNeed)的前提下,如果程序已經執行到了切入點,那么我是選擇怎么樣的策略, 目前所選擇的策略是 Around ,也就是,完全替代切入點的方法,但是依然保留了 執行原方法邏輯的可能性 joinPoint.proceed();

除了@Around策略之外,還有以下:
通過AOP的思想 打造萬能動態權限申請框架Demo完全解析
PermissionAspect類的作用是: 定義切入點和切入策略,那么現在我們確定切入點是 被注解 @PermissionNeed修飾的方法,切入策略是 @Around,那么,切入之后我們做了哪些事呢?

接下往下看...

PermissionAspectActivity

  public class permissionAspectActivity extends AppcompatActivity {
     private final static String permissionsTag = "permissions";
     private final static String requestCodeTag = "requestCode";
     private static IPermissionCallback mCallback;

    /**
     * 啟動當前這個Activity
     */
   public static void startActivity(Context context, String[] permissions,int requestCode,IPermissionCallback callback) {
      Log.d("PermissionAspectTag","context is : "+ context.getClass().getSimpleName());
     if (context  == null) return;
     mCallback = callback;
     //啟動當前這個Activiyt并且取消切換動畫
    Intent intent = new Intent(context,PermissionAspectActivity.class);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |Intent.FLAG_ACTIVITY_CLEAR_TOP);
    //開啟新的任務棧并且清除棧頂...為何要清除棧頂
    intent.putExtra(permissionsTag, permissions);
    intent.putExtra(requestCodeTag, requestCode);

    context.startActivity(intent);
    //利用context啟動activity
    if (context instanceof Activity) {
    //并且,如果是activity啟動的,那么還要屏蔽掉activity切換動畫
        ((Activity) context).overridePendingTransition(0, 0);
    }

  }

  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     Intent intent = getIntent();
     String[] permissions = intent.getStringArrayExtra (permissionsTag);
     int requestCode = intent.getIntExtra(requestCodeTag,0);

     if (PermissionUtil.hasSelfPermissions(this, permissions)) {
         mCallback.granted(requestCode);
         finish();
         overridePendingTransition(0,0);
     } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
         requestPermissions(permissions, requestCode);
    }

  }

   @Override
   public void onRequestPermissionsResult(int requestCode,@NonNull String[] permissions, @NonNull int[] grantResults) {
     //現在拿到了權限的申請結果,那么如何處理,我這個Activity只是為了申請,然后把結果告訴外界,所以結果的處理只能是外界傳進來
     boolean granted = PermissionUtil.verifyPermissions(grantResults);
     if (granted) {
     //如果用戶給了權限
          mCallback.granted(requestCode);
     } else {   
          if (PermissionUtil.shouldShowRequestPermissionRationale(this ,permissions)) {
             mCallback.denied(requestCode);
          } else {
             mCallback.deniedForever(requestCode);
         }

     }
     finish();
     overridePendingTransition(0,0);
   }

  }

解讀:

1.提供一個靜態方法 publicstaticvoidstartActivity(Contextcontext,String[]permissions,intrequestCode,IPermissionCallbackcallback),
用于啟動自己 PermissionAspectActivity,接收的參數分別為: context,需要的權限數組,權限返回碼,權限結果回調接口

  1. onCreate方法中,檢查是否已經有想要申請的權限,如果有,直接調用 mCallback.granted(requestCode); 并且結束自身,并且要注意隱藏Activity的切換動畫。如果沒有,那么,就去requestPermissions(permissions,requestCode);申請權限。
  2. 處理權限申請的回調,并且分情況調用 mCallback的回調方法,然后結束自身

需要注意:
PermissionAspectActivity必須在module的清單文件中注冊
通過AOP的思想 打造萬能動態權限申請框架Demo完全解析
并且 要定義它的 theme使得Activity完全透明
通過AOP的思想 打造萬能動態權限申請框架Demo完全解析
Gif圖效果演示:
通過AOP的思想 打造萬能動態權限申請框架Demo完全解析

AOP思想以及常用AOP框架

所謂AOPApsectOrientedProgramming) 面向切面編程。

此概念是基于OOPObjectOrientiedProgramming)面向對象編程。在OOP中,我們可以把不同的業務功能都分成一個一個的模塊,然后每一個模塊有自己的專一職責,從而優化編程過程,降低編程犯錯幾率。但是隨著OOP類的數量的增加,我們會發現,在某一些業務類中,經常有一些相同的代碼在重復編寫,但是無可奈何,比如日志打印/動態權限申請/埋點數據上報/用戶登錄狀態檢查 /服務器端口連通性檢查 等等。這些代碼,我們雖然可以他們抽離出來整理到一個個專一的模塊中,但是調用的時候,還是到處分散的,并且這些調用還***了本來不直接相關的業務代碼,讓我們閱讀業務代碼十分費勁。

而AOP的出現,就是基于OOP的這種缺陷而出現的優化方案。利用AOP,我們可以對業務邏輯的各個部分進行隔離,使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,提高開發效率,減少犯錯概率。

畫圖表示:
通過AOP的思想 打造萬能動態權限申請框架Demo完全解析
如上圖,OOP中,同樣的一段過程,我們把登錄檢查,權限檢查,埋點上報的調用代碼寫了3遍,然而都是雷同代碼,只是參數不同而已。而,換成AOP的思想來編碼。則是如下:
通過AOP的思想 打造萬能動態權限申請框架Demo完全解析

所采取的方案為:

在class A , B, C中 找到切入點,然后在切入點插入共同的邏輯,而不是多次編寫雷同的代碼。

本文的Demo中,插入相同的邏輯,使用的是 Java自定義注解+@Aspect切面類+@PointCut切入點+@Around切入策略 的方式。這只是AOP方案的一種,叫做 AspectJ

除此之外,Android開發中常用的AOP方案還有:

(Java注解存在3個階段,一個是源碼期,一個是編譯期,一個運行期)

APT

Java的注解解析技術(AnnotationProcessingTool), Apt的作用時期,是 通過 自定義注解解析類(extends AbastractProcessor),對自定義注解進行解析,然后通過JavaPoet這種java類生成工具,來生成編譯期才會有的.java(源碼中并沒有),然而我們源碼中卻可以使用這個類。

ASM

Asm是Java的字節碼操作框架,它可以動態生成類或者增強既有類的功能。理論上,它可以對class文件做任何他想做的事。包括,改變class文件的內容,或者生成新的class。嚴格來說AspectJ底層就是ASM,只不過AspectJ幫我們做了ASM框架做起來很麻煩,容易出錯的事情,讓我們可以簡單的通過 @Aspect @PointCut @Around 這樣的注解,就能完成AOP面向切面編程。但是,ASM作為AspectJ的祖宗,某些時候依然可以完成AspectJ所無法觸及到的功能, 就像是c/c++作為Java的祖宗, 現在依然有自己不可替代的作用。

AspectJ AOP框架的深入原理研究

本來想寫成一篇,但是發現篇幅太長,留個尾巴,下一篇,解析AspectJ是如何通過@注解的方式來插入邏輯的。

文章太長了,順手留下GitHub鏈接,需要獲取相關內容的可以自己去找
https://github.com/xiangjiana/Android-MS

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

西丰县| 渑池县| 鸡东县| 昌宁县| 海阳市| 平遥县| 武冈市| 长治县| 建湖县| 融水| 永济市| 水城县| 如东县| 台山市| 大竹县| 永仁县| 武定县| 临洮县| 横山县| 建水县| 荔浦县| 广宁县| 老河口市| 湘潭县| 宁海县| 南康市| 静海县| 泽库县| 桂阳县| 泗阳县| 冀州市| 额敏县| 项城市| 鄯善县| 高淳县| 奇台县| 新昌县| 龙州县| 崇州市| 上栗县| 来宾市|