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()  方法,打印值日,和上面的一样。

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

<think>好的,我现在需要帮助用户解决如何在Android开发中添加Log日志并进行埋点输出的问题。首先,我得仔细理解用户的需求。用户提到了日志埋点输出,这可能涉及到常规的日志记录以及埋点数据的上报,用于分析用户行为或监控应用性能。 根据用户提供的引用内容,特别是引用[3],里面提到了View的performClick方法,这里可能是埋点的一个切入点。另外,引用[4]提到上报方式,如JSON、URL参数、头部信息,以及携带cookie或本地存储的数据。还有引用[1]和[2]展示了日志输出的代码示例,比如使用LogTool和AspectJ进行切面编程。 首先,我应该分步骤来回答用户的问题。用户可能需要知道如何添加常规的Log日志,以及如何实现埋点上报。常规的Log可以使用Android自带的Log类,而埋点可能需要更复杂的方法,比如手动在关键点插入代码,或者使用AOP(面向切面编程)来无侵入地添加埋点。 接下来,我需要考虑埋点的具体实现方式。根据引用[3],可以重写View的performClick方法,或者通过AspectJ来切入点击事件。例如,在onClick方法执行前后插入埋点代码。此外,可能还需要处理数据的收集和上报,比如使用OkHttp或Retrofit发送到服务器,并按照引用[4]提到的格式组织数据,包括设备信息、用户ID、时间戳等。 另外,用户可能关心如何保证埋点的高效性和减少对主线程的影响。这里可能需要提到异步线程或使用队列批量上报数据,避免阻塞UI线程。同时,数据存储方面,可以使用本地数据库暂存数据,待有网络时再上传,防止数据丢失。 还要注意引用的正确使用,比如在提到埋点切入点时引用[3],在数据上报方式时引用[4]。需要确保每个引用的位置合理,自然融入回答中。 最后,生成相关问题,可能涉及优化埋点性能、数据安全、第三方工具的使用等,这些都需要基于之前的回答内容来提出,帮助用户进一步了解相关主题。</think>### 如何在 Android 开发中添加 Log 日志并进行埋点输出 #### 1. 添加基础 Log 日志 Android 提供了 `android.util.Log` 类支持日志输出,包含不同级别: ```java Log.v("TAG", "Verbose信息"); // 最低级别,调试细节 Log.d("TAG", "Debug信息"); // 调试关键变量 Log.i("TAG", "Info信息"); // 业务流程节点 Log.w("TAG", "Warning信息"); // 潜在问题 Log.e("TAG", "Error信息"); // 错误事件 ``` 通过 `adb logcat` 可查看日志,建议封装工具类统一管理日志开关[^1]。 --- #### 2. 埋点实现方式 ##### (1) **手动代码埋点** 在关键事件(如按钮点击)中插入上报代码: ```java button.setOnClickListener(v -> { // 业务逻辑 reportEvent("BUTTON_CLICK", "页面A_功能按钮"); }); ``` 需封装 `reportEvent` 方法,包含事件类型、参数、时间戳等[^3]。 ##### (2) **AOP 无侵入埋点** 通过 AspectJ 实现切面编程,拦截目标方法: ```java @Aspect public class ClickAspect { @Around("execution(* android.view.View.OnClickListener.onClick(..))") public void onClickHook(ProceedingJoinPoint joinPoint) { View view = (View) joinPoint.getArgs()[0]; String viewId = getViewPath(view); // 获取视图唯一标识 reportEvent("VIEW_CLICK", viewId); // 上报事件 joinPoint.proceed(); // 执行原方法 } } ``` 需配置 AspectJ 插件和规则文件。 ##### (3) **系统回调埋点** 利用 `AccessibilityService` 或 `Window.Callback` 监听全局交互事件。 --- #### 3. 数据上报实现 ##### 数据结构设计 ```json { "event_type": "VIEW_CLICK", "timestamp": 1629984000000, "params": { "page": "MainActivity", "view_id": "btn_submit" }, "device_id": "IMEI_123456", "user_id": "user_001" } ``` ##### 上报技术方案 - **网络请求**:通过 `OkHttp` 或 `Retrofit` 异步上报 - **本地缓存**:使用 `Room` 数据库暂存数据,网络恢复后重传 - **批量上报**:合并多条事件减少请求频率[^4] --- #### 4. 优化与注意事项 - **异步线程**:避免在主线程执行网络请求(使用 `WorkManager` 或线程池) - **数据压缩**:对上报内容进行 GZIP 压缩 - **隐私合规**:过滤敏感信息(如用户手机号),遵循 GDPR/CCPA - **性能监控**:通过 `StrictMode` 检测埋点导致的卡顿 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值