AOP
是啥這裡就不展開講了,想了解的可以搜搜搜。
那實現AOP
用什麼?
在Android
絕大部分使用ASM
和AspectJ
關於ASM
可以去看看作者同事的文章
或者巴掌的文章
下面開始講AspectJ
一:效果演示
場景:我有一段程式碼需要在擁有某個許可權後才能執行 效果:一行註解完成工作
可以看到,我的方法只有一條註解,就能幫我完成這些工作了。很顯然是這條註解幫我完成了這些工作,那它是怎麼工作的。二:準備工作
很多人都會卡在這裡,各種配置不通過。 第一種方式:使用醫生的庫https://github.com/HujiangTechnology/gradle_plugin_android_aspectjx 第二種方式:原生方式 這裡展開講講第二種
- 根
build.gradle
增加依賴
classpath 'org.aspectj:aspectjtools:1.8.9'
classpath 'org.aspectj:aspectjweaver:1.8.9'
複製程式碼
- 新建一個
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
等,這裡就不多介紹用法,畢竟這不是介紹用法的文章,不夠專業,腰板硬。
這裡提供一個思路,不僅可以用的登入啊、快取啊、統計啊什麼的。不過據大佬們的說法,這個東西存在相容性的問題,尚未清楚具體的相容性問題,忘告知。