Android面向切面编程(AOP)

WHY

如果说OOP(面向对象的程序设计)的主要思想是将问题归分到相应的对象(类)中去实现,再把相关类模块化使得模块内聚、模块间低耦。正是因为高度的模块化使得每个模块处理类似需求的时候,很容易造成冗余,分散的情况

AOP举个栗子

那么AOP面向切面编程(Aspect-Oriented Programming)就是把涉及到众多模块的某一类问题进行统一管理,因为AOP是方法论,你可以用任何的方式按照这个思想去实现。
那么问题来了:怎么样不需要显示的去修改原来的业务逻辑,最少的改动插入代码,去优雅的实现AOP切面。

WHO

AOP从实现原理上可以分为运行时AOP编译时AOP,对于Android来讲运行时AOP的实现主要是hook某些关键方法,编译时AOP主要是在Apk打包过程中对java,class,dex文件进行扫描更改。
Android主流的aop 框架和工具类库:

  • AspectJ: 一个 JavaTM 语言的面向切面编程的无缝扩展
  • Javassist for Android: 用于字节码操作的知名 java 类库 Javassist 的 Android 平台移植版。
  • DexMaker: Dalvik 虚拟机上,在编译期或者运行时生成代码的 Java API。
  • ASMDEX: 一个类似 ASM 的字节码操作库,运行在Android平台,操作Dex字节码。
  • APT: 通过Android-apt的Gradle插件(官方),以及jre原生代码在编译器生成Java文件

本文主要探究一下通过预编译方式和运行期动态代理在Android中的AOP实现方式,包括***APT,AspectJ ***


其实APT,AspectJ,Javassist正好对应的是Android不通编译期,进行代码或者字节码操作的,因为实现的方式,解决的问题也有不同

APT,AspectJ,Javassist的运行操作时机

其实通俗来说,
APT就是单独用一个Java的module来生成app内的java代码
AspectJ就是提供了很多横切关注点来在编译class文件的时候动态来修改你的java的代码

HOW

APT

代表框架:DataBinding,Dagger2, ButterKnife, EventBus3 、AndroidAnnotation等
注解处理器 Java5 中叫APT(Annotation Processing Tool),在Java6开始,规范化为 Pluggable Annotation Processing。Apt应该是这其中我们最常见到的了,难度也最低。定义编译期的注解,再通过继承Proccesor实现代码生成逻辑,实现了编译期生成代码的逻辑。

Talk is cheap. Show me your code .

下面我来用APT实现一个简单的全局路由来演示一下APT的工作原理

  • 1.配置
    使用官方gradle插件(如果之前有使用框架用到了android-apt插件的最好去掉,原作者不再维护了,而且两个插件兼容会有问题)
工程中的module依赖.png

appmodule的gradle配置,工程依赖apt入口

dependencies {
    annotationProcessor project(':apt')
    compile project(':apt-lib')
}

创建一个apt-libmodule用来提供路由需要的注解
gradle配置

apply plugin: 'java'
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
// 解决build警告:编码GBK的不可映射字符
tasks.withType(JavaCompile) {
    options.encoding = "UTF-8"
}

全局路由注解类

@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface Rounter {
    String value();
}

路由参数注解

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface Extra {
    String value();
}

创建一个aptmodule用来负责编译期生成java代码
gradle配置

apply plugin: 'java'
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
dependencies {
    compile 'com.google.auto.service:auto-service:1.0-rc2'
    compile 'com.squareup:javapoet:1.7.0'
    compile project(':lib')
}
//  解决build警告:编码GBK的不可映射字符
tasks.withType(JavaCompile) {
    options.encoding = "UTF-8"
}

com.google.auto.service:auto-service:1.0-rc2
谷歌提供的Java 生成源代码库
com.squareup:javapoet:1.7.0
提供了各种 API 让你用各种姿势去生成 Java 代码文件

  • 2.AnnotationProcessor
@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_8)//java版本支持
@SupportedAnnotationTypes({//标注注解处理器支持的注解类型
        "com.app.annotation.apt.Rounter"})
public class AnnotationProcessor extends AbstractProcessor {
    public Filer mFiler; //文件相关的辅助类
    public Elements mElements; //元素相关的辅助类
    public Messager mMessager; //日志相关的辅助类

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        mFiler = processingEnv.getFiler();
        mElements = processingEnv.getElementUtils();
        mMessager = processingEnv.getMessager();
        new RouterProcessor().process(roundEnv, this);
        return true;
    }
}

@AutoService(Processor.class)指定了AnnotationProcessor类为Processor的入口process方法相当于java的main()方法
动态生成路由类TRouterRouterProcessor

public class RouterProcessor implements IProcessor {
    @Override
    public void process(RoundEnvironment roundEnv, AnnotationProcessor mAbstractProcessor) {
        String CLASS_NAME = "TRouter";
        TypeSpec.Builder tb = classBuilder(CLASS_NAME).addModifiers(PUBLIC, FINAL).addJavadoc("@ 全局路由器 此类由apt自动生成");

        FieldSpec extraField = FieldSpec.builder(ParameterizedTypeName.get(DataMap.class), "mCurActivityExtra")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .build();
        tb.addField(extraField);

        MethodSpec.Builder methodBuilder1 = MethodSpec.methodBuilder("go")
                .addJavadoc("@此方法由apt自动生成")
                .addModifiers(PUBLIC, STATIC)
                .addParameter(String.class, "name").addParameter(DataMap.class, "extra")
                .addParameter(ClassName.get("android.view", "View"), "view");

        MethodSpec.Builder methodBuilder2 = MethodSpec.methodBuilder("bind")
                .addJavadoc("@此方法由apt自动生成")
                .addModifiers(PUBLIC, STATIC)
                .addParameter(ClassName.get("android.app", "Activity"), "mContext");

        List<ClassName> mList = new ArrayList<>();
        CodeBlock.Builder blockBuilderGo = CodeBlock.builder();
        CodeBlock.Builder blockBuilderBind = CodeBlock.builder();
        ClassName appClassName = ClassName.get("com.wingjay.jianshi.global", "ActivityStackManager");
        blockBuilderGo.addStatement("mCurActivityExtra=extra");
        blockBuilderGo.addStatement("Activity mContext=$T.getInstance().getTopActivity()", appClassName);
        blockBuilderGo.beginControlFlow(" switch (name)");//括号开始

        blockBuilderBind.add(" if (mCurActivityExtra == null) return;\n");
        blockBuilderBind.beginControlFlow(" switch (mContext.getClass().getSimpleName())");//括号开始

        List<RouterActivityModel> mRouterActivityModels = new ArrayList<>();
        try {
            for (TypeElement element : ElementFilter.typesIn(roundEnv.getElementsAnnotatedWith(Rounter.class))) {
                ClassName currentType = ClassName.get(element);
                if (mList.contains(currentType)) continue;
                mList.add(currentType);
                RouterActivityModel mRouterActivityModel = new RouterActivityModel();
                mRouterActivityModel.setElement(element);
                mRouterActivityModel.setActionName(element.getAnnotation(Rounter.class).value());
                List<Element> mExtraElements = new ArrayList<>();
                List<String> mExtraElementKeys = new ArrayList<>();
                for (Element childElement : element.getEnclosedElements()) {
                    SceneTransition mSceneTransitionAnnotation = childElement.getAnnotation(SceneTransition.class);
                    if (mSceneTransitionAnnotation != null) {
                        mRouterActivityModel.setSceneTransitionElementName(mSceneTransitionAnnotation.value());
                        mRouterActivityModel.setSceneTransitionElement(childElement);
                    }
                    Extra mExtraAnnotation = childElement.getAnnotation(Extra.class);
                    if (mExtraAnnotation != null) {
                        mExtraElementKeys.add(mExtraAnnotation.value());
                        mExtraElements.add(childElement);
                    }
                }
                mRouterActivityModel.setExtraElementKeys(mExtraElementKeys);
                mRouterActivityModel.setExtraElements(mExtraElements);
                boolean isNeedBind = (mExtraElementKeys != null && mExtraElementKeys.size() > 0
                        || mRouterActivityModel.getSceneTransitionElement() != null);
                mRouterActivityModel.setNeedBind(isNeedBind);
                mRouterActivityModels.add(mRouterActivityModel);
            }
            ClassName mActivityCompatName = ClassName.get("android.support.v4.app", "ActivityCompat");
            ClassName mIntentClassName = ClassName.get("android.content", "Intent");
            ClassName mActivityOptionsCompatName = ClassName.get("android.support.v4.app", "ActivityOptionsCompat");
            for (RouterActivityModel item : mRouterActivityModels) {
                blockBuilderGo.add("case $S: \n", item.getActionName());//1
                if (item.isNeedBind())
                    blockBuilderBind.add("case $S: \n", item.getElement().getSimpleName());//1
                if (item.getExtraElements() != null && item.getExtraElements().size() > 0) {
                    for (int i = 0; i < item.getExtraElements().size(); i++) {
                        Element mFiled = item.getExtraElements().get(i);
                        blockBuilderBind.add("(($T)mContext)." +//1
                                        "$L" +//2
                                        "= ($T) " +//3
                                        "mCurActivityExtra.get(" +//4
                                        "$S);\n",//5
                                item.getElement(),//1
                                mFiled,//2
                                mFiled,//3
                                item.getExtraElementKeys().get(i)//5
                        );//5
                    }
                }
                if (item.getSceneTransitionElement() != null) {
                    blockBuilderGo.add("$L.startActivity(mContext," +//2
                                    "\nnew $L(mContext," +//3
                                    "\n$L.class)," +//4
                                    "\n$T.makeSceneTransitionAnimation(" +//5
                                    "\nmContext,view," +//6
                                    "\n$S).toBundle());", //7
                            mActivityCompatName,//2
                            mIntentClassName,//3
                            item.getElement(),//4
                            mActivityOptionsCompatName,//5
                            item.getSceneTransitionElementName());//6

                    blockBuilderBind.add(
                            "$T.setTransitionName(" +//2
                                    "(($T)mContext).mViewBinding." +//3
                                    "$L, " +//4
                                    "$S);\n",//5
                            ClassName.get("android.support.v4.view", "ViewCompat"),//2
                            item.getElement(),//3
                            item.getSceneTransitionElement(),//4
                            item.getSceneTransitionElementName());//5
                } else {
                    blockBuilderGo.add("mContext.startActivity(" +//2
                                    "\nnew $L(mContext," +//3
                                    "\n$L.class));", //7
                            mIntentClassName,//3
                            item.getElement()//4
                    );
                }
                blockBuilderGo.addStatement("\nbreak");//1
                if (item.isNeedBind()) blockBuilderBind.addStatement("break");//1
            }
            blockBuilderGo.addStatement("default: break");
            blockBuilderGo.endControlFlow();
            methodBuilder1.addCode(blockBuilderGo.build());
            blockBuilderBind.addStatement("default: break");
            blockBuilderBind.endControlFlow();
            methodBuilder2.addCode(blockBuilderBind.build());

            tb.addMethod(methodBuilder1.build());
            tb.addMethod(methodBuilder2.build());

            //增加go(action)和go(action,extra):两个重载方法
            tb.addMethod(MethodSpec.methodBuilder("go")
                    .addJavadoc("@此方法由apt自动生成")
                    .addModifiers(PUBLIC, STATIC)
                    .addParameter(String.class, "name")
                    .addParameter(DataMap.class, "extra")
                    .addCode("go(name,extra,null);\n").build());

            tb.addMethod(MethodSpec.methodBuilder("go")
                    .addJavadoc("@此方法由apt自动生成")
                    .addModifiers(PUBLIC, STATIC)
                    .addParameter(String.class, "name")
                    .addCode("go(name,null,null);\n").build());

            tb.addMethod(MethodSpec.methodBuilder("getMap")
                    .returns(DataMap.class)
                    .addJavadoc("@此方法由apt自动生成")
                    .addModifiers(PUBLIC, STATIC)
                    .addCode("return new DataMap();\n").build());

            JavaFile javaFile = JavaFile.builder(AptConstants.PackageName, tb.build()).build();// 生成源代码
            javaFile.writeTo(mAbstractProcessor.mFiler);// 在 app module/build/generated/source/apt 生成一份源代码
        } catch (FilerException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

看结尾javaFile.writeTo(mAbstractProcessor.mFiler),
OK这个类就是使用了com.squareup:javapoet提供的一些很方便写.java文件的类和方法来生成最终的TRounter.java

  • 3.使用路由
    下面就可以在原来的项目中使用路由了
@Rounter(RounterConstants.MAIN)
public class MainActivity extends BaseActivity {
...
}
@Rounter(RounterConstants.EDIT)
public class EditActivity extends BaseActivity {
    @Extra(DataMap.DATA_HEAD)
    public String diaryUUID;
...
}

路由跳转

       TRouter.go(RounterConstants.MAIN);

       TRouter.go(RounterConstants.EDIT, TRouter.getMap().addHead(diary.getUuid()));

那么生成的这个TRounter在哪呢,可以在\build\generated\source\apt\debug中相应的包名下查看apt生成的代码,如下

/**
 * @ 全局路由器 此类由apt自动生成 */
public final class TRouter {
  public static DataMap mCurActivityExtra;

  /**
   * @此方法由apt自动生成 */
  public static void go(String name, DataMap extra, View view) {
    mCurActivityExtra=extra;
    Activity mContext=ActivityStackManager.getInstance().getTopActivity();
     switch (name) {
      case "rounter_diary": 
      mContext.startActivity(
      new android.content.Intent(mContext,
      com.wingjay.jianshi.ui.DiaryListActivity.class));
          break;
      case "rounter_edit": 
      mContext.startActivity(
      new android.content.Intent(mContext,
      com.wingjay.jianshi.ui.EditActivity.class));
          break;
      case "rounter_main": 
      mContext.startActivity(
      new android.content.Intent(mContext,
      com.wingjay.jianshi.ui.MainActivity.class));
          break;
      case "rounter_setting": 
      mContext.startActivity(
      new android.content.Intent(mContext,
      com.wingjay.jianshi.ui.SettingActivity.class));
          break;
      case "rounter_signup": 
      mContext.startActivity(
      new android.content.Intent(mContext,
      com.wingjay.jianshi.ui.SignupActivity.class));
          break;
      case "rounter_view": 
      mContext.startActivity(
      new android.content.Intent(mContext,
      com.wingjay.jianshi.ui.ViewActivity.class));
          break;
      default: break;
    }
  }

  /**
   * @此方法由apt自动生成 */
  public static void bind(Activity mContext) {
     if (mCurActivityExtra == null) return;
     switch (mContext.getClass().getSimpleName()) {
      case "EditActivity": 
      ((EditActivity)mContext).diaryUUID= (String) mCurActivityExtra.get("data_head");
      break;
      case "ViewActivity": 
      ((ViewActivity)mContext).diaryUuid= (String) mCurActivityExtra.get("data_head");
      break;
      default: break;
    }
  }

  /**
   * @此方法由apt自动生成 */
  public static void go(String name, DataMap extra) {
    go(name,extra,null);
  }

  /**
   * @此方法由apt自动生成 */
  public static void go(String name) {
    go(name,null,null);
  }

  /**
   * @此方法由apt自动生成 */
  public static DataMap getMap() {
    return new DataMap();
  }
}

好了如上就简单可以用APT实现一个简单的全局路由了,如果不是开发框架的团队的话,我认为APT在搭建团队工程的框架方面还是有很大的利用场景的,比方说绑定model和View和present,比方说生成一些约定的接口或者方法


End of APT


AspectJ

代表框架:我就只知道JakeWharton/hugo了 = =||

如果说APT还很难体现AOP的概念的话,那么AspectJ就是先行者特别为AOP开发的一套语言支持了。它是一种几乎和Java完全一样的语言,或者说AspectJ应该就是一种扩展Java。当然,除了使用AspectJ特殊的语言外,AspectJ还支持原生的Java,只要加上对应的AspectJ注解就好。
AspectJ现在托管于Eclipse项目中
|【这是官方网站】
|【这是参考文档】
|【这是接口文档】

主要术语

  • Cross-cutting concerns(横切关注点): 尽管面向对象模型中大多数类会实现单一特定的功能,但通常也会开放一些通用的附属功能给其他类。例如,我们希望在数据访问层中的类中添加日志,同时也希望当UI层中一个线程进入或者退出调用一个方法时添加日志。尽管每个类都有一个区别于其他类的主要功能,但在代码里,仍然经常需要添加一些相同的附属功能。
  • Advice(通知): 注入到class文件中的代码。典型的 Advice 类型有 before、after 和 around,分别表示在目标方法执行之前、执行后和完全替代目标方法执行的代码。 除了在方法中注入代码,也可能会对代码做其他修改,比如在一个class中增加字段或者接口。
  • Joint point(连接点): 程序中可能作为代码注入目标的特定的点,例如一个方法调用或者方法入口。
  • Pointcut(切入点): 告诉代码注入工具,在何处注入一段特定代码的表达式。例如,在哪些 joint points 应用一个特定的 Advice。切入点可以选择唯一一个,比如执行某一个方法,也可以有多个选择,比如,标记了一个定义成@DebguTrace 的自定义注解的所有方法。
  • Aspect(切面): Pointcut 和 Advice 的组合看做切面。例如,我们在应用中通过定义一个 pointcut 和给定恰当的advice,添加一个日志切面。
  • Weaving(织入): 注入代码(advices)到目标位置(joint points)的过程。
    整体原理就如下图:
    30689-55846998f4f5b4ce.png
  • 其实我的理解就是AspectJ提供了各种各样的切入点(关注点/代码插入点),在生成class文件的时候可以动态替换你原来的代码或者插入你想要的代码

传送门:想更多了解AspectJ语法的

举几个栗子

  • 性能监控切片
  • 用户登录切片
  • 防止频繁点击
  • 用户行为路径

环境配置
项目的Gradle

  dependencies {
    classpath 'com.android.tools.build:gradle:0.12.+'
    classpath 'org.aspectj:aspectjtools:1.8.1'
  }

app的Gradle

import com.app.plugin.AspectjPlugin
dependencies {
    compile 'org.aspectj:aspectjrt:1.8.9'
 }

性能监控切片
性能监控的注解

@Retention(RetentionPolicy.CLASS)//这个注解周期声明在 class 文件上
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})//可以注解构造函数和方法
public @interface MonitorLog {
}

切片实现类,如果看不懂的可以去上面看下语法

@Aspect
public class MonitorAspect {

    @Pointcut("execution(@com.zzj.jianshi.aspect.annotation.MonitorLog * *(..))")//方法切入点
    public void methodAnnotated() {
    }

    @Pointcut("execution(@com.zzj.jianshi.aspect.annotation.MonitorLog *.new(..))")//构造器切入点
    public void constructorAnnotated() {
    }

    @Around("methodAnnotated() || constructorAnnotated()")//在连接点进行方法替换
    public Object aroundJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
        if (!isMonitorOpened()) {//后台下发监控开关
            return joinPoint.proceed();
        }

        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String className = methodSignature.getDeclaringType().getSimpleName();
        String methodName = methodSignature.getName();
        long startTime = System.nanoTime();
        Object result = joinPoint.proceed();//执行原方法
        StringBuilder keyBuilder = new StringBuilder();
        keyBuilder.append(methodName + ":");
        for (Object obj : joinPoint.getArgs()) {
            if (obj instanceof String) keyBuilder.append((String) obj);
            else if (obj instanceof Class) keyBuilder.append(((Class) obj).getSimpleName());
        }
        String key = keyBuilder.toString();
        Timber.e("MonitorAspect --->: " + className + "." + key + joinPoint.getArgs().toString() +
                " --->:" + "[" + (TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)) + "ms]");// 记录时间差
        return result;
    }
}

业务代码中,在耗时操作或者关键方法中添加注解,如下:

    @Override
    @MonitorLog
    protected void onStart() {

可以看下日志输出

06-08 15:49:02.457 729-729/? E/MonitorAspect: MonitorAspect --->: JianShiApplication.initLog:[Ljava.lang.Object;@c567935 --->:[2ms]
06-08 15:49:02.465 729-729/? E/MonitorAspect: MonitorAspect --->: JianShiApplication.onCreate:[Ljava.lang.Object;@3a1d85d --->:[53ms]
06-08 15:49:03.684 729-729/com.wingjay.android.jianshi E/MonitorAspect: MonitorAspect --->: MainActivity.onCreate:[Ljava.lang.Object;@64c8c14 --->:[369ms]
06-08 15:49:03.816 729-729/com.wingjay.android.jianshi E/MonitorAspect: MonitorAspect --->: MainActivity.onStart:[Ljava.lang.Object;@970c60a --->:[130ms]
06-08 15:50:20.288 729-729/com.wingjay.android.jianshi E/MonitorAspect: MonitorAspect --->: MainActivity.onStart:[Ljava.lang.Object;@efb02be --->:[2ms]

用户登录切片

@Aspect
public class CheckLoginAspect {

    @Pointcut("execution(@com.app.annotation.aspect.CheckLogin * *(..))")//方法切入点
    public void methodAnnotated() {
    }

    @Around("methodAnnotated()")//在连接点进行方法替换
    public void aroundJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
        if (null == SpUtil.getUser()) {
            Snackbar.make(App.getAppContext().getCurActivity().getWindow().getDecorView(), "请先登录!", Snackbar.LENGTH_LONG)
                    .setAction("登录", new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            TRouter.go(C.LOGIN);//跳转到登录页面
                        }
                    }).show();
            return;
        }
        joinPoint.proceed();//执行原方法
    }
}

防止频繁点击

@Aspect
public class SingleClickAspect {
    static int TIME_TAG = R.id.click_time;
    public static final int MIN_CLICK_DELAY_TIME = 600;

    @Pointcut("execution(@com.app.annotation.aspect.SingleClick * *(..))")//方法切入点
    public void methodAnnotated() {
    }

    @Around("methodAnnotated()")//在连接点进行方法替换
    public void aroundJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
        View view = null;
        for (Object arg : joinPoint.getArgs())
            if (arg instanceof View) view = (View) arg;
        if (view != null) {
            Object tag = view.getTag(TIME_TAG);
            long lastClickTime = ((tag != null) ? (long) tag : 0);
            LogUtils.showLog("SingleClickAspect", "lastClickTime:" + lastClickTime);
            long currentTime = Calendar.getInstance().getTimeInMillis();
            if (currentTime - lastClickTime > MIN_CLICK_DELAY_TIME) {//过滤掉600毫秒内的连续点击
                view.setTag(TIME_TAG, currentTime);
                LogUtils.showLog("SingleClickAspect", "currentTime:" + currentTime);
                joinPoint.proceed();//执行原方法
            }
        }
    }
}

OK,用户行为路径什么的我就没有实现了,是用来装逼的

那么问题来了,说好的AspectJ执行之后修改的class代码去哪里了?
我们来看\build\intermediates\classes下面对应的class的原来代码变了么?

源代码

        @Override
    protected void onPause() {
        super.onPause();
        Timber.e("befor testAspect() ");
        testAspect();
        Timber.e("after testAspect() ");
    }

    @MonitorLog
    public void testAspect() {
        Timber.e("testAspect() inn start");
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Timber.e("testAspect() inn end");
    }

\build\intermediates\classes下面

    protected void onPause() {
        super.onPause();
        Timber.e("testAspect() befor", new Object[0]);
        this.testAspect();
        Timber.e("testAspect() after", new Object[0]);
    }

    @MonitorLog
    public void testAspect() {
        JoinPoint var2 = Factory.makeJP(ajc$tjp_2, this, this);
        MonitorAspect var10000 = MonitorAspect.aspectOf();
        Object[] var3 = new Object[]{this, var2};
        var10000.aroundJoinPoint((new MainActivity$AjcClosure5(var3)).linkClosureAndJoinPoint(69648));
    }

为什么要加日志?没错我就是想看一下AspectJ切片对原逻辑性能是否有影响,我相信这个对于加入项目的选型可能是致命的,下面来看我实际测试的日志

06-08 16:23:33.895 31713-31713/ E/MainActivity: befor testAspect() 
06-08 16:23:33.895 31713-31713/ E/MonitorAspect: MonitorAspect start
06-08 16:23:33.895 31713-31713/ E/MainActivity: testAspect() inn start
06-08 16:23:33.997 31713-31713/ E/MainActivity: testAspect() inn end
06-08 16:23:33.997 31713-31713/ E/MonitorAspect: MonitorAspect --->: MainActivity.testAspect:[Ljava.lang.Object;@7f6e61b --->:[101ms]
06-08 16:23:33.998 31713-31713/ E/MonitorAspect: MonitorAspect end
06-08 16:23:33.998 31713-31713/ E/MainActivity: after testAspect() 

这就说明在编译器做的代理性能还是非常高的
可以说AspectJ是面向切面编程最好的切入点了

相关代码传送门

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 164,121评论 24 696
  • 一、简述 1、AOP的概念 如果你用java做过后台开发,那么你一定知道AOP这个概念。如果不知道也无妨,套用百度...
    GitLqr阅读 3,187评论 6 24
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 128,934评论 18 137
  • 1. AOP与OOP的区别 平时我接触多的就是OOP(Object Oriented Programming面向对...
    生椰拿铁锤阅读 2,026评论 3 22
  • 汽车从马路驶过鸟儿从枝头飞过船从港口飘过鱼在礁石的边上游过 窗口后的眼睛香炉里信仰的灰烬楼梯口静默的高跟鞋午夜里幽...
    刘汉皇阅读 87评论 1 2
  • 【幼儿说】原创,转载请标出处 下面是一个简单的心理小测试。 假设情景: 如果有一天晚上,你因为临时加班很晚才回家,...
    幼儿说阅读 268评论 0 0
  • ‘’这不是个坏答案,但是我没有一个好的答案‘’,两个答案都不错,但是取其中一个,另一个就会被搁置,这时管理者需要平...
    台一DDM路静娟阅读 124评论 0 0
  • 我经常在课堂上爱说一句话:“你所拥有的一切都是你自身的反映,是你自己选择的结果。”也就是说,你追求什么,说明你的信...
    马石头阅读 125评论 0 3
  • 编写java程序如下; 以上程序在运行时正常输出,但在将遍历的文件目录改为根盘符,比如 扫描出一部分文件后出现报错...
    扒块腹肌阅读 1,457评论 0 0