AspectJ在Android中的使用

一喇喉、AOP

全稱“Aspect Oriented Programming”,面向切面編程硅堆,由于面向?qū)ο蟮乃枷胍蟾邇?nèi)聚,低耦合的風(fēng)格姓蜂,使模塊代碼間的可見性變差萎羔,對(duì)于埋點(diǎn)液走,日志輸出等需求,就會(huì)變的十分復(fù)雜贾陷,如果手動(dòng)編寫代碼缘眶,入侵性很大,不利于擴(kuò)展髓废,AOP應(yīng)運(yùn)而生巷懈。

二、使用場(chǎng)景

當(dāng)我們需要在某個(gè)方法運(yùn)行前和運(yùn)行后做一些處理時(shí)慌洪,便可使用AOP技術(shù)顶燕。具體有:

  • 統(tǒng)計(jì)埋點(diǎn)
  • 日志打印/打點(diǎn)
  • 數(shù)據(jù)校驗(yàn)
  • 行為攔截
  • 性能監(jiān)控
  • 動(dòng)態(tài)權(quán)限控制
    ***三、環(huán)境配置
    我們以Android工程+官方的AspectJ為例
    1.項(xiàng)目根目錄build.gradle配置
dependencies {
        classpath 'org.aspectj:aspectjtools:1.8.9'
        classpath 'org.aspectj:aspectjweaver:1.8.9'
    }

2.具體使用AspectJ的工程build.gradle中配置

dependencies {
   implementation 'org.aspectj:aspectjrt:1.8.13'
}
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main

final def log = project.logger
final def variants = project.android.applicationVariants

variants.all { variant ->
    if (!variant.buildType.isDebuggable()) {
        log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
        return;
    }
    JavaCompile javaCompile = variant.javaCompile
    javaCompile.doLast {
        String[] args = ["-showWeaveInfo",
                         "-1.8",
                         "-inpath", javaCompile.destinationDir.toString(),
                         "-aspectpath", javaCompile.classpath.asPath,
                         "-d", javaCompile.destinationDir.toString(),
                         "-classpath", javaCompile.classpath.asPath,
                         "-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]
        log.debug "ajc args: " + Arrays.toString(args)
        MessageHandler handler = new MessageHandler(true);
        new Main().run(args, handler);
        for (IMessage message : handler.getMessages(null, true)) {
            switch (message.getKind()) {
                case IMessage.ABORT:
                case IMessage.ERROR:
                case IMessage.FAIL:
                    log.error message.message, message.thrown
                    break;
                case IMessage.WARNING:
                    log.warn message.message, message.thrown
                    break;
                case IMessage.INFO:
                    log.info message.message, message.thrown
                    break;
                case IMessage.DEBUG:
                    log.debug message.message, message.thrown
                    break;
            }
        }
    }
}

以上配置完成后冈爹,便可以項(xiàng)目中進(jìn)行使用了涌攻,當(dāng)然,除了官方提供的频伤,還有其他三方的aspectJ框架供我們使用恳谎,如https://github.com/HujiangTechnology/gradle_plugin_android_aspectjx,其配置與上面的略有不同憋肖,開發(fā)者根據(jù)自己選擇的具體框架進(jìn)行相應(yīng)的配置

三惠爽、代碼實(shí)例

public class AspectJActivity extends AppCompatActivity {
    private String TAG = "AspectJActivity";
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_aspect);
        Log.i(TAG,hashCode()+"");
        aspectTest();//以普通方法進(jìn)行aspectJ處理
        aspectTestAnn();//以注解方式進(jìn)行
    }


    public void aspectTest() {
        Log.i(TAG, "aspect test");
    }

    @AspectAnn
    public void aspectTestAnn() {
        Log.i(TAG, "aspect by annoation test");
    }
}

要求,在運(yùn)行aspectTest()前先打印一個(gè)信息瞬哼,在運(yùn)行aspectTestAnn后也打印一個(gè)信息婚肆,兩者最大的區(qū)別在于,前者使用類名和方法名來實(shí)現(xiàn)切面坐慰,而后者則使用注解來實(shí)現(xiàn)较性,在實(shí)際開發(fā)中,用注解來實(shí)現(xiàn)切面的場(chǎng)景更多一些结胀。
自定義注解AspectAnn

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AspectAnn {

}

關(guān)鍵的切面處理類 AspectUtil

@Aspect
public class AspectUtil {

    @Pointcut
            ("execution(* com.game.xiangxuemytest.aspectj.AspectJActivity.aspectTest(..))")
    public void pointActionMethod() {}

    @Before("pointActionMethod()")
    public void testBefore(){
        Log.i("AspectJActivity","AspectUtil method1 Before");
    }

//    @Pointcut
//            ("@annotation(com.game.xiangxuemytest.aspectj.AspectAnn)")
//    public void pointActionMethod2(AspectAnn ann) {
//
//    }

    @Pointcut
            ("execution(@com.game.xiangxuemytest.aspectj.AspectAnn * *(..))")
    public void pointActionMethod2() {

    }

    @After("pointActionMethod2()")
    public void testAfter(JoinPoint point) {
        Log.i("AspectJActivity", "AspectUtil method2 After");
    }
}

@Aspect 標(biāo)志切面的處理類
@Pointcut標(biāo)志切點(diǎn)是誰赞咙,后面跟符合切點(diǎn)的規(guī)則。
1> 包名+類名+方法名來確定切點(diǎn)規(guī)則:
pointActionMethod()上糟港,com.game.xiangxuemytest.aspectj.AspectJActivity.aspectTest(..)攀操。其中第一個(gè) * 號(hào)表示返回值可為任意類型,括號(hào)內(nèi)表示參數(shù)列表秸抚, .. 表示匹配任意個(gè)參數(shù)速和,參數(shù)類型為任何類型
2>以注解來確定切點(diǎn)規(guī)則的:
方法 pointActionMethod2()歹垫,@com.game.xiangxuemytest.aspectj.AspectAnn * (..)),注意兩個(gè) 之間有個(gè)空格颠放,和方式1略有不同排惨。
@Before
切入代碼運(yùn)行在目標(biāo)代碼前,同理還有 @After,@Around等碰凶,表示目標(biāo)后暮芭,及目標(biāo)前+后。其目的表示切入點(diǎn)運(yùn)行的代碼欲低。同時(shí)辕宏,由于可以存在多個(gè)切入點(diǎn),所以該注解后要跟上切入點(diǎn)的方法名砾莱,
execution瑞筐,這個(gè)我理解的為實(shí)現(xiàn)切入的方式吧。另外還有一個(gè)call恤磷,隨后我們可以根據(jù).class文件看一下兩者的區(qū)別。

運(yùn)行結(jié)果:

AspectUtil method1 Before
aspect test
aspect by annoation test
AspectUtil method2 After

四野宜、實(shí)現(xiàn)原理

通過Gradle Transform扫步,在class文件生成后至dex文件生成前,遍歷并匹配所有符合AspectJ文件中聲明的切點(diǎn)匈子,更改我們?cè)械?class文件河胎,將事先聲明好的代碼在切點(diǎn)前后織入。因此虎敦,這個(gè)增加我們的編譯時(shí)間游岳,下面來看一下上述代碼的最終的.class文件


image.png

為了方便后面的問題,我們以截圖的方式顯示其徙,注意紅框部分胚迫。從.class文件可以看到,我們?cè)械姆椒ㄇ盎蚝笸倌牵黾恿诵碌拇a访锻。從而實(shí)現(xiàn)了我們想要的切面功能。

五闹获、call與execution

上面我們說了期犬,這兩個(gè)會(huì)影響切面實(shí)現(xiàn)的方式,我們把execution改為call后避诽,看一下其.class

image.png

比較兩者龟虎,可以發(fā)現(xiàn),call的方式沙庐,不會(huì)改變?cè)械拇a鲤妥,而是在目標(biāo)方法的調(diào)用前后佳吞,進(jìn)行了相應(yīng)的新代碼的插入。

以上是個(gè)人對(duì)AspecJ的一些入門理解旭斥,希望能對(duì)小伙伴們有所幫助容达。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市垂券,隨后出現(xiàn)的幾起案子花盐,更是在濱河造成了極大的恐慌,老刑警劉巖菇爪,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件算芯,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡凳宙,警方通過查閱死者的電腦和手機(jī)熙揍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來氏涩,“玉大人届囚,你說我怎么就攤上這事∈羌猓” “怎么了意系?”我有些...
    開封第一講書人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長饺汹。 經(jīng)常有香客問我蛔添,道長,這世上最難降的妖魔是什么兜辞? 我笑而不...
    開封第一講書人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任迎瞧,我火速辦了婚禮,結(jié)果婚禮上逸吵,老公的妹妹穿的比我還像新娘凶硅。我一直安慰自己,他們只是感情好扫皱,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開白布咏尝。 她就那樣靜靜地躺著,像睡著了一般啸罢。 火紅的嫁衣襯著肌膚如雪编检。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評(píng)論 1 310
  • 那天扰才,我揣著相機(jī)與錄音允懂,去河邊找鬼。 笑死衩匣,一個(gè)胖子當(dāng)著我的面吹牛蕾总,可吹牛的內(nèi)容都是我干的粥航。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼生百,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼递雀!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蚀浆,我...
    開封第一講書人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤缀程,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后市俊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體杨凑,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年摆昧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了撩满。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡绅你,死狀恐怖伺帘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情忌锯,我是刑警寧澤伪嫁,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站汉规,受9級(jí)特大地震影響礼殊,放射性物質(zhì)發(fā)生泄漏驹吮。R本人自食惡果不足惜针史,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望碟狞。 院中可真熱鬧啄枕,春花似錦、人聲如沸族沃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽脆淹。三九已至常空,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間盖溺,已是汗流浹背漓糙。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留烘嘱,地道東北人昆禽。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓蝗蛙,卻偏偏與公主長得像,于是被迫代替她去往敵國和親醉鳖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子捡硅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359