网上有很多介绍 AOP 的, 就不多说什么了, 这里就直接开始了.
场景: 比如界面上有 N 个按钮, 有网络的时候, 点击按钮可以进入下一页, 没有网络的时候, 不响应点击事件, 那么使用 AOP 的这种方式就很容易实现, 当然, 这里只是随便说了一个场景, 实际上, 还有很多场景适合使用 AOP.
- 加依赖
implementation 'org.aspectj:aspectjrt:1.9.6'
- 在
app build.gradle
中添加其他配置
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.9",
"-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;
}
}
}
}
- 创建注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckNet {
String value() default "";
}
- 创建切面类
//@Aspect 声明这是一个切面类.
@Aspect
public class SectionAspect {
//抽取公共的切入点表达式
//本类引用的话, 可直接这样写 @Before("pointCut()")
//其他切面引用的话, 可以写为 @Before("com.example.aop_study.pointCut()")
//
@Pointcut("execution(@com.example.aop_study.CheckNet * * (..))")
public void pointCut() {}
@Around("pointCut()")
public Object aroundHandle(ProceedingJoinPoint joinPoint) throws Throwable {
//拿到方法签名
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
CheckNet checkNet = signature.getMethod().getAnnotation(CheckNet.class);
if (checkNet != null) {
//判断有没有网络, 获取 context.
//getThis 表示当前切点方法所在的类
Object obj = joinPoint.getThis();
Context context = getContext(obj);
if (context != null) {
if (!isNetworkAvailable(context)) {
//不再向下执行
Log.e("SectionAspect","没有网络");
return null;
}
}
}
return joinPoint.proceed();
}
/**
* 通过对象获取上下文
*
* @param obj
* @return
*/
private Context getContext(Object obj) {
if (obj instanceof Activity) {
return (Activity) obj;
} else if (obj instanceof Fragment) {
Fragment fragment = (Fragment) obj;
return fragment.getActivity();
} else if (obj instanceof View) {
View view = (View) obj;
return view.getContext();
}
return null;
}
/**
* 检查当前网络是否可用
*
* @return
*/
private static boolean isNetworkAvailable(Context context) {
// 获取手机所有连接管理对象(包括对wi-fi,net等连接的管理)
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (Build.VERSION_CODES.LOLLIPOP <= Build.VERSION.SDK_INT) {
Network[] networks = connectivityManager.getAllNetworks();
NetworkInfo networkInfo;
for (Network network : networks) {
networkInfo = connectivityManager.getNetworkInfo(network);
if (NetworkInfo.State.CONNECTED.equals(networkInfo.getState())) {
return true;
}
}
} else {
if (connectivityManager != null) {
NetworkInfo[] networkInfo = connectivityManager.getAllNetworkInfo();
if (networkInfo != null && networkInfo.length > 0) {
for (int i = 0; i < networkInfo.length; i++) {
// 判断当前网络状态是否为连接状态
if (networkInfo[i].getState() == NetworkInfo.State.CONNECTED) {
return true;
}
}
}
}
}
return false;
}
}
关于切入点表达式, 详细的可以参考 切入点表达式 我就不在这里丢人现眼了. (Android 中使用这个表达式好像在 API 开发的时候使用有点不同, 好像是这样.)
- 使用.
@CheckNet
public void test(View view) {
Intent intent = new Intent(this,MainActivity.class);
startActivity(intent);
}
简单粗暴. 在切面类中中使用的是环绕通知, 其实还有其他通知, 例如前置通知, 后置通知, 返回通知等. 但是一般都会使用环绕通知, 因为如果使用后面几个的话顺序会错乱. 剩下的分别为
-
@Before
前置通知 -
@After
后置通知 -
@AfterReturning
返回通知, 返回通知可以获取到返回值, 可以这样写@AfterReturning(value = "pointCut()",returning = "result") public void logReturn(JoinPoint joinPoint,Object result){ System.out.println("@AfterReturning_返回结果为{"+result+"}"); }
-
@AfterThrowing
异常通知.写法为@AfterThrowing(value = "pointCut()",throwing = "exception") public void logException(Exception exception){ System.out.println("@AfterThrowing_异常信息,{"+exception+"}"); }
还有更多的 AOP 使用方式等待我们发现, 并且使用的话, 不需要担心性能问题, 因为使用后就是使用 Aspectj
的编译器, class
文件由 Aspect
去编译, 会将注解的代码 copy
到 class
文件. 类似于在编译的时候, 将我们网络判断的那段代码 Copy
到了跳转的地方, 有兴趣的同学可以运行后反编译看一下.
文末分享一位大神总结的关于 AOP 的一些常见的用法. 归纳AOP在Android开发中的几种常见用法