Android 基于AOP监控之——AspectJ构建指南

简介: ##如何使用## 使用AspectJ做AOP可以做一些非侵入的AOP监控操作,方便简洁,功能强大,而且对目标工程没有侵入性,可以做切面的操作:监听方法耗时、输出日志、控制初入参数、进行运行时修改等等操作。 在Eclipse中已经有AJDT插件集成了AspectJ编译器的使用和关键字的声明。但

如何使用

使用AspectJ做AOP可以做一些非侵入的AOP监控操作,方便简洁,功能强大,而且对目标工程没有侵入性,可以做切面的操作:监听方法耗时、输出日志、控制初入参数、进行运行时修改等等操作。

在Eclipse中已经有AJDT插件集成了AspectJ编译器的使用和关键字的声明。但是在Android Studio中没有这样的官方插件。因此,这里讲一下如何在Android Studio中使用AspectJ,来实现非侵入式的AOP监控。详细了解AspectJ的使用我在另一篇文章写了——Android基于AOP的非侵入式监控

AspectJ的使用核心就是它的编译器,它就做了一件事,将AspectJ的代码在编译期插入目标程序当中,运行时跟在其它地方没什么两样,因此要使用它最关键的就是使用它的编译器去编译代码ajc。ajc会构建目标程序与AspectJ代码的联系,在编译期将AspectJ代码插入被切出的PointCut中,已达到AOP的目的。
因此,无论在什么IDE上(如果使用命令行就可以直接使用ajc编译了),问题就是让IDE使用ajc作为编译器编译代码。

使用方法

1、插件:网上有人在github上提供了集成的插件gradle-android-aspectj-plugin,一开始我也是用的这个,但是在项目当中,无法兼容databinding,这个问题现在作者依然没有解决。

2、Gradle配置:通过在Gradle构建脚本中,定义任务来使得项目执行ajc编译,将AOP 的Module编织进入目标工程中,达到非侵入式AOP的目的。

下面就介绍一下第二种方法的具体的使用步骤。

Step

1、创建AS原工程

这里写图片描述

2、创建module(Android Library),然后添加AspectJ依赖至module中。

compile 'org.aspectj:aspectjrt:1.8.9'

3、编写build脚本,添加任务,使得IDE使用ajc作为编译器编译代码。

这里,分别对module、app的构建脚本添加一些任务,使得IDE使用ajc编译代码。

build.gradle(app):

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'
    }
}

apply plugin: 'com.android.application'

repositories {
    mavenCentral()
}

dependencies {
    compile project(':aspectjlibrary')
}

android {
    compileSdkVersion 21
    buildToolsVersion '22.0.1'

    buildTypes {
        debug {
            minifyEnabled false  // should disable proguard in debug builds
        }
    }
    defaultConfig {
        applicationId "com.example.lingyimly.try2"
        minSdkVersion 15
        targetSdkVersion 21

    }

    lintOptions {
        abortOnError true
    }

}


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;
            }
        }
    }
}


build.gradle(module):

import com.android.build.gradle.LibraryPlugin
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main

apply plugin: 'com.android.library'
buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.1.0'
        classpath 'org.aspectj:aspectjtools:1.8.9'
        classpath 'org.aspectj:aspectjweaver:1.8.9'
    }
}


repositories {
    mavenCentral()
}
dependencies {
    compile 'org.aspectj:aspectjrt:1.8.9'
    compile 'com.android.support:appcompat-v7:22.2.1'
}
android {
    compileSdkVersion 22
    buildToolsVersion '23.0.1'

    lintOptions {
        abortOnError false
    }

}

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", plugin.project.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;
            }
        }
    }
}

4、编写AspectJ切面程序代码

编写切面程序,做切面的操作:监听方法耗时、输出日志、控制初入参数、进行运行时修改

/**
 * 截获类名最后含有Activity、Layout的类的所有非static方法(static方法另外加一个static修饰的execution或者call即可:execution( static * *..Activity+.*(..))
 * 监听目标方法的执行时间
 */
@Aspect
public class TraceAspect {
  private static Object currentObject = null;
    //截获所有后缀为Activity或者Layout的类中所有方法的执行体(除了static,要监听static需要重新加一个static的execution 规则)
    //target、this是用于截获运行时类型,便于做一些入参、出参的修改,或者做其他操作
  private static final String POINTCUT_METHOD =
      "(execution(* *..Activity+.*(..)) ||execution(* *..Layout+.*(..))) && target(Object) && this(Object)";

    //截获所有后缀为Activity或者Layout的类中所有方法的调用(除了static,要监听static需要重新加一个static的execution 规则)
    //target、this是用于截获运行时类型,便于做一些入参、出参的修改,或者做其他操作
    private static final String POINTCUT_CALL = "(call(* *..Activity+.*(..)) || call(* *..Layout+.*(..))) && target(Object) && this(Object)";
  @Pointcut(POINTCUT_METHOD)
  public void methodAnnotated() {}
  @Pointcut(POINTCUT_CALL)
  public void methodCall(){}

    /**
     *  截获原方法,并替换
     * @param joinPoint
     * @return
     * @throws Throwable
     */
  @Around("methodAnnotated()")
  public Object weaveJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
    if (currentObject == null){
        currentObject = joinPoint.getTarget();
    }
      //初始化计时器
    final StopWatch stopWatch = new StopWatch();
      //开始监听
      stopWatch.start();
      //调用原方法的执行。
    Object result = joinPoint.proceed();
      //监听结束
    stopWatch.stop();
      //获取方法信息对象
      MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
      String className;
      //获取当前对象,通过反射获取类别详细信息
      className = joinPoint.getThis().getClass().getName();

      String methodName = methodSignature.getName();
      String msg =  buildLogMessage(methodName, stopWatch.getTotalTime(1));
    if (currentObject != null && currentObject.equals(joinPoint.getTarget())){
        DebugLog.log(new MethodMsg(className,msg,stopWatch.getTotalTime(1)));
    }else if(currentObject != null && !currentObject.equals(joinPoint.getTarget())){
        DebugLog.log(new MethodMsg(className, msg,stopWatch.getTotalTime(1)));
        Log.e(className,msg);
        currentObject = joinPoint.getTarget();
//        DebugLog.outPut(new Path());    //日志存储
//        DebugLog.ReadIn(new Path());    //日志读取
    }
    return result;
  }

    @After("methodCall()")
    public void onCallAfter(JoinPoint joinPoint) throws Throwable{
        Log.e("onCallAfter:", "class : "+joinPoint.getSignature().getDeclaringTypeName() + "method : " +((MethodSignature)joinPoint.getSignature()).getName());
    }
    /**
     * 在截获的目标方法调用之前执行该Advise
     * @param joinPoint
     * @throws Throwable
     */
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    @Before("methodCall()")
    public void onCallBefore(JoinPoint joinPoint) throws Throwable{
        Log.e("onCallBefore:", "class : "+joinPoint.getSignature().getDeclaringTypeName() + "method : " +((MethodSignature)joinPoint.getSignature()).getName());
        Activity activity = null;
        //获取目标对象,截获运行时类型
        activity = ((Activity)joinPoint.getTarget());
        //插入自己的实现,控制目标对象的执行
        ChooseDialog dialog = new ChooseDialog(activity);
        dialog.show();

        //做其他的操作
        buildLogMessage("test",20);
    }
  /**
   * 创建一个日志信息
   *
   * @param methodName 方法名
   * @param methodDuration 执行时间
   * @return
   */
  private static String buildLogMessage(String methodName, double methodDuration) {
    StringBuilder message = new StringBuilder();
    message.append(methodName);
    message.append(" --> ");
    message.append("[");
    message.append(methodDuration);
    if (StopWatch.Accuracy == 1){
        message.append("ms");
    }else {
        message.append("mic");
    }
    message.append("]      \n");
    return message.toString();
  }

}


5 运行结果

启动Activity,点击Button进入另一个Activity

07-20 11:12:30.991 3536-3536/android10.org.viewgroupperformance E/onCallBefore:: class : org.android10.viewgroupperformance.activity.MainActivitymethod : setContentView
07-20 11:12:31.111 3536-3536/android10.org.viewgroupperformance E/onCallAfter:: class : org.android10.viewgroupperformance.activity.MainActivitymethod : setContentView
07-20 11:12:31.111 3536-3536/android10.org.viewgroupperformance E/onCallBefore:: class : org.android10.viewgroupperformance.activity.MainActivitymethod : mapGUI
07-20 11:12:31.131 3536-3536/android10.org.viewgroupperformance E/onCallBefore:: class : org.android10.viewgroupperformance.activity.MainActivitymethod : findViewById

07-20 11:13:34.391 3536-3536/android10.org.viewgroupperformance E/onCallBefore:: class : org.android10.viewgroupperformance.activity.MainActivitymethod : startActivity
07-20 11:13:34.441 3536-3536/android10.org.viewgroupperformance E/onCallAfter:: class : org.android10.viewgroupperformance.activity.MainActivitymethod : startActivity
07-20 11:13:34.501 3536-3536/android10.org.viewgroupperformance E/onCallBefore:: class : org.android10.viewgroupperformance.activity.RelativeLayoutTestActivitymethod : setContentView
07-20 11:13:34.531 3536-3536/android10.org.viewgroupperformance E/onCallAfter:: class : org.android10.viewgroupperformance.activity.RelativeLayoutTestActivitymethod : setContentView
07-20 11:13:34.531 3536-3536/android10.org.viewgroupperformance E/org.android10.viewgroupperformance.activity.RelativeLayoutTestActivity: onCreate --> [32.708ms]      

DEMO地址

AsptecjDemo

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
3天前
|
存储 安全 Android开发
安卓应用开发:构建一个高效的用户登录系统
【5月更文挑战第3天】在移动应用开发中,用户登录系统的设计与实现是至关重要的一环。对于安卓平台而言,一个高效、安全且用户体验友好的登录系统能够显著提升应用的用户留存率和市场竞争力。本文将探讨在安卓平台上实现用户登录系统的最佳实践,包括对最新身份验证技术的应用、安全性考量以及性能优化策略。
|
1天前
|
移动开发 前端开发 Android开发
构建高效Android应用:探究Kotlin协程的优势
【5月更文挑战第4天】 在移动开发领域,尤其是对于Android开发者而言,编写响应迅速且高效的应用程序至关重要。Kotlin作为一种现代的编程语言,其提供的协程特性为异步编程带来了革命性的改变。本文将深入探讨Kotlin协程在Android开发中的应用优势,并通过实例代码展示如何利用协程简化异步任务处理,提高应用性能和用户体验。
|
2天前
|
移动开发 Java Android开发
构建高效Android应用:探究Kotlin与Java的性能对比
【5月更文挑战第4天】在移动开发的世界中,性能一直是衡量应用质量的重要指标。随着Kotlin的兴起,许多Android开发者开始考虑是否应该从传统的Java迁移到Kotlin。本文通过深入分析两者在Android平台上的性能差异,帮助开发者理解Kotlin在实际项目中的表现,并提供选择编程语言时的参考依据。
13 5
|
3天前
|
缓存 测试技术 Android开发
构建高效的Android应用:从设计到实现
【5月更文挑战第2天】 在移动设备日益普及的今天,打造一个既快速又流畅的Android应用对于开发者而言至关重要。本文将深入探讨如何优化Android应用的性能,涵盖UI设计的最佳实践、代码层面的性能提升技巧以及利用最新的Android框架和工具进行应用开发的策略。我们将通过实例分析,揭示那些影响应用响应速度和稳定性的关键因素,并提出切实可行的解决方案,帮助开发者构建出色的用户体验。
|
4天前
|
缓存 移动开发 Android开发
构建高效Android应用:从系统优化到用户体验
【5月更文挑战第2天】 在移动开发的浪潮中,创造一个流畅且响应迅速的Android应用是每个开发者追求的目标。本文将深入探讨如何通过系统级别的优化和细致的设计考量,提升应用性能并增强用户满意度。我们将从减少应用启动时间、内存管理的最佳实践、电池寿命的优化策略以及用户界面(UI)设计的心理学影响等方面,展开全面而具体的技术讨论。
|
4天前
|
Java 编译器 Android开发
构建高效Android应用:探究Kotlin与Java的性能差异
【5月更文挑战第1天】 在移动开发的世界中,性能优化始终是开发者关注的焦点。随着Kotlin的兴起,许多团队和开发者面临着一个选择:是坚持传统的Java语言,还是转向现代化、更加简洁的Kotlin?本文通过深入分析和对比Kotlin与Java在Android应用开发中的性能表现,揭示两者在编译效率、运行速度和内存消耗等方面的差异。我们将探讨如何根据项目需求和团队熟悉度,选择最适合的语言,以确保应用的高性能和流畅体验。
|
5天前
|
缓存 安全 Android开发
构建高效Android应用:采用Kotlin进行内存优化
【5月更文挑战第1天】随着移动设备的普及,用户对应用程序的性能要求越来越高。特别是对于Android开发者来说,理解并优化应用的内存使用是提升性能的关键。本文将探讨使用Kotlin语言在Android开发中实现内存优化的策略和技术。我们将深入分析Kotlin特有的语言特性和工具,以及它们如何帮助开发者减少内存消耗,避免常见的内存泄漏问题,并提高整体应用性能。
|
5天前
|
Java 开发者 Spring
Spring AOP的切点是通过使用AspectJ的切点表达式语言来定义的。
【5月更文挑战第1天】Spring AOP的切点是通过使用AspectJ的切点表达式语言来定义的。
15 5
|
5天前
|
安全 Android开发 开发者
构建高效Android应用:采用Kotlin与Jetpack的实践指南
【4月更文挑战第30天】 在移动开发领域,随着技术的不断进步,为了提高应用的性能和用户体验,开发者们不断地探索新的工具和框架。对于Android平台而言,Kotlin语言以其简洁性和功能性成为了开发的首选。而Jetpack组件则提供了一套高质量的库、工具和指南,帮助开发者更轻松地构建高质量的应用程序。本文将探讨如何结合Kotlin语言和Jetpack组件来优化Android应用的开发流程,提升应用性能,并保证代码的可维护性和可扩展性。
|
5天前
|
Java 编译器 Android开发
构建高效Android应用:探究Kotlin与Java的性能差异
【4月更文挑战第30天】在Android开发领域,Kotlin作为一种现代化的编程语言,因其简洁性和功能性受到了开发者的广泛欢迎。尽管与传统的Java相比,Kotlin提供了诸多便利,但关于其性能表现的讨论始终未息。本文将深入分析Kotlin和Java在Android平台上的性能差异,通过实际测试数据揭示两种语言在编译效率、运行速度以及内存占用方面的具体表现,并探讨如何利用Kotlin的优势来提升Android应用的整体性能。