史上最實(shí)用的Android切片應(yīng)用庫XAOP使用指南

項(xiàng)目簡介

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

設(shè)計原由

在我們平時開發(fā)的過程中婚夫,一定會遇到權(quán)限申請恰起、線程切換燃少、數(shù)據(jù)緩存终蒂、異常捕獲恬吕、埋點(diǎn)和方法執(zhí)行時間統(tǒng)計等問題签则。這些都是非常常見的問題,實(shí)現(xiàn)起來也不是很難铐料,不過就是太麻煩了渐裂,還會讓程序多出很多重復(fù)性侨颈、模版化的代碼。

設(shè)計思路

讓我最初接觸到AOP思想的是JakeWharton的hugo,通過閱讀它的源碼之后芯义,讓我對aspectj這項(xiàng)技術(shù)的動態(tài)代碼編織深深地著了迷哈垢。之后我詳細(xì)研究了aspectj相關(guān)的技術(shù),并不斷搜集AOP在Android上的典型應(yīng)用場景扛拨,然后通過aspectj這項(xiàng)技術(shù)去逐一實(shí)現(xiàn)耘分。最后就成就了XAOP這個庫。

解決痛點(diǎn)

  • 解決快速點(diǎn)擊的問題
  • 解決Android6.0以上動態(tài)權(quán)限申請的問題
  • 線程自由切換的問題
  • 日志埋點(diǎn)問題
  • 緩存問題(磁盤緩存和內(nèi)存緩存)
  • 異常捕獲處理
  • 業(yè)務(wù)攔截(登陸驗(yàn)證绑警、有效性驗(yàn)證等)

集成指南

添加Gradle依賴

1.先在項(xiàng)目根目錄的 build.gradle 的 repositories 添加:

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

2.再在項(xiàng)目根目錄的 build.gradle 的 dependencies 添加xaop插件:

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

3.在項(xiàng)目的 build.gradle 中增加依賴并引用xaop插件

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

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

4.在Application中進(jìn)行初始化


XAOP.init(this); //初始化插件
XAOP.debug(true); //日志打印切片開啟
XAOP.setPriority(Log.INFO); //設(shè)置日志打印的等級,默認(rèn)為0

//設(shè)置動態(tài)申請權(quán)限切片 申請權(quán)限被拒絕的事件響應(yīng)監(jiān)聽
XAOP.setOnPermissionDeniedListener(new PermissionUtils.OnPermissionDeniedListener() {
    @Override
    public void onDenied(List<String> permissionsDenied) {
      // 權(quán)限申請被拒絕的處理
    }

});

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

//設(shè)置自動捕獲異常的處理者
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.在項(xiàng)目根目錄的 build.gradle 的 dependencies 添加 aspectjx 插件:

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

2.在項(xiàng)目的 build.gradle 中增加依賴并引用 aspectjx 插件

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

aspectjx {
    include '項(xiàng)目的applicationId'
}

詳細(xì)使用可參見 kotlin-test 項(xiàng)目進(jìn)行使用.

混淆配置

-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>;
}

基礎(chǔ)使用

快速點(diǎn)擊切片

  • SingleClick屬性表
屬性名 類型 默認(rèn)值 備注
value long 1000 快速點(diǎn)擊的間隔(ms)

1.使用@SingleClick標(biāo)注點(diǎn)擊的方法北启。注意點(diǎn)擊的方法中一定要有點(diǎn)擊控件View作為方法參數(shù)卜朗,否則將不起作用。

2.可以設(shè)置快速點(diǎn)擊的時間間隔咕村,單位:ms场钉。不設(shè)置的話默認(rèn)是1000ms。

@SingleClick(5000)
public void handleOnClick(View v) {
    XLogger.e("點(diǎn)擊響應(yīng)懈涛!");
    ToastUtil.get().toast("點(diǎn)擊響應(yīng)逛万!");
    hello("xuexiangjys", "666666");
}

動態(tài)申請權(quán)限切片

  • Permission屬性表
屬性名 類型 默認(rèn)值 備注
value String[] / 需要申請權(quán)限的集合

1.使用@Permission標(biāo)注需要申請權(quán)限執(zhí)行的方法∨疲可設(shè)置申請一個或多個權(quán)限宇植。

2.使用@Permission標(biāo)注的方法,在執(zhí)行時會自動判斷是否需要申請權(quán)限埋心。

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

}

主線程切片

1.使用@MainThread標(biāo)注需要在主線程中執(zhí)行的方法指郁。

2.使用@MainThread標(biāo)注的方法,在執(zhí)行時會自動切換至主線程踩窖。

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

IO線程切片

  • IOThread屬性表
屬性名 類型 默認(rèn)值 備注
value ThreadType ThreadType.Fixed 子線程的類型

1.使用@IOThread標(biāo)注需要在io線程中執(zhí)行的方法坡氯〕亢幔可設(shè)置線程池的類型ThreadType洋腮,不設(shè)置的話默認(rèn)是Fixed類型。

線程池的類型如下:

  • Single:單線程池
  • Fixed:多線程池
  • Disk:磁盤讀寫線程池(本質(zhì)上是單線程池)
  • Network:網(wǎng)絡(luò)請求線程池(本質(zhì)上是多線程池)

2.使用@IOThread標(biāo)注的方法手形,在執(zhí)行時會自動切換至指定類型的io線程啥供。

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

日志打印切片

  • DebugLog屬性表
屬性名 類型 默認(rèn)值 備注
priority int 0 日志的優(yōu)先級

1.使用@DebugLog標(biāo)注需要打印的方法和類】饪罚可設(shè)置打印的優(yōu)先級伙狐,不設(shè)置的話默認(rèn)優(yōu)先級為0涮毫。注意:如果打印的優(yōu)先級比XAOP.setPriority設(shè)置的優(yōu)先級小的話,將不會進(jìn)行打印贷屎。

2.使用@DebugLog標(biāo)注的類和方法在執(zhí)行的過程中罢防,方法名、參數(shù)唉侄、執(zhí)行的時間以及結(jié)果都將會被打印咒吐。

3.可調(diào)用XAOP.setISerializer設(shè)置打印時序列化參數(shù)對象的序列化器。

4.可調(diào)用XAOP.setLogger設(shè)置打印的實(shí)現(xiàn)接口属划。默認(rèn)提供的是突破4000限制的logcat日志打印恬叹。

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

內(nèi)存緩存切片

  • MemoryCache屬性表
屬性名 類型 默認(rèn)值 備注
value String "" 內(nèi)存緩存的key
enableEmpty boolean true 對于String、數(shù)組和集合等同眯,是否允許緩存為空

1.使用@MemoryCache標(biāo)注需要內(nèi)存緩存的方法绽昼。可設(shè)置緩存的key须蜗,不設(shè)置的話默認(rèn)key為方法名(參數(shù)1名=參數(shù)1值|參數(shù)2名=參數(shù)2值|...),當(dāng)然你也可以修改key的自動生成規(guī)則硅确,你只需要調(diào)用XAOP.setICacheKeyCreator即可。

2.標(biāo)注的方法一定要有返回值明肮,否則內(nèi)存緩存切片將不起作用疏魏。

3.使用@MemoryCache標(biāo)注的方法,可自動實(shí)現(xiàn)緩存策略晤愧。默認(rèn)使用的內(nèi)存緩存是LruCache大莫。

4.可調(diào)用XAOP.initMemoryCache設(shè)置內(nèi)存緩存的最大數(shù)量。默認(rèn)是Runtime.getRuntime().maxMemory() / 1024) / 8

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

磁盤緩存切片

  • DiskCache屬性表
屬性名 類型 默認(rèn)值 備注
value String "" 內(nèi)存緩存的key
cacheTime long -1 緩存時間【單位:s】官份,默認(rèn)是永久有效
enableEmpty boolean true 對于String只厘、數(shù)組和集合等,是否允許緩存為空

1.使用@DiskCache標(biāo)注需要磁盤緩存的方法舅巷「嵛叮可設(shè)置緩存的key,不設(shè)置的話默認(rèn)key為方法名(參數(shù)1名=參數(shù)1值|參數(shù)2名=參數(shù)2值|...),當(dāng)然你也可以修改key的自動生成規(guī)則钠右,你只需要調(diào)用XAOP.setICacheKeyCreator即可赋元。

2.可設(shè)置磁盤緩存的有效期,單位:s飒房。不設(shè)置的話默認(rèn)永久有效搁凸。

3.標(biāo)注的方法一定要有返回值,否則磁盤緩存切片將不起作用狠毯。

4.使用@DiskCache標(biāo)注的方法护糖,可自動實(shí)現(xiàn)緩存策略。默認(rèn)使用的磁盤緩存是JakeWharton的DiskLruCache嚼松。

5.可調(diào)用XAOP.initDiskCache設(shè)置磁盤緩存的屬性,包括磁盤序列化器IDiskConverter嫡良,磁盤緩存的根目錄锰扶,磁盤緩存的最大空間等。

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

自動捕獲異常切片

  • Safe屬性表
屬性名 類型 默認(rèn)值 備注
value String "" 捕獲異常的標(biāo)志

1.使用@Safe標(biāo)注需要進(jìn)行異常捕獲的方法寝受】琅#可設(shè)置一個異常捕獲的標(biāo)志Flag,默認(rèn)的Flag為當(dāng)前類名.方法名很澄。

2.調(diào)用XAOP.setIThrowableHandler設(shè)置捕獲異常的自定義處理者漓帅,可實(shí)現(xiàn)對異常的彌補(bǔ)處理。如果不設(shè)置的話痴怨,將只打印異常的堆棧信息忙干。

3.使用@Safe標(biāo)注的方法,可自動進(jìn)行異常捕獲,并統(tǒng)一進(jìn)行異常處理浪藻,保證方法平穩(wěn)執(zhí)行捐迫。

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

自定義攔截切片

  • Intercept屬性表
屬性名 類型 默認(rèn)值 備注
value int[] / 攔截類型

1.使用@Intercept標(biāo)注需要進(jìn)行攔截的方法和類“可設(shè)置申請一個或多個攔截類型施戴。

2.如果不調(diào)用XAOP.setInterceptor設(shè)置切片攔截的攔截器的話,自定義攔截切片將不起作用萌丈。

3.使用@Intercept標(biāo)注的類和方法赞哗,在執(zhí)行時將自動調(diào)用XAOP設(shè)置的攔截器進(jìn)行攔截處理。如果攔截器處理返回true的話辆雾,該類或方法的執(zhí)行將被攔截肪笋,不執(zhí)行。

4.使用@Intercept可以靈活地進(jìn)行切片攔截度迂。比如用戶登錄權(quán)限等藤乙。

@SingleClick(5000)
@DebugLog(priority = Log.ERROR)
@Intercept(3)
public void handleOnClick(View v) {
    XLogger.e("點(diǎn)擊響應(yīng)!");
    ToastUtil.get().toast("點(diǎn)擊響應(yīng)惭墓!");
    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 + ".";
}

【注意】:當(dāng)有多個切片注解修飾時坛梁,一般是從上至下依次順序執(zhí)行。


進(jìn)階使用

登陸驗(yàn)證

在應(yīng)用中腊凶,對于部分功能划咐,如:個人中心、錢包钧萍、收藏等需要我們驗(yàn)證登錄的功能褐缠,我們都可以通過@Intercept業(yè)務(wù)攔截切片來實(shí)現(xiàn)。

  1. 定義業(yè)務(wù)攔截類型
// 登錄校驗(yàn)攔截類型
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) { //沒登錄,進(jìn)行攔截
                    ToastUtils.toast("請先進(jìn)行登陸划煮!");
                    ActivityUtils.startActivity(LoginActivity.class);
                    return true; //return true送丰,直接攔截切片的執(zhí)行
                }
                break;
            default:
                break;
        }
        return false;
    }
});
  1. 在需要攔截的地方增加@Intercept標(biāo)注
@Intercept(INTERCEPT_LOGIN)
public void doSomeThing() {
    ToastUtils.toast("已登陸過啦~~");
}

常見問題

接入的問題

使用前缔俄,請一定要仔細(xì)閱讀集成指南弛秋,只要你每一步都參照文檔上寫的來接入器躏,是不會有任何問題的!

1.問:我的項(xiàng)目是kotlin項(xiàng)目蟹略,我該怎么使用登失?

答:kotlin項(xiàng)目的配置,只需要在原先項(xiàng)目的基礎(chǔ)上加上aspectjx 插件即可挖炬,詳情請參考兼容Kotlin語法配置 揽浙。

2.問:為什么我每次運(yùn)行編譯時,一直報錯Invalid byte tag in constant pool意敛,而且會自動生成一個ajcore.xxxxxxxxx.txt文件?

答:這里很有可能你的項(xiàng)目目前還是使用的androidx版本馅巷,但是你使用的XAOP版本是support版本,導(dǎo)致編譯失敗草姻。這里需要強(qiáng)調(diào)的是钓猬,如果你的項(xiàng)目是support版本,請使用1.0.5版本撩独;如果你的項(xiàng)目是androidx版本敞曹,請使用1.1.0及以上版本。

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

答:這里可能的原因有兩個澳迫。

  • 1.你使用的XAOP版本和你的項(xiàng)目版本不匹配導(dǎo)致。比如你的項(xiàng)目是androidx版本剧劝,但是你卻使用XAOP的support版本橄登,這樣瞎配的話,切片是不會起任何作用的讥此。
  • 2.你忘記在項(xiàng)目的 build.gradle 中增加xaop插件的引用了示绊。
apply plugin: 'com.xuexiang.xaop' //引用xaop插件

使用的問題

1.問:為什么我使用@SingleClick標(biāo)注點(diǎn)擊的方法不起作用?

答:被@SingleClick標(biāo)注的方法中暂论,一定要有點(diǎn)擊控件View作為方法參數(shù)面褐,否則將不起作用。

2.問:為什么我使用@Permission標(biāo)注的方法取胎,返回值失效了展哭?

答:由于動態(tài)申請權(quán)限是一個異步的操作,所以被@Permission標(biāo)注的方法是不能有返回值的闻蛀。

配套設(shè)施

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末匪傍,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子觉痛,更是在濱河造成了極大的恐慌役衡,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件薪棒,死亡現(xiàn)場離奇詭異手蝎,居然都是意外死亡榕莺,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門棵介,熙熙樓的掌柜王于貴愁眉苦臉地迎上來钉鸯,“玉大人,你說我怎么就攤上這事邮辽∵氲瘢” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵吨述,是天一觀的道長岩睁。 經(jīng)常有香客問我,道長揣云,這世上最難降的妖魔是什么笙僚? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮灵再,結(jié)果婚禮上肋层,老公的妹妹穿的比我還像新娘。我一直安慰自己翎迁,他們只是感情好栋猖,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著汪榔,像睡著了一般蒲拉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上痴腌,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天雌团,我揣著相機(jī)與錄音,去河邊找鬼士聪。 笑死锦援,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的剥悟。 我是一名探鬼主播灵寺,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼区岗!你這毒婦竟也來了略板?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤慈缔,失蹤者是張志新(化名)和其女友劉穎叮称,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瓤檐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年赂韵,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片距帅。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡右锨,死狀恐怖括堤,靈堂內(nèi)的尸體忽然破棺而出碌秸,到底是詐尸還是另有隱情,我是刑警寧澤悄窃,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布讥电,位于F島的核電站,受9級特大地震影響轧抗,放射性物質(zhì)發(fā)生泄漏恩敌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一横媚、第九天 我趴在偏房一處隱蔽的房頂上張望纠炮。 院中可真熱鬧,春花似錦灯蝴、人聲如沸恢口。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽耕肩。三九已至,卻和暖如春问潭,著一層夾襖步出監(jiān)牢的瞬間猿诸,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工狡忙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留梳虽,地道東北人。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓灾茁,卻偏偏與公主長得像怖辆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子删顶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

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