看我用AspectJ切切切程式碼來減少工作量

SamanLan發表於2018-01-31

AOP是啥這裡就不展開講了,想了解的可以搜搜搜。 那實現AOP用什麼? 在Android絕大部分使用ASMAspectJ 關於ASM可以去看看作者同事的文章 或者巴掌的文章 下面開始講AspectJ

一:效果演示

場景:我有一段程式碼需要在擁有某個許可權後才能執行 效果:一行註解完成工作

效果
可以看到,我的方法只有一條註解,就能幫我完成這些工作了。很顯然是這條註解幫我完成了這些工作,那它是怎麼工作的。

二:準備工作

很多人都會卡在這裡,各種配置不通過。 第一種方式:使用醫生的庫https://github.com/HujiangTechnology/gradle_plugin_android_aspectjx 第二種方式:原生方式 這裡展開講講第二種

  1. build.gradle增加依賴
        classpath 'org.aspectj:aspectjtools:1.8.9'
        classpath 'org.aspectj:aspectjweaver:1.8.9'
複製程式碼
  1. 新建一個Android Library Module並在build.gradle增加程式碼
import com.android.build.gradle.LibraryPlugin
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
複製程式碼
compile 'org.aspectj:aspectjrt:1.8.9'
複製程式碼
android.libraryVariants.all { variant ->
    LibraryPlugin plugin = project.plugins.getPlugin(LibraryPlugin)
    JavaCompile javaCompile = variant.javaCompile
    javaCompile.doLast {
        String[] args = ["-showWeaveInfo",
                         "-1.5",
                         "-inpath", javaCompile.destinationDir.toString(),
                         "-aspectpath", javaCompile.classpath.asPath,
                         "-d", javaCompile.destinationDir.toString(),
                         "-classpath", javaCompile.classpath.asPath,
                         "-bootclasspath", android.bootClasspath.join(
                File.pathSeparator)]

        MessageHandler handler = new MessageHandler(true);
        new Main().run(args, handler)

        def log = project.logger
        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:
                case IMessage.INFO:
                    log.info message.message, message.thrown
                    break;
                case IMessage.DEBUG:
                    log.debug message.message, message.thrown
                    break;
            }
        }
    }
}
複製程式碼

加黑標註注意:"-bootclasspath", android.bootClasspath.join這裡的bootClasspath前面用android很多人根據網上的配置編譯不通過是因為這裡。因為高版本的 gradle(作者gradle版本為2.3.2)需要改成這樣子 3. 回到app(你的專案)module的build.gradle增加

import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
複製程式碼
compile project(':剛剛的module')
複製程式碼
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.5",
                         "-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;
            }
        }
    }
}
複製程式碼

趕緊去編譯一下看看通過沒

程式碼來了

程式碼截圖
可以看到裡面實際也是申請許可權的程式碼而已,但是是放在裡@Aspect裡面,這就會發生以下奇妙的反應了。 簡單描述一下過程: 編譯的時候AspectJ會尋找@Aspect註解的類,使用裡面的@point所描述的特徵,遍歷所有class進行操作。而@Around是具體的操作行為,會對所有符合@point特徵的方法替換為它所註解的方法。這裡是許可權申請,然後在申請成功後再使用原方法。這樣就達到了原方法必須在申請許可權成功後才會執行下去了。看一下編譯後的程式碼
編譯後的程式碼
可以看到完全是走AspectJ的方法了。 類似@Around的還有@Before、@After等,這裡就不多介紹用法,畢竟這不是介紹用法的文章,不夠專業,腰板硬。 這裡提供一個思路,不僅可以用的登入啊、快取啊、統計啊什麼的。不過據大佬們的說法,這個東西存在相容性的問題,尚未清楚具體的相容性問題,忘告知。

相關文章