史上最實用的Android切片應用庫XAOP使用指南

項目簡介

一個輕量級的AOP(Android)應用框架,囊括了最實用的AOP應用。項目地址: https://github.com/xuexiangjys/XAOP, 喜歡的話,歡迎star支持!

設計原由

在我們平時開發的過程中,一定會遇到權限申請、線程切換、數據緩存、異常捕獲、埋點和方法執行時間統計等問題。這些都是非常常見的問題,實現起來也不是很難,不過就是太麻煩了,還會讓程序多出很多重複性、模版化的代碼。

設計思路

讓我最初接觸到AOP思想的是JakeWharton的hugo,通過閱讀它的源碼之後,讓我對aspectj這項技術的動態代碼編織深深地着了迷。之後我詳細研究了aspectj相關的技術,並不斷蒐集AOP在Android上的典型應用場景,然後通過aspectj這項技術去逐一實現。最後就成就了XAOP這個庫。

解決痛點

  • 解決快速點擊的問題
  • 解決Android6.0以上動態權限申請的問題
  • 線程自由切換的問題
  • 日誌埋點問題
  • 緩存問題(磁盤緩存和內存緩存)
  • 異常捕獲處理
  • 業務攔截(登陸驗證、有效性驗證等)

集成指南

添加Gradle依賴

1.先在項目根目錄的 build.gradle 的 repositories 添加:

allprojects {
    repositories {
        ...
        maven { url "https://jitpack.io" }
    }
}

2.再在項目根目錄的 build.gradle 的 dependencies 添加xaop插件:

buildscript {
    ···
    dependencies {
        ···
        classpath 'com.github.xuexiangjys.XAOP:xaop-plugin:1.1.0'
    }
}

3.在項目的 build.gradle 中增加依賴並引用xaop插件

apply plugin: 'com.xuexiang.xaop' //引用xaop插件

dependencies {
    ···
    //如果是androidx項目,使用1.1.0版本及以上
    implementation 'com.github.xuexiangjys.XAOP:xaop-runtime:1.1.0'
    //如果是support項目,請使用1.0.5版本
    implementation 'com.github.xuexiangjys.XAOP:xaop-runtime:1.0.5'
}

4.在Application中進行初始化


XAOP.init(this); //初始化插件
XAOP.debug(true); //日誌打印切片開啓
XAOP.setPriority(Log.INFO); //設置日誌打印的等級,默認爲0

//設置動態申請權限切片 申請權限被拒絕的事件響應監聽
XAOP.setOnPermissionDeniedListener(new PermissionUtils.OnPermissionDeniedListener() {
    @Override
    public void onDenied(List<string> permissionsDenied) {
    	//權限申請被拒絕的處理
    }
});

//設置自定義攔截切片的處理攔截器
XAOP.setInterceptor(new Interceptor() {
    @Override
    public boolean intercept(int type, JoinPoint joinPoint) throws Throwable {
        XLogger.d("正在進行攔截,攔截類型:" + type);
        switch(type) {
            case 1:
                //做你想要的攔截
                break;
            case 2:
                return true; //return true,直接攔截切片的執行
            default:
                break;
        }
        return false;
    }
});

//設置自動捕獲異常的處理者
XAOP.setIThrowableHandler(new IThrowableHandler() {
    @Override
    public Object handleThrowable(String flag, Throwable throwable) {
        XLogger.d("捕獲到異常,異常的flag:" + flag);
        if (flag.equals(TRY_CATCH_KEY)) {
            return 100;
        }
        return null;
    }
});

兼容Kotlin語法配置

1.在項目根目錄的 build.gradle 的 dependencies 添加 aspectjx 插件:

buildscript {
    ···
    dependencies {
        ···
        classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.10'
    }
}

2.在項目的 build.gradle 中增加依賴並引用 aspectjx 插件

apply plugin: 'android-aspectjx' //引用aspectjx插件

aspectjx {
    include '項目的applicationId'
}

詳細使用可參見 kotlin-test 項目進行使用.

混淆配置

-keep @com.xuexiang.xaop.annotation.* class * {*;}
-keep @org.aspectj.lang.annotation.* class * {*;}
-keep class * {
    @com.xuexiang.xaop.annotation.* <fields>;
    @org.aspectj.lang.annotation.* <fields>;
}
-keepclassmembers class * {
    @com.xuexiang.xaop.annotation.* <methods>;
    @org.aspectj.lang.annotation.* <methods>;
}

基礎使用

快速點擊切片

  • SingleClick屬性表
屬性名 類型 默認值 備註
value long 1000 快速點擊的間隔(ms)

1.使用@SingleClick標註點擊的方法。注意點擊的方法中一定要有點擊控件View作爲方法參數,否則將不起作用。

2.可以設置快速點擊的時間間隔,單位:ms。不設置的話默認是1000ms。

@SingleClick(5000)
public void handleOnClick(View v) {
    XLogger.e("點擊響應!");
    ToastUtil.get().toast("點擊響應!");
    hello("xuexiangjys", "666666");
}

動態申請權限切片

  • Permission屬性表
屬性名 類型 默認值 備註
value String[] / 需要申請權限的集合

1.使用@Permission標註需要申請權限執行的方法。可設置申請一個或多個權限。

2.使用@Permission標註的方法,在執行時會自動判斷是否需要申請權限。

@SingleClick
@Permission({PermissionConsts.CALENDAR, PermissionConsts.CAMERA, PermissionConsts.LOCATION})
private void handleRequestPermission(View v) {

}

主線程切片

1.使用@MainThread標註需要在主線程中執行的方法。

2.使用@MainThread標註的方法,在執行時會自動切換至主線程。

@MainThread
private void doInMainThread(View v) {
    mTvHello.setText("工作在主線程");
}

IO線程切片

  • IOThread屬性表
屬性名 類型 默認值 備註
value ThreadType ThreadType.Fixed 子線程的類型

1.使用@IOThread標註需要在io線程中執行的方法。可設置線程池的類型ThreadType,不設置的話默認是Fixed類型。

線程池的類型如下:

  • Single:單線程池
  • Fixed:多線程池
  • Disk:磁盤讀寫線程池(本質上是單線程池)
  • Network:網絡請求線程池(本質上是多線程池)

2.使用@IOThread標註的方法,在執行時會自動切換至指定類型的io線程。

@IOThread(ThreadType.Single)
private String doInIOThread(View v) {
    return "io線程名:" + Thread.currentThread().getName();
}

日誌打印切片

  • DebugLog屬性表
屬性名 類型 默認值 備註
priority int 0 日誌的優先級

1.使用@DebugLog標註需要打印的方法和類。可設置打印的優先級,不設置的話默認優先級爲0。注意:如果打印的優先級比XAOP.setPriority設置的優先級小的話,將不會進行打印。

2.使用@DebugLog標註的類和方法在執行的過程中,方法名、參數、執行的時間以及結果都將會被打印。

3.可調用XAOP.setISerializer設置打印時序列化參數對象的序列化器。

4.可調用XAOP.setLogger設置打印的實現接口。默認提供的是突破4000限制的logcat日誌打印。

@DebugLog(priority = Log.ERROR)
private String hello(String name, String cardId) {
    return "hello, " + name + "! Your CardId is " + cardId + ".";
}

內存緩存切片

  • MemoryCache屬性表
屬性名 類型 默認值 備註
value String "" 內存緩存的key
enableEmpty boolean true 對於String、數組和集合等,是否允許緩存爲空

1.使用@MemoryCache標註需要內存緩存的方法。可設置緩存的key,不設置的話默認key爲方法名(參數1名=參數1值|參數2名=參數2值|...),當然你也可以修改key的自動生成規則,你只需要調用XAOP.setICacheKeyCreator即可。

2.標註的方法一定要有返回值,否則內存緩存切片將不起作用。

3.使用@MemoryCache標註的方法,可自動實現緩存策略。默認使用的內存緩存是LruCache

4.可調用XAOP.initMemoryCache設置內存緩存的最大數量。默認是Runtime.getRuntime().maxMemory() / 1024) / 8

@MemoryCache
private String hello(String name, String cardId) {
    return "hello, " + name + "! Your CardId is " + cardId + ".";
}

磁盤緩存切片

  • DiskCache屬性表
屬性名 類型 默認值 備註
value String "" 內存緩存的key
cacheTime long -1 緩存時間【單位:s】,默認是永久有效
enableEmpty boolean true 對於String、數組和集合等,是否允許緩存爲空

1.使用@DiskCache標註需要磁盤緩存的方法。可設置緩存的key,不設置的話默認key爲方法名(參數1名=參數1值|參數2名=參數2值|...),當然你也可以修改key的自動生成規則,你只需要調用XAOP.setICacheKeyCreator即可。

2.可設置磁盤緩存的有效期,單位:s。不設置的話默認永久有效。

3.標註的方法一定要有返回值,否則磁盤緩存切片將不起作用。

4.使用@DiskCache標註的方法,可自動實現緩存策略。默認使用的磁盤緩存是JakeWharton的DiskLruCache

5.可調用XAOP.initDiskCache設置磁盤緩存的屬性,包括磁盤序列化器IDiskConverter,磁盤緩存的根目錄,磁盤緩存的最大空間等。

@DiskCache
private String hello(String name, String cardId) {
    return "hello, " + name + "! Your CardId is " + cardId + ".";
}

自動捕獲異常切片

  • Safe屬性表
屬性名 類型 默認值 備註
value String "" 捕獲異常的標誌

1.使用@Safe標註需要進行異常捕獲的方法。可設置一個異常捕獲的標誌Flag,默認的Flag爲當前類名.方法名

2.調用XAOP.setIThrowableHandler設置捕獲異常的自定義處理者,可實現對異常的彌補處理。如果不設置的話,將只打印異常的堆棧信息。

3.使用@Safe標註的方法,可自動進行異常捕獲,並統一進行異常處理,保證方法平穩執行。

@Safe(TRY_CATCH_KEY)
private int getNumber() {
    return 100 / 0;
}

自定義攔截切片

  • Intercept屬性表
屬性名 類型 默認值 備註
value int[] / 攔截類型

1.使用@Intercept標註需要進行攔截的方法和類。可設置申請一個或多個攔截類型。

2.如果不調用XAOP.setInterceptor設置切片攔截的攔截器的話,自定義攔截切片將不起作用。

3.使用@Intercept標註的類和方法,在執行時將自動調用XAOP設置的攔截器進行攔截處理。如果攔截器處理返回true的話,該類或方法的執行將被攔截,不執行。

4.使用@Intercept可以靈活地進行切片攔截。比如用戶登錄權限等。

@SingleClick(5000)
@DebugLog(priority = Log.ERROR)
@Intercept(3)
public void handleOnClick(View v) {
    XLogger.e("點擊響應!");
    ToastUtil.get().toast("點擊響應!");
    hello("xuexiangjys", "666666");
}

@DebugLog(priority = Log.ERROR)
@Intercept({1,2,3})
private String hello(String name, String cardId) {
    return "hello, " + name + "! Your CardId is " + cardId + ".";
}

【注意】:當有多個切片註解修飾時,一般是從上至下依次順序執行。


進階使用

登陸驗證

> 在應用中,對於部分功能,如:個人中心、錢包、收藏等需要我們驗證登錄的功能,我們都可以通過@Intercept業務攔截切片來實現。

  1. 定義業務攔截類型
// 登錄校驗攔截類型
public static final int INTERCEPT_LOGIN = 10;
  1. 定義攔截處理邏輯
XAOP.setInterceptor(new Interceptor() {
    @Override
    public boolean intercept(int type, JoinPoint joinPoint) throws Throwable {
        switch(type) {
            case INTERCEPT_LOGIN:
                if (!LoginActivity.sIsLogined) { //沒登錄,進行攔截
                    ToastUtils.toast("請先進行登陸!");
                    ActivityUtils.startActivity(LoginActivity.class);
                    return true; //return true,直接攔截切片的執行
                }
                break;
            default:
                break;
        }
        return false;
    }
});
  1. 在需要攔截的地方增加@Intercept標註
@Intercept(INTERCEPT_LOGIN)
public void doSomeThing() {
    ToastUtils.toast("已登陸過啦~~");
}

常見問題

接入的問題

> 使用前,請一定要仔細閱讀集成指南,只要你每一步都參照文檔上寫的來接入,是不會有任何問題的!

1.問:我的項目是kotlin項目,我該怎麼使用?

答:kotlin項目的配置,只需要在原先項目的基礎上加上aspectjx 插件即可,詳情請參考兼容Kotlin語法配置

2.問:爲什麼我每次運行編譯時,一直報錯Invalid byte tag in constant pool,而且會自動生成一個ajcore.xxxxxxxxx.txt文件?

答:這裏很有可能你的項目目前還是使用的androidx版本,但是你使用的XAOP版本是support版本,導致編譯失敗。這裏需要強調的是,如果你的項目是support版本,請使用1.0.5版本;如果你的項目是androidx版本,請使用1.1.0及以上版本。

3.問:爲什麼我編譯都通過了,但是使用任何一個切片都沒有起任何作用?

答:這裏可能的原因有兩個。

  • 1.你使用的XAOP版本和你的項目版本不匹配導致。比如你的項目是androidx版本,但是你卻使用XAOP的support版本,這樣瞎配的話,切片是不會起任何作用的。
  • 2.你忘記在項目的 build.gradle 中增加xaop插件的引用了。
apply plugin: 'com.xuexiang.xaop' //引用xaop插件

使用的問題

1.問:爲什麼我使用@SingleClick標註點擊的方法不起作用?

答:被@SingleClick標註的方法中,一定要有點擊控件View作爲方法參數,否則將不起作用。

2.問:爲什麼我使用@Permission標註的方法,返回值失效了?

答:由於動態申請權限是一個異步的操作,所以被@Permission標註的方法是不能有返回值的。


配套設施

微信公衆號

> 更多資訊內容,歡迎微信搜索公衆號:「我的Android開源之旅」

</methods></methods></fields></fields></string>

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章