Android 面向切面编程 AOP
AOP是Aspect Oriented Programming的缩写,即『面向切面编程』。 优势:无侵入
比如我希望在所有页面启动的时候加一个埋点~ 希望在所有按钮点击的时候加个快速重复点击的判断~等等 这样在项目中同一种类型的所有代码处,
统一加入逻辑处理的方法,却不需要改动原有代码,叫做 面向切面编程 AOP
Android实现AOP,可以使用的方案主要有两个(都是基于 aspectJ 的,所以语法都一致):
一个是大神的 github.com/JakeWharton… (不支持)
一个是沪江的 https://github.com/HujiangTechnology/gradle_plugin_android_aspectjx(支持kotlin 依赖方式 github上很详细)
添加依赖:
可参考:https://www.jianshu.com/p/c055df92790c (使用的沪江的)
语法说明:
更详细的说明:
AOP中AspectJ的一些使用说明
例子:
先定义个切面类来拦截点击事件:
import com.yitkeji.wukongkabao.utils.FastClickUtils; import com.yitkeji.wukongkabao.utils.LogUtil; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; @Aspect //标注AOP类,表示该类里面是处理切面代码的,固定写法 public class FastClickBlockAspect { /** Around注解 说明见上图 * * 表示匹配任意内容 * execution。方法被调用。 * .. 表示匹配任意类型任意数量内容 */ @Around("execution(* android.view.View.OnClickListener.onClick(..))") public void onClick(ProceedingJoinPoint joinPoint) { LogUtil.e("拦截到onClick方法执行"); try { if(FastClickUtils.isNoFastClick()){ LogUtil.e("没有快速点击,继续执行源代码"); joinPoint.proceed();//继续往下面走,就会执行拦截的方法,这里为 onClick }else { //快速点击了 不做处理 LogUtil.e("快速点击了 不做处理"); } } catch (Throwable throwable) { throwable.printStackTrace(); } LogUtil.e("拦截结束"); } }
activity中点击事件:
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test); findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { LogUtil.e("点击了"); } }); }
当点击按钮时,打印日志:
单机 时 连续点击 时
要注意一点around和after两种类型是有冲突的,around和before可以共存,
所以还是建议两种方式,一种before和after配合使用,一种around单独使用。
package com.example.mytest.aop; import android.util.Log; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; /** * 要注意一点around和after两种类型是有冲突的,around和before可以共存, * 所以还是建议两种方式,一种before和after配合使用,一种around单独使用。 * * 方法参数的获取 * 自定义注解参数的获取 * * https://blog.csdn.net/niubitianping/article/details/78492054?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-4&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-4 */ @Aspect public class CheckNetworkAspect { /** * 对切点的定义 */ //@Pointcut("execution(* android.view.View.OnClickListener.onClick(..))") public void pointcut() { } //@Before("pointcut()") public void before(JoinPoint point) { Log.e("tag", "@Before"); } //@After("pointcut()") public void after(JoinPoint point) { Log.e("tag", "@After"); } /** * 切点方法返回结果之后执行 * * @param point * @param returnValue */ //@AfterReturning("pointcut()") public void afterReturning(JoinPoint point, Object returnValue) { Log.e("tag", "@AfterReturning returnValue = " + returnValue); } /** * 切点表达式: execution(<修饰符模式>? <返回类型模式> <方法名模式>(<参数模式>) <异常模式>?) * Around注解 说明见上图 * * 表示匹配任意内容 * execution。方法被调用。 * .. 表示匹配任意类型任意数量内容 */ @Around("execution(* android.view.View.OnClickListener.onClick(..))") public void onClick(ProceedingJoinPoint joinPoint) { Log.e("tag", "拦截到方法前执行"); MethodSignature signature = (MethodSignature) joinPoint.getSignature(); String name = signature.getName(); // 方法名:test Method method = signature.getMethod(); // 方法:public final void com.example.mytest.aop.AopTestActivity$onCreate$1.onClick(android.view.View) Class returnType = signature.getReturnType(); // 返回值类型:void Class declaringType = signature.getDeclaringType(); // 方法所在类名:MainActivity String[] parameterNames = signature.getParameterNames(); // 参数名:view Class[] parameterTypes = signature.getParameterTypes(); // 参数类型:View List<Object> args = Arrays.asList(joinPoint.getArgs());//方法参数 Log.e("tag", "参数 = " + args); try { joinPoint.proceed();//继续往下面走,就会执行拦截的方法,这里为 onClick } catch (Throwable throwable) { throwable.printStackTrace(); } Log.e("tag", "拦截到方法后执行"); } @Pointcut("execution(@com.example.mytest.aop.CheckNetwork * *(..))") public void executionCheckNetwork() { } @Around("executionCheckNetwork() && @annotation(checkNetwork)") public Object afterReturningTest(ProceedingJoinPoint point, CheckNetwork checkNetwork) throws Throwable { Log.e("tag", "@AfterReturning checkNetwork = " + checkNetwork.value()); Object result = point.proceed();//被拦截的方法执行后的返回值 无返回值时 为null Log.e("tag", "返回值 = " + result); return result; } }
可参考:
Android 面向切面编程 AOP 解决连续点击打开重复页面问题 (包含语法说明)
防止按钮连续点击 (包含自定义Pointcuts可以让我们更加精确的切入一个或多个指定的切入点)
第三方(实际项目例子):https://github.com/AICareless/AopArms