前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >谈谈对Android上AspectJ使用的想法

谈谈对Android上AspectJ使用的想法

作者头像
包子388321
发布2020-06-16 16:33:03
1.7K0
发布2020-06-16 16:33:03
举报
文章被收录于专栏:包子的书架

AOP是什么

概念:AOP是Aspect Oriented Programming的缩写,即『面向切面编程』;切面编程,就是在你项目原有的功能基础上,通过AOP去添加新的功能,这些功能是建立在原有功能的基础上的,而且原有的功能并不知道你已经添加了新的功能;AOP就是在某一个类或方法执行前后打个标记,声明在执行到这里之前要先执行什么,执行完这里之后要接着执行什么。插入了新的执行方法。

AOP和OOP的不同

OOP,即『面向对象编程』,它提倡的是将功能模块化,对象化,而AOP的思想,则不太一样,它提倡的是针对同一类问题的统一处理,当然,我们在实际编程过程中,不可能单纯的安装AOP或者OOP的思想来编程,很多时候,可能会混合多种编程思想,大家也不必要纠结该使用哪种思想,取百家之长,才是正道。

AOP的使用场景

主要用于不想侵入原有代码的场景中,例如SDK需要无侵入的在宿主中插入一些代码,做日志埋点、性能监控、数据校验、持久化、动态权限控制、甚至是代码调试等等。

什么是AspectJ

AspectJ实际上是对AOP编程思想的一个实践,当然,除了AspectJ以外,还有很多其它的AOP实现,例如ASMDex,但目前最好、最方便的,依然是AspectJ。

原理图

图片来自https://www.jianshu.com/p/0fa8073fd144

工作原理.png

首先谈谈AspectJ相关的几个概念点

  • Join Points(连接点) Join Points可以看作是程序运行时的一个执行点,比如:一个函数的调用可以看作是个Join Points,如Log.d()这个函数,d()可以看作是个Join Points,而调运d()的函数也可以认为是一个Join Points;设置一个变量,或者读取一个变量也可以是个Join Points;for循环也可以看作是Join Points。可以是函数,构造方法等

Join Points.png

  • Pointcuts(切入点) 一个程序会有多个Join Points,即使同一个函数,也还分为call和execution类型的Join Points,但并不是所有的Join Points都是我们关心的,Pointcuts就是提供一种使得开发者能够选择自己需要的JoinPoints的方法;或者说是程序中可能作为代码注入目标的特定的点,例如一个方法调用或者方法入口。

image.png 以上的 Signature 都是由一段表达式组成,且每个关键词之间都有“空格”,下面是对关键词的解释:

image.png

  • Advice Advice,也就是具体的插入点。典型的 Advice 类型有 before、after 和 around,分别表示在目标方法执行之前、执行后和完全替代目标方法执行的代码。

image.png 看个例子:

代码语言:javascript
复制
@Before("execution(* android.app.Activity.on**(..))")
public void onActivityMethodBefore(JoinPoint joinPoint) throws Throwable {
}

这里会分成几个部分,我们依次来看: --- @Before:Advice,也就是具体的插入点 --- execution:处理Join Point的类型,例如call、execution --- (* android.app.Activity.on(..)):这个是最重要的表达式,第一个『』表示返回值,『』表示返回值为任意类型,后面这个就是典型的包名路径,其中可以包含『』来进行通配,几个『』没区别。同时,这里可以通过『&&、||、!』来进行条件组合。()代表这个方法的参数,你可以指定类型,例如android.os.Bundle,或者(..)这样来代表任意类型、任意个数的参数。 --- public void onActivityMethodBefore:实际切入的代码。

应用

在Android项目中使用AspectJ

在android中配置aspectj是特别麻烦的,目前市场上流行的一款在Android使用的插件 gradle_plugin_android_aspectjx

如何在Android studio配置gradle_plugin_android_aspectjx的插件
  • 项目根目录的build.gradle中增加依赖:classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.4'
代码语言:javascript
复制
buildscript {
    
    repositories {
        google()
        jcenter(){
            url 'http://jcenter.bintray.com/'
        }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.2'
        classpath 'com.jakewharton:butterknife-gradle-plugin:8.8.1'  //添加这一行
        classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.4'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter(){
            url 'http://jcenter.bintray.com/'
        }
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}
  • 在主项目或者库的build.gradle中增加AspectJ的依赖:api 'org.aspectj:aspectjrt:1.8.9' //相关api的引入
代码语言:javascript
复制
dependencies {
  implementation fileTree(dir: 'libs', include: ['*.jar'])
  api 'org.aspectj:aspectjrt:1.8.9'
              ...
}
  • 在主项目的build.gradle中添加:apply plugin: 'android-aspectjx'
代码语言:javascript
复制
apply plugin: 'com.android.application'
apply plugin: 'android-aspectjx'
  • Aspect的实现类
代码语言:javascript
复制
package com.jason.aspectj;

import android.content.Context;
import android.util.Log;
import android.widget.Toast;

import com.jason.aspectj.tools.StopWatch;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;

/**
* Description:AspectActivityTest
*
* @author Chenby
* @create 2019/10/10 14: 25
*/
@Aspect
public class AspectActivityTest {

 /**
  * 重要的概念知识
  */
 //Join Points,简称JPoints,是AspectJ的核心思想之一,它就像一把刀,把程序的整个执行过程切成了一段段不同的部分
 //Advice,Advice其实是最好理解的,也就是我们具体插入的代码,以及如何插入这些代码
 //@Before:Advice,也就是具体的插入点, 常见的有:Before,After,Around
 //execution:处理Join Point的类型,例如call、execution
 //execution是在被切入的方法中,call是在调用被切入的方法前或者后

 //Call(Before)
 //Pointcut{
 //    Pointcut Method
 //}
 //Call(After)

 //Pointcut{
 //  execution(Before)
 //    Pointcut Method
 //  execution(After)
 //}

 //(* android.app.Activity.on**(..)):这个是最重要的表达式,第一个『*』表示返回值,『*』表示返回值为任意类型,后面这个就是典型的包名路径,
 // 其中可以包含『*』来进行通配,几个『*』没区别。同时,这里可以通过『&&、||、!』来进行条件组合。()代表这个方法的参数,你可以指定类型,
 // 例如android.os.Bundle,或者(..)这样来代表任意类型、任意个数的参数。


 private static final String TAG = "Chenby";

 /**
  * advice 采用before的例子, Joint Point的类型是execution  Before 表示在方法之后插入代码
  *
  * @throws Throwable
  */
 @Before("execution(* android.app.Activity.on**(..))")
 public void onActivityMethodBefore(JoinPoint joinPoint) throws Throwable {
   String key = joinPoint.getSignature().toString();
   Log.d(TAG, "onActivityMethodBefore: " + key);
 }

 /**
  * advice 采用after的例子, Joint Point的类型是execution  After 表示在方法之后插入代码
  *
  * @throws Throwable
  */
 @After("execution(* android.app.Activity+.on*(android.os.Bundle))")
 public void onActivityMethodAfter(JoinPoint joinPoint) throws Throwable {
   String key = joinPoint.getSignature().toString();
   Log.d(TAG, "onActivityMethodAfter: " + key);
 }

 /**
  * advice 采用 Around的例子, Joint Point的类型是execution, Around表示在方法前后各插入代码
  *
  * @throws Throwable
  */
 @Around("execution(* com.cby.mvvmdemo.ui.mine.MinePageFragment.testAOP(*))")
 public void onFragmentMethodAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
   String key = proceedingJoinPoint.getSignature().toString();
   Log.d(TAG, "onFragmentMethodAround: " + key);
   //表示原有方法的执行
   proceedingJoinPoint.proceed();
   Log.d(TAG, "onFragmentMethodAround: " + key);
 }

 @Around("execution(* com.cby.mvvmdemo.ui.mine.MinePageFragment.testAOP(android.content.Context))")
 public void onMethodAroundToast(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
   Context mContext = null;
   //proceedingJoinPoint.getThis()可以获取到调用该方法的对象
   if (proceedingJoinPoint.getThis() instanceof Context) {
     mContext = (Context) proceedingJoinPoint.getThis();
   } else {
     //proceedingJoinPoint.getArgs()可以获取到方法的所有参数
     for (Object context : proceedingJoinPoint.getArgs()) {
       if (context instanceof Context) {
         mContext = (Context) context;
         break;
       }
     }
   }
   if (mContext == null) {
     return;
   }
   proceedingJoinPoint.proceed();
   Toast.makeText(mContext.getApplicationContext(), "Test show toast for aop!", Toast.LENGTH_SHORT)
       .show();
 }

 private static final String POINTCUT_METHOD =
     "execution(@com.jason.aspectj.custom.DebugTool * *(..))";

 private static final String POINTCUT_CONSTRUCTOR =
     "execution(@com.jason.aspectj.custom.DebugTool *.new(..))";

 @Pointcut(POINTCUT_METHOD)
 public void methodAnnotatedWithDebugTrace(){}

 @Pointcut(POINTCUT_CONSTRUCTOR)
 public void constructorAnnotatedDebugTrace() {}

 @Around("methodAnnotatedWithDebugTrace() || constructorAnnotatedDebugTrace()")
 public Object onDebugToolMethodAround(ProceedingJoinPoint joinPoint) throws Throwable {

   MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
   String className = methodSignature.getDeclaringType().getSimpleName();
   String methodName = methodSignature.getName();

   final StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   Object result = joinPoint.proceed();
   stopWatch.stop();

   Log.i(className, buildLogMessage(methodName, stopWatch.getTotalTimeMillis()));

   return result;
 }

 /**
  * Create a log message.
  *
  * @param methodName A string with the method name.
  * @param methodDuration Duration of the method in milliseconds.
  * @return A string representing message.
  */
 private static String buildLogMessage(String methodName, long methodDuration) {
   StringBuilder message = new StringBuilder();
   message.append("Chenby --> ");
   message.append(methodName);
   message.append(" --> ");
   message.append("[");
   message.append(methodDuration);
   message.append("ms");
   message.append("]");

   return message.toString();
 }

 @AfterReturning(value = "execution(* com.cby.mvvmdemo.z_test.activities.AopTestActivity.AfterReturning*(..))", returning = "num")
 public void testAspectAfterReturning(int num) {
   Log.e(TAG, "AfterReturning-num:" + num);
 }
}
  /**
   * proceed(ibject[] args)的使用
   */
  @Around("execution(* com.jason.aspectj*.many*(..))")
  public Object process(ProceedingJoinPoint point) throws Throwable {
       System.out.println("@Around:执行目标方法之前...");
       //访问目标方法的参数:
       Object[] args = point.getArgs();
       if (args != null && args.length > 0 && args[0].getClass() == String.class) {
           args[0] = "改变后的参数1";
       }
       //用改变后的参数执行目标方法
       Object returnValue = point.proceed(args);
       System.out.println("@Around:执行目标方法之后...");
       System.out.println("@Around:被织入的目标对象为:" + point.getTarget());
       return "原返回值:" + returnValue + ",这是返回结果的后缀";
   }
  • 自定义的注解
代码语言:javascript
复制
package com.jason.aspectj.custom;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Description:DebugTool :自定义AOP的注解
*
* @author Chenby
* @create 2019/10/10 16: 31
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface DebugTool {
}

结语

以上是借鉴了网上的文章和自己的一些理解关于AOP的AspectJ的编程的想法,如有错误欢迎评论留言指出

参考文献

https://www.jianshu.com/p/0fa8073fd144 https://blog.csdn.net/eclipsexys/article/details/54425414 https://www.cnblogs.com/yxx123/p/6665736.html

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • AOP是什么
    • AOP和OOP的不同
      • AOP的使用场景
        • 什么是AspectJ
          • 原理图
        • 首先谈谈AspectJ相关的几个概念点
          • 应用
            • 在Android项目中使用AspectJ
            • 如何在Android studio配置gradle_plugin_android_aspectjx的插件
          • 结语
            • 参考文献
            相关产品与服务
            应用性能监控
            应用性能监控(Application Performance Management,APM)是一款应用性能管理平台,基于实时多语言应用探针全量采集技术,为您提供分布式性能分析和故障自检能力。APM 协助您在复杂的业务系统里快速定位性能问题,降低 MTTR(平均故障恢复时间),实时了解并追踪应用性能,提升用户体验。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档