[Android開發(fā)]推薦一個專屬Android 端 AOP 切面框架,沒有AspectJ芒帕,

AndroidAOP 是專屬于 Android 端 Aop 框架歉嗓,只需一個注解就可以請求權(quán)限、切換線程背蟆、禁止多點鉴分、監(jiān)測生命周期等等哮幢,本庫不是基于 AspectJ 實現(xiàn)的 Aop,當然你也可以定制出屬于你的 Aop 代碼

AndroidAOP - Github鏈接

特色功能

1冠场、本庫內(nèi)置了開發(fā)中常用的一些切面注解供你使用

2家浇、本庫支持讓你自己做切面,語法簡單易上手

3碴裙、本庫同步支持 Java 和 Kotlin 代碼

4钢悲、本庫支持切入三方庫

5、本庫支持切點方法為 Lambda 表達式的情況

6舔株、本庫支持生成所有切點信息Json文件莺琳,方便一覽所有切點位置在此配置

7、本庫不是基于 AspectJ 實現(xiàn)的载慈,織入代碼量極少惭等,侵入性極低

點此下載apk

版本限制

最低Gradle版本:8.0

最低SDK版本:minSdkVersion >= 21

使用步驟

在開始之前可以給項目一個Star嗎?非常感謝办铡,你的支持是我唯一的動力辞做。歡迎Star和Issues!

一、在項目根目錄下的build.gradle添加(必須)

buildscript {
    dependencies {
        //必須項 ??
        classpath 'io.github.FlyJingFish.AndroidAop:android-aop-plugin:1.2.0'
    }
}
plugins {
    //非必須項 ??寡具,如果需要自定義切面秤茅,并且使用 android-aop-ksp 這個庫的話需要配置 ,下邊版本號根據(jù)你項目的 Kotlin 版本決定
    id 'com.google.devtools.ksp' version '1.8.0-1.0.9' apply false
}

Kotlin 和 KSP Github 的匹配版本號列表

二童叠、在 app 的build.gradle添加(此步為必須項)

??注意:??此步為必須項??

//必須項 ??
plugins {
    ...
    id 'android.aop'//最好放在最后一行
}

三框喳、引入依賴庫

plugins {
    //非必須項 ??,如果需要自定義切面厦坛,并且使用 android-aop-ksp 這個庫的話需要配置 
    id 'com.google.devtools.ksp'
}

dependencies {
    //必須項 ??
    implementation 'io.github.FlyJingFish.AndroidAop:android-aop-core:1.2.0'
    implementation 'io.github.FlyJingFish.AndroidAop:android-aop-annotation:1.2.0'
    //非必須項 ??五垮,如果你想自定義切面需要用到,??支持Java和Kotlin代碼寫的切面
    ksp 'io.github.FlyJingFish.AndroidAop:android-aop-ksp:1.2.0'
    //非必須項 ??杜秸,如果你想自定義切面需要用到放仗,??只適用于Java代碼寫的切面
    annotationProcessor 'io.github.FlyJingFish.AndroidAop:android-aop-processor:1.2.0'
    //??上邊的 android-aop-ksp 和 android-aop-processor 二選一
}

提示:ksp 或 annotationProcessor只是在當前 module 起作用,在哪個 module 中有自定義切面代碼就加在哪個 module撬碟,必須依賴項可以通過 api 方式只加到公共 module 上

四诞挨、在 app 的build.gradle添加 androidAopConfig 配置項(此步為可選配置項)

plugins {
    ...
}
androidAopConfig {
    // enabled 為 false 切面不再起作用,默認不寫為 true
    enabled true 
    // include 不設(shè)置默認全部掃描小作,設(shè)置后只掃描設(shè)置的包名的代碼
    include '你項目的包名','自定義module的包名','自定義module的包名'
    // exclude 是掃描時排除的包
    // 可排除 kotlin 相關(guān)亭姥,提高速度
    exclude 'kotlin.jvm', 'kotlin.internal','kotlinx.coroutines.internal', 'kotlinx.coroutines.android'
    
    // verifyLeafExtends 是否開啟驗證葉子繼承稼钩,默認打開顾稀,如果沒有設(shè)置 @AndroidAopMatchClassMethod 的 type = MatchType.LEAF_EXTENDS,可以關(guān)閉
    verifyLeafExtends true
    //默認關(guān)閉坝撑,開啟在 Build 或 打包后 將會生成切點信息json文件在 app/build/tmp/cutInfo.json
    cutInfoJson false
}
android {
    ...
}

提示:合理使用 include 和 exclude 可提高編譯速度静秆,建議直接使用 include 設(shè)置你項目的相關(guān)包名(包括 app 和自定義 module 的)

另外設(shè)置此處之后由于 Android Studio 可能有緩存粮揉,建議重啟 AS 并 clean 下項目再繼續(xù)開發(fā)

本庫內(nèi)置了一些功能注解可供你直接使用

注解名稱 參數(shù)說明 功能說明
@SingleClick value = 快速點擊的間隔,默認1000ms 單擊注解抚笔,加入此注解扶认,可使你的方法只有單擊時才可進入
@DoubleClick value = 兩次點擊的最大用時,默認300ms 雙擊注解殊橙,加入此注解辐宾,可使你的方法雙擊時才可進入
@IOThread ThreadType = 線程類型 切換到子線程的操作,加入此注解可使你的方法內(nèi)的代碼切換到子線程執(zhí)行
@MainThread 無參數(shù) 切換到主線程的操作膨蛮,加入此注解可使你的方法內(nèi)的代碼切換到主線程執(zhí)行
@OnLifecycle value = Lifecycle.Event 監(jiān)聽生命周期的操作叠纹,加入此注解可使你的方法內(nèi)的代碼在對應(yīng)生命周期內(nèi)才去執(zhí)行
@TryCatch value = 你自定義加的一個flag 加入此注解可為您的方法包裹一層 try catch 代碼
@Permission value = 權(quán)限的字符串數(shù)組 申請權(quán)限的操作,加入此注解可使您的代碼在獲取權(quán)限后才執(zhí)行
@Scheduled initialDelay = 延遲開始時間
interval = 間隔
repeatCount = 重復次數(shù)
isOnMainThread = 是否主線程
id = 唯一標識
定時任務(wù)敞葛,加入此注解誉察,可使你的方法每隔一段時間執(zhí)行一次,調(diào)用AndroidAop.shutdownNow(id)或AndroidAop.shutdown(id)可停止
@Delay delay = 延遲時間
isOnMainThread = 是否主線程
id = 唯一標識
延遲任務(wù)惹谐,加入此注解持偏,可使你的方法延遲一段時間執(zhí)行,調(diào)用AndroidAop.shutdownNow(id)或AndroidAop.shutdown(id)可取消
@CustomIntercept value = 你自定義加的一個字符串數(shù)組的flag 自定義攔截氨肌,配合 AndroidAop.setOnCustomInterceptListener 使用鸿秆,屬于萬金油

上述注解使用示例都在這,還有這

這塊強調(diào)一下 @OnLifecycle

  • 1、@OnLifecycle 加到的方法所屬對象必須是屬于直接或間接繼承自 FragmentActivity 或 Fragment的方法才有用儒飒,或者注解方法的對象實現(xiàn) LifecycleOwner 也可以
  • 2谬莹、如果第1點不符合的情況下,可以給切面方法第一個參數(shù)設(shè)置為第1點的類型桩了,在調(diào)用切面方法傳入也是可以的附帽,例如:
public class StaticClass {
    @SingleClick(5000)
    @OnLifecycle(Lifecycle.Event.ON_RESUME)
    public static void onStaticPermission(MainActivity activity, int maxSelect , ThirdActivity.OnPhotoSelectListener back){
        back.onBack();
    }

}

下面再著重介紹下 @TryCatch @Permission @CustomIntercept

  • @TryCatch 使用此注解你可以設(shè)置以下設(shè)置(非必須)
AndroidAop.INSTANCE.setOnThrowableListener(new OnThrowableListener() {
    @Nullable
    @Override
    public Object handleThrowable(@NonNull String flag, @Nullable Throwable throwable,TryCatch tryCatch) {
        // TODO: 2023/11/11 發(fā)生異常可根據(jù)你當時傳入的flag作出相應(yīng)處理井誉,如果需要改寫返回值邻储,則在 return 處返回即可
        return 3;
    }
});
  • @Permission 使用此注解必須配合以下設(shè)置(??此步為必須設(shè)置的,否則是沒效果的)
AndroidAop.INSTANCE.setOnPermissionsInterceptListener(new OnPermissionsInterceptListener() {
    @SuppressLint("CheckResult")
    @Override
    public void requestPermission(@NonNull ProceedJoinPoint joinPoint, @NonNull Permission permission, @NonNull OnRequestPermissionListener call) {
        Object target =  joinPoint.getTarget();
        if (target instanceof FragmentActivity){
            RxPermissions rxPermissions = new RxPermissions((FragmentActivity) target);
            rxPermissions.request(permission.value()).subscribe(call::onCall);
        }else if (target instanceof Fragment){
            RxPermissions rxPermissions = new RxPermissions((Fragment) target);
            rxPermissions.request(permission.value()).subscribe(call::onCall);
        }else{
            // TODO: target 不是 FragmentActivity 或 Fragment 杠愧,說明注解所在方法不在其中篮撑,請自行處理這種情況
            // 建議:切點方法第一個參數(shù)可以設(shè)置為 FragmentActivity 或 Fragment ,然后 joinPoint.args[0] 就可以拿到
        }
    }
});
  • @CustomIntercept 使用此注解你必須配合以下設(shè)置(??此步為必須設(shè)置的在岂,否則還有什么意義呢奔则?)
AndroidAop.INSTANCE.setOnCustomInterceptListener(new OnCustomInterceptListener() {
    @Nullable
    @Override
    public Object invoke(@NonNull ProceedJoinPoint joinPoint, @NonNull CustomIntercept customIntercept) {
        // TODO: 2023/11/11 在此寫你的邏輯 在合適的地方調(diào)用 joinPoint.proceed(),
        //  joinPoint.proceed(args)可以修改方法傳入的參數(shù)蔽午,如果需要改寫返回值易茬,則在 return 處返回即可

        return null;
    }
});

??上邊三個監(jiān)聽,最好放到你的 application 中

此外本庫也同樣支持讓你自己做切面,實現(xiàn)起來非常簡單抽莱!

本庫通過 @AndroidAopPointCut 和 @AndroidAopMatchClassMethod 兩種注解范抓,實現(xiàn)自定義切面

一、@AndroidAopPointCut 是在方法上通過注解的形式做切面的食铐,上述中注解都是通過這個做的匕垫,詳細使用請看wiki文檔

??注意:自定義的注解(也就是被 @AndroidAopPointCut 注解的注解類)如果是 Kotlin 代碼請用 android-aop-ksp 那個庫

下面以 @CustomIntercept 為例介紹下該如何使用

  • 創(chuàng)建注解
@AndroidAopPointCut(CustomInterceptCut.class)
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomIntercept {
    String[] value() default {};
}
  • 創(chuàng)建注解處理切面的類(需要實現(xiàn) BasePointCut 接口,它的泛型填上邊的注解)
class CustomInterceptCut : BasePointCut<CustomIntercept> {
    override fun invoke(
        joinPoint: ProceedJoinPoint,
        annotation: CustomIntercept //annotation就是你加到方法上的注解
    ): Any? {
        // 在此寫你的邏輯
        // joinPoint.proceed() 表示繼續(xù)執(zhí)行切點方法的邏輯虐呻,不調(diào)用此方法不會執(zhí)行切點方法里邊的代碼
        // 關(guān)于 ProceedJoinPoint 可以看wiki 文檔象泵,詳細點擊下方鏈接
        return joinPoint.proceed()
    }
}

關(guān)于 ProceedJoinPoint 使用說明,下文的 ProceedJoinPoint 同理

  • 使用

直接將你寫的注解加到任意一個方法上斟叼,例如加到了 onCustomIntercept() 當 onCustomIntercept() 被調(diào)用時首先會進入到上文提到的 CustomInterceptCut 的 invoke 方法上

@CustomIntercept("我是自定義數(shù)據(jù)")
fun onCustomIntercept(){
    
}

二单芜、@AndroidAopMatchClassMethod 是做匹配某類及其對應(yīng)方法的切面的

匹配方法支持精準匹配,點此看wiki詳細使用文檔

??注意:自定義的匹配類方法切面(也就是被 @AndroidAopMatchClassMethod 注解的代碼)如果是 Kotlin 代碼請用 android-aop-ksp 那個庫

  • 例子一
package com.flyjingfish.test_lib;

public class TestMatch {
    public void test1(int value1,String value2){

    }

    public String test2(int value1,String value2){
        return value1+value2;
    }
}

假如 TestMatch 是要匹配的類犁柜,而你想要匹配到 test2 這個方法洲鸠,下邊是匹配寫法:

package com.flyjingfish.test_lib.mycut;

@AndroidAopMatchClassMethod(
        targetClassName = "com.flyjingfish.test_lib.TestMatch",
        methodName = ["test2"],
        type = MatchType.SELF
)
class MatchTestMatchMethod : MatchClassMethod {
  override fun invoke(joinPoint: ProceedJoinPoint, methodName: String): Any? {
    Log.e("MatchTestMatchMethod","======"+methodName+",getParameterTypes="+joinPoint.getTargetMethod().getParameterTypes().length);
    // 在此寫你的邏輯 
    //不想執(zhí)行原來方法邏輯,??就不調(diào)用下邊這句
    return joinPoint.proceed()
  }
}

可以看到上方 AndroidAopMatchClassMethod 設(shè)置的 type 是 MatchType.SELF 表示只匹配 TestMatch 這個類自身馋缅,不考慮其子類

  • 例子二

假如想 Hook 所有的 android.view.View.OnClickListener 的 onClick扒腕,說白了就是想全局監(jiān)測所有的設(shè)置 OnClickListener 的點擊事件,代碼如下:

@AndroidAopMatchClassMethod(
    targetClassName = "android.view.View.OnClickListener",
    methodName = ["onClick"],
    type = MatchType.EXTENDS //type 一定是 EXTENDS 因為你想 hook 所有繼承了 OnClickListener 的類
)
class MatchOnClick : MatchClassMethod {
//    @SingleClick(5000) //聯(lián)合 @SingleClick 萤悴,給所有點擊增加防多點瘾腰,6不6
    override fun invoke(joinPoint: ProceedJoinPoint, methodName: String): Any? {
        Log.e("MatchOnClick", "=====invoke=====$methodName")
        return joinPoint.proceed()
    }
}

可以看到上方 AndroidAopMatchClassMethod 設(shè)置的 type 是 MatchType.EXTENDS 表示匹配所有繼承自 OnClickListener 的子類,另外更多繼承方式覆履,請參考Wiki文檔

??注意:如果子類沒有該方法蹋盆,則切面無效,另外對同一個類的同一個方法不要做多次匹配硝全,否則只有一個會生效

匹配切面實用場景:

  • 例如你想做退出登陸邏輯時可以使用上邊這個栖雾,只要在頁面內(nèi)跳轉(zhuǎn)就可以檢測是否需要退出登陸

  • 又或者你想在三方庫某個方法上設(shè)置切面,可以直接設(shè)置對應(yīng)類名伟众,對應(yīng)方法析藕,然后 type = MatchType.SELF,這樣可以侵入三方庫的代碼凳厢,當然這么做記得修改上文提到的 androidAopConfig 的配置

詳細使用請看wiki文檔

常見問題

1账胧、Build時報錯 "ZipFile invalid LOC header (bad signature)"

  • 請重啟Android Studio,然后 clean 項目

2先紫、 同一個方法存在多個注解或匹配切面時治泥,怎么處理的

  • 多個切面疊加到一個方法上時注解優(yōu)先于匹配切面(上文的匹配切面),注解切面之間從上到下依次執(zhí)行
  • 調(diào)用 proceed 才會執(zhí)行下一個切面遮精,多個切面中最后一個切面執(zhí)行 proceed 才會調(diào)用切入方法內(nèi)的代碼
  • 在前邊切面中調(diào)用 proceed(args) 可更新方法傳入?yún)?shù)居夹,并在下一個切面中也會拿到上一層更新的參數(shù)
  • 存在異步調(diào)用proceed時,第一個異步調(diào)用 proceed 切面的返回值(就是 invoke 的返回值)就是切入方法的返回值;

混淆規(guī)則

下邊是涉及到本庫的一些必須混淆規(guī)則

# AndroidAop必備混淆規(guī)則 -----start-----

-keep class * {
    @androidx.annotation.Keep <fields>;
}

-keepnames class * implements com.flyjingfish.android_aop_annotation.base.BasePointCut
-keepnames class * implements com.flyjingfish.android_aop_annotation.base.MatchClassMethod
-keep class * implements com.flyjingfish.android_aop_annotation.base.BasePointCut{
    public <init>();
}
-keep class * implements com.flyjingfish.android_aop_annotation.base.MatchClassMethod{
    public <init>();
}

# AndroidAop必備混淆規(guī)則 -----end-----
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末吮播,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子眼俊,更是在濱河造成了極大的恐慌意狠,老刑警劉巖,帶你破解...
    沈念sama閱讀 223,002評論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疮胖,死亡現(xiàn)場離奇詭異环戈,居然都是意外死亡,警方通過查閱死者的電腦和手機澎灸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,357評論 3 400
  • 文/潘曉璐 我一進店門院塞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人性昭,你說我怎么就攤上這事拦止。” “怎么了糜颠?”我有些...
    開封第一講書人閱讀 169,787評論 0 365
  • 文/不壞的土叔 我叫張陵汹族,是天一觀的道長。 經(jīng)常有香客問我其兴,道長顶瞒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,237評論 1 300
  • 正文 為了忘掉前任元旬,我火速辦了婚禮榴徐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘匀归。我一直安慰自己坑资,他們只是感情好,可當我...
    茶點故事閱讀 69,237評論 6 398
  • 文/花漫 我一把揭開白布穆端。 她就那樣靜靜地躺著盐茎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪徙赢。 梳的紋絲不亂的頭發(fā)上字柠,一...
    開封第一講書人閱讀 52,821評論 1 314
  • 那天,我揣著相機與錄音狡赐,去河邊找鬼窑业。 笑死,一個胖子當著我的面吹牛枕屉,可吹牛的內(nèi)容都是我干的常柄。 我是一名探鬼主播,決...
    沈念sama閱讀 41,236評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼西潘!你這毒婦竟也來了卷玉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,196評論 0 277
  • 序言:老撾萬榮一對情侶失蹤喷市,失蹤者是張志新(化名)和其女友劉穎相种,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體品姓,經(jīng)...
    沈念sama閱讀 46,716評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡寝并,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,794評論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了腹备。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片衬潦。...
    茶點故事閱讀 40,928評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖植酥,靈堂內(nèi)的尸體忽然破棺而出镀岛,到底是詐尸還是另有隱情,我是刑警寧澤友驮,帶...
    沈念sama閱讀 36,583評論 5 351
  • 正文 年R本政府宣布哎媚,位于F島的核電站,受9級特大地震影響喊儡,放射性物質(zhì)發(fā)生泄漏拨与。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,264評論 3 336
  • 文/蒙蒙 一艾猜、第九天 我趴在偏房一處隱蔽的房頂上張望买喧。 院中可真熱鬧,春花似錦匆赃、人聲如沸淤毛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,755評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽低淡。三九已至,卻和暖如春瞬项,著一層夾襖步出監(jiān)牢的瞬間蔗蹋,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,869評論 1 274
  • 我被黑心中介騙來泰國打工囱淋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留猪杭,地道東北人。 一個月前我還...
    沈念sama閱讀 49,378評論 3 379
  • 正文 我出身青樓妥衣,卻偏偏與公主長得像皂吮,于是被迫代替她去往敵國和親戒傻。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,937評論 2 361

推薦閱讀更多精彩內(nèi)容