AOP,即面向切面编程。
定义:
把某一方面的一些功能提取出来与一批对象进行隔离,提取之后我们就可以对某个单方面的功能进行编程。
举个例子,app中许多功能都必须要求用户手机联网才能进行。写代码的时候,我们得加上判断:
if(能联网)
balabala..
else
提示用户检查手机网络
显然,这段逻辑需要重复写很多次,而aop的作用就是将你从不断的重复劳动中解放出来。
这里我们需要借助第三方的编译工具 AspectJ
下载aspectj 地址: http://www.eclipse.org/aspectj/downloads.php
选择一个版本下载完成后解压出里面的
放到libs中并添加至项目依赖:
修改build.gradle:
apply plugin: 'com.android.application'
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.aspectj:aspectjtools:1.8.9'
classpath 'org.aspectj:aspectjweaver:1.8.9'
}
}
repositories {
mavenCentral()
}
android {
...
}
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;
}
}
}
}
dependencies {
...
}
至此aspectJ配置完成。
下面介绍AspectJ如何处理切面,以判断手机网络是否连通为例:
1、自定义一个注解,作用目标为方法。被注解的方法即为切点。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckNet {
}
2、处理切面逻辑
@Aspect
public class CheckNetAspect {
/**
* 找到处理切点,即切点注解
* * *代表所有方法,(..)代表所有参数,这里可以根据具体的方法类型来做处理
*/
@Pointcut("execution(@com.android.pronetway.SchoolGoods.ioc.CheckNet * *(..))")
public void insertBehvior() {
}
/**
* 如何处理切面
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("insertBehvior()")
public Object dealPont(ProceedingJoinPoint joinPoint) throws Throwable {
final MethodSignature signature = (MethodSignature) joinPoint.getSignature();
final CheckNet checkNet = signature.getMethod().getAnnotation(CheckNet.class);
if (checkNet != null) {
final boolean connected = NetworkUtils.isConnected();
if (!connected) {
ToastUtils.showShort("请检查您的网络");
return null;
}
}
return joinPoint.proceed();
}
}
3、使用很简单,如获取短信验证码的方法,直接在上面加上@CheckNet注解,即可在发送请求前先判断手机网络。如果没网,会提示"请检查您的网络",而不走方法内逻辑。:
@CheckNet
public void getCheckCode(String number) {
//doSomething...
}
4、反编译看下里面的魔法
该框架在编译时自动生成相关代码,并不会有效率上的问题,我们可以反编译看下.class文件:
private static final Object getCheckcode_aroundBody1$advice(LoginPresenter paramLoginPresenter, String paramString, JoinPoint paramJoinPoint, CheckNetAspect paramCheckNetAspect, ProceedingJoinPoint paramProceedingJoinPoint)
{
if (((CheckNet)((MethodSignature)paramProceedingJoinPoint.getSignature()).getMethod().getAnnotation(CheckNet.class) != null) && (!NetworkUtils.isConnected()))
{
ToastUtils.showShort("请检查您的网络");
return null;
}
getCheckcode_aroundBody0(paramLoginPresenter, paramString, paramProceedingJoinPoint);
return null;
}
private static final void getCheckcode_aroundBody0(LoginPresenter paramLoginPresenter, String paramString, JoinPoint paramJoinPoint)
{
//doSomething...
}
@CheckNet
public void getCheckcode(String paramString)
{
JoinPoint localJoinPoint = Factory.makeJP(ajc$tjp_0, this, this, paramString);
getCheckcode_aroundBody1$advice(this, paramString, localJoinPoint, CheckNetAspect.aspectOf(), (ProceedingJoinPoint)localJoinPoint);
}