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
等,这里就不多介绍用法,毕竟这不是介绍用法的文章,不够专业,腰板硬。
这里提供一个思路,不仅可以用的登录啊、缓存啊、统计啊什么的。不过据大佬们的说法,这个东西存在兼容性的问题,尚未清楚具体的兼容性问题,忘告知。