如何使用AOP来打印Android日志

in 默认分类 with 0 comment

传统情况下,我们在Android中打印log都是通过类似于Log.d(TAG,"your log")这样的语句来进行log打印,但是你是否有时会觉得在你的代码逻辑中充斥着太多的log输出语句,想要改变一下呢?其实,我们可以使用AOP技术,来分离Android的log打印逻辑,减少逻辑代码中log打印语句的使用,更快速地打印Android log。

准备

首先,我们需要在项目中引入Aspectj,关于Android项目中添加Aspectj的支持,推荐github上的gradle_plugin_android_aspectjx项目,这是一个gradle插件,配置好之后即可启用Android上的AOP支持

开始


引入Aspectj的支持后,我们就可以进行Android下的AOP开发了,对于日志记录,我们先定义一个LogMethod注解

LogMethod.kt:

package com.example.aop.annotation

@Target(AnnotationTarget.FUNCTION)  // 声明此注解的目标为函数
@Retention(AnnotationRetention.RUNTIME)  // 由于添加了是否在函数执行前或函数执行后打印log的控制,所以这个注解需要在运行时仍然存在,用于在运行时获取注解参数的值
annotation class LogMethod(val before: Boolean = false, val after: Boolean = false)  // 若before为true,则会在函数执行前打印log,若after为true,则会在函数执行后打印log,可同时设置为true

注解创建完成后,我们还需要定义一个切面来实现注解方法

LogMethodAspect.kt:

package com.example.aop

import android.util.Log
import com.example.aop.annotation.LogMethod
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.util.*

@Aspect  // 声明这是一个切面以启用Aspectj
class LogMethodAspect {

    // 定义切点为添加了该注解的函数
    @Pointcut("execution(@com.example.aop.annotation.LogMethod * *(..))")
    fun onLogMethod() {
    }

    // 因为需要在函数执行前后打印log,所以需要创建一个环绕通知
    @Around("onLogMethod()&& @annotation(logMethod)")
    @Throws(Throwable::class)
    fun logMethod(joinPoint: ProceedingJoinPoint, logMethod: LogMethod): Any? {
        val signature = joinPoint.signature as MethodSignature

        // 在函数执行前打印log,并记录参数列表
        if (logMethod.before) {
            Log.d(signature.toShortString(), " Args : ${joinPoint.args?.let { Arrays.deepToString(joinPoint.args)}}")
        }

        // 执行函数
        val result = joinPoint.proceed()

        // 在函数执行后打印结果,并记录返回值,若无返回值,则打印void
        if (logMethod.after) {
            val type = signature.returnType.toString()
            Log.d(signature.toShortString(), " Result : ${if ("void".equals(type, ignoreCase = true)) "void" else result}")
        }

        // 返回函数执行结果
        return result
    }
}

就这样,我们的注解就写好了,现在,只需要在需要记录log的函数前面加上@LogMethod(before = true, after = true)注解就行了,当然,可以根据需要,仅在函数执行前或执行后打印log

使用

我们可以创建一个MainActivity来测试我们编写的注解是否生效(部分无关代码已省略)
MainActivity.kt:

    ......

    @LogMethod(before = true,after = true)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    @LogMethod(before = true)
    override fun onStart() {
        super.onStart()
    }

    @LogMethod(after = true)
    override fun onStop() {
        super.onStop()
    }

    ......

运行此MainActivity,log的输出结果如下:

com.example.demo D/MainActivity.onCreate(..):  Args : [null]
com.example.demo D/MainActivity.onCreate(..):  Result : void
com.example.demo D/MainActivity.onStart():  Args : []
com.example.demo D/MainActivity.onStop():  Result : void

可以看到,我们并未在代码中使用任何log打印语句,即可成功监测函数的执行情况,达到了使用log进行代码执行情况跟踪的目的

总结

通过使用AOP技术,可以实现Android应用中log模块与应用业务逻辑分离的目的,减少了代码中log输出语句的目的,实现了log输出的集中管理,在实际使用中,还可以根据情况添加额外的判断(如自定义log的输出级别),增加了代码的可读性与可维护性。

但是由于受Android AOP实现的限制,目前在Android中仅支持基于注解的切入方式,这也意味着我们仅能针对类、属性或函数进行面向切面编程,所以本log记录注解只能针对函数使用,如果需要在代码块中输出log,仍需使用Android中的log打印函数来实现。