AOP 在android中的使用之埋点日志

android 开发,我们通常使用的是面向对象编程,这个写起来比较方便。但一些特殊的功能,比如说埋点统计些信息,或者打印某些方法的消耗时间,如果我们在要统计的地方直接写代码,看着不优雅,并且也把功能耦合在一起了。AOP 叫做切面编程,它更像一把刀切入到某个功能里面,不用直接耦合代码。比如如打印耗时日志,使用切面编程则可以把要统计的一些方法的代码统一放在一个地方,通过注解来引用,这样就比较完美的做到了代码分离。


AspectJ 是 AOP 的一个实现类库,我们可以直接使用它。先说说gradle配置方式,我们可以在 Android Studio 中建个 module 库,再库的 gradle 配置文件中,引入 aspectj 的单独脚本配置,我把它抽了出来,如下

build.gradle 
******************

apply plugin: 'com.android.library'
apply from: 'asplib.gradle'
android {
  compileSdkVersion 28
  buildToolsVersion '28.0.3'
}

******************

asplib.gradle
*********************

import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main

buildscript {
  repositories {
    mavenCentral()
    google()
  }
  dependencies {
    classpath 'com.android.tools.build:gradle:3.4.1'
    classpath 'org.aspectj:aspectjtools:1.8.1'
  }
}

dependencies {
  implementation 'org.aspectj:aspectjrt:1.8.1'
}


project.android.libraryVariants.all { variant ->
  JavaCompile javaCompile = variant.javaCompileProvider.get()
  javaCompile.doLast {
    String[] args = ["-showWeaveInfo",
                     "-1.5",
                     "-inpath", javaCompile.destinationDir.toString(),
                     "-aspectpath", javaCompile.classpath.asPath,
                     "-d", javaCompile.destinationDir.toString(),
                     "-classpath", javaCompile.classpath.asPath]

    MessageHandler handler = new MessageHandler(true)
    new Main().run(args, handler)
  }
}

*******************************


在 module 中,定义一个类 TrAspect ,用 @Aspect 来修饰它,比如我们来打印下 Activity 的声明周期日志,

@Aspect
public class TrAspect {

  private static final String POINTCUT_ONMETHOD = "execution(* android.app.Activity.on**(..))";

  @Before(POINTCUT_ONMETHOD)
  public void beforeOnMethod(JoinPoint joinPoint) {
    MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
    String className = methodSignature.getDeclaringType().getSimpleName();
    String methodName = methodSignature.getName();
    Log.e("TrAspect", "before   " + className + "    " + methodName );

  }

  @After(POINTCUT_ONMETHOD)
  public void onMethLog(JoinPoint joinPoint){
    MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
    String className = methodSignature.getDeclaringType().getSimpleName();
    String methodName = methodSignature.getName();
    Log.e("TrAspect", "after   " + className + "    " + methodName );
  }

}


在主工程 app 中,创建 MainActivity,重写 onCreate(Bundle savedInstanceState) 方法

public class MainActivity extends Activity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
  }
  
}


然后我们编译下,发现无效,什么原因呢?原来 app 主工程中 build.gradle 也需要配置(我也不清楚原因),抽取出来,build.gradle 中配置 apply from:'asp.gradle'

asp.gradle

********************************

import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main


buildscript {
  repositories {
    mavenCentral()
    google()
  }
  dependencies {
    classpath 'com.android.tools.build:gradle:3.4.1'
    classpath 'org.aspectj:aspectjtools:1.8.1'
  }
}

dependencies {
  implementation project(':gintonic')
  implementation 'org.aspectj:aspectjrt:1.8.1'
}


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.javaCompileProvider.get()
  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)]

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

***************************


编译后,打印日志

2020-04-29 19:08:28.406 11794-11794/ E/TrAspect: before   MainActivity    onCreate
2020-04-29 19:08:28.495 11794-11794/ E/TrAspect: after   MainActivity    onCreate

细看 TrAspect 中使用的注解,@Before 和 @After 这两个,意思是在要切入的方法之前和之后执行,"execution(* android.app.Activity.on**(..))" 是我们配置的条件,这个里面是说 Activity 子类中所有以 on 开头的方法,当然是我们重写的方法,这就是个条件匹配,一旦匹配到了,马上执行方法。如果想把上面二合一,怎么办?使用 @Around,它具有 @Before 和 @After 的功能,见代码

  @Around(POINTCUT_ONMETHOD)
  public Object weaveJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
    MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
    String className = methodSignature.getDeclaringType().getSimpleName();
    String methodName = methodSignature.getName();

    Log.e("TrAspect", "before   " + className + "    " + methodName );
    Object result = joinPoint.proceed();

    Log.e("TrAspect", "after   " + className + "    " + methodName );

    return result;
  }

joinPoint.proceed(); 的意思就是执行 onCreate()  方法,打印值日,和上面的一样。

同理,埋点也可以这么做,我们可以通过判断方法名字,把日志写入缓存或文本中,然后在主工程中通过读取缓存输入,上报到服务端。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值