Android Aop编程

一、OOP与AOP

作为Android开发者,相信大家对OOP(面向对象编程)不陌生。OOP面向对象编程思想,更习惯于将业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。而AOP,面向切面编程则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。

举个栗子,在日常开发中,我们经常需要判断手机网络状态、用户登录状态、埋点日志等。

如果按照OOP的编程思想,则我们需要在每个需要以上操作的地方添加执行代码。如果将些逻辑判断的过程抽象为一个横切面,用很简单的方法插入到业务流程中,将带来更高的效率和清晰的结构。

二、AOP的使用场景

AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理,拦截分发,Hook等等。

三、重要概念

结合下面这张图便于理解:

  • Cross-cutting concerns(横切关注点): 尽管面向对象模型中大多数类会实现单一特定的功能,但通常也会开放一些通用的附属功能给其他类。例如,我们希望在数据访问层中的类中添加日志,同时也希望当UI层中一个线程进入或者退出调用一个方法时添加日志。尽管每个类都有一个区别于其他类的主要功能,但在代码里,仍然经常需要添加一些相同的附属功能。

  • Advice(通知): 注入到class文件中的代码。典型的 Advice 类型有 before、after 和 around,分别表示在目标方法执行之前、执行后和完全替代目标方法执行的代码。 除了在方法中注入代码,也可能会对代码做其他修改,比如在一个class中增加字段或者接口。

  • Joint point(连接点): 程序中可能作为代码注入目标的特定的点,例如一个方法调用或者方法入口。

  • Pointcut(切入点): 告诉代码注入工具,在何处注入一段特定代码的表达式。例如,在哪些 joint points 应用一个特定的 Advice。切入点可以选择唯一一个,比如执行某一个方法,也可以有多个选择,比如,标记了一个定义成@DebguTrace 的自定义注解的所有方法。

  • Aspect(切面): Pointcut 和 Advice 的组合看做切面。例如,我们在应用中通过定义一个 pointcut 和给定恰当的advice,添加一个日志切面。

  • Weaving(织入): 注入代码(advices)到目标位置(joint points)的过程

(如果现在还不是很理解,没关系,结合下面代码很容易明白)

四、AOP编程实战

1、引入方式1

直接引入aspectj,在moudle下配置build.gradle

apply plugin: 'com.android.application'
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'
        classpath 'org.aspectj:aspectjweaver:1.8.9'
    }
}
repositories {
    mavenCentral()
}
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.8",
                         "-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;
            }
        }
    }
}


android {
    compileSdkVersion 27
    ......
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:27.1.1'
    ......
    compile 'org.aspectj:aspectjrt:1.8.9'
}

这种方式稍显麻烦,其实还有一个更简便的引入方式(前人栽树后人乘凉,感觉前辈!)

2、引入方法2

  • 在项目根目录下的build.gradle中,添加依赖:
dependencies {
     ......
     classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.0'
}
  • 在app的build.gradle中,添加:
apply plugin: 'android-aspectjx'
dependencies {
    ......
    implementation 'org.aspectj:aspectjrt:1.8.9'
}

3、编写类

1)自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckNetwork {
}

2)创建切面类

@Aspect
public class CheckNetworkAspect {

    int count = 0;

    @Pointcut("execution(@com.kwmax.alltestdemo.aop.CheckNetwork  * *(..))")
    public void execCheckNetwork(){

    }

    @Around("execCheckNetwork()")
    public Object checkNetwork(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        CheckNetwork annotation = signature.getMethod().getAnnotation(CheckNetwork.class);
        if (annotation != null) {
            Context context = getContext(joinPoint.getThis());
            if (NetWorkUtils.isNetworkConnected(context)) {
                Toast.makeText(context, count+"当前网络正常", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(context, count+"此时没有网络连接", Toast.LENGTH_SHORT).show();
            }
            count++;
            return joinPoint.proceed();
        }
        return null;
    }


    /**
     * 通过对象获取上下文
     */
    private Context getContext(Object object) {
        if (object instanceof Activity) {
            return (Activity) object;
        } else if (object instanceof Fragment) {
            Fragment fragment = (Fragment) object;
            return fragment.getActivity();
        } else if (object instanceof android.app.Fragment) {
            android.app.Fragment fragment = (android.app.Fragment) object;
            return fragment.getActivity();
        } else if (object instanceof View) {
            View view = (View) object;
            return view.getContext();
        }
        return null;
    }
}

需要注意的是:

@Pointcut来标识所要寻找的切点,* *(..)  “**”表示是任意包名   “..”表示任意类型任意多个参数。

这里切入点就为我们的自定义注解的方法,所以要填写自定义注解的路径。

@Around()此处写的是@Pointcut注解下的方法名,要一致。除此之外,还有@Before和@After注解可用,代表在方法前插入代码和方法后插入代码

如果在自定义的注解中定义了方法,通过如下可以拿到

String content = annotation.value();
int type = annotation.type();

自定义注解上的参数赋值如下

@BehaviorTrace(value = "打开首页", type = 1)
public void behaviorTrace(){
        Log.i(TAG,"用户行为检查");
}

4、测试

我们在执行方法之前检查网络状态:

@CheckNetwork
private void test() {
      //测试方法
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

KWMax

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值