【Android】Android AOP 编程框架

什么是AOP编程

AOP编程全称Aspect Oriented Programming,面向切面编程

主要功能是在不改变原代码的前提下,对特点代码节点进行修改,预处理,后期处理

AOP的历史

Android的AOP编程框架比较多,它们大多具备以下特点

  • 以AspectJ为基础,提供AOP编程能力
  • AspectJ最早为Java项目中的编程框架,依赖于Maven插件实现
  • AspectJ的原理是在预编译期间,修改代码文件,生成代理对象,从而实现拦截效果
  • 通过Gradle插件,可以将AspectJ应用到Java和Kotlin项目
  • 但大多Gradle插件,依赖于Gradle版本,并且和Android的插件可能产生冲突
  • 因此大多Android Aspect Gradle Plugin,多年没人维护,不能在最新项目中使用
  • 因为Gradle和Android插件更新太频繁,并且不兼容旧代码,Aspect的适配工作工作量太大,很多作者都放弃维护了
io.freefair.aspectj

这个是我们今天要介绍的主角

相对于其它AOP框架,它的版本比较新,而且作者自身在使用,因此不用太担心维护问题

这个项目,没有依赖于其它库,是自己从零开始实现的,但支持了AspectJ的全部功能

其实依赖的库和插件越多,自身兼容性越容易出问题

引入插件依赖

这里用的是Gradle8.7+KotlinScript

settings.gradle.kts中输入以下代码

注意各个gradle插件的版本兼容性,这是gradle不可回避的一个问题

不过相信随着gradle越来越强大,功能越来越丰富,终究会有稳定下来的一天

groovy 复制代码
pluginManagement {
    repositories {
        maven("https://maven.aliyun.com/repository/public/")
        maven("https://maven.aliyun.com/repository/central/")
        maven("https://maven.aliyun.com/repository/gradle-plugin/")
        maven("https://maven.aliyun.com/repository/apache-snapshots/")
        maven("https://plugins.gradle.org/m2/")
        maven("https://jitpack.io/")
        mavenLocal()
        gradlePluginPortal()
        google()
        mavenCentral()
    }
}

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
    repositories {
        maven("https://maven.aliyun.com/repository/public/")
        maven("https://maven.aliyun.com/repository/central/")
        maven("https://maven.aliyun.com/repository/gradle-plugin/")
        maven("https://maven.aliyun.com/repository/apache-snapshots/")
        maven("https://plugins.gradle.org/m2/")
        maven("https://jitpack.io/")
        mavenLocal()
        gradlePluginPortal()
        google()
        mavenCentral()
    }
}

buildscript {
    repositories {
        maven("https://maven.aliyun.com/repository/public/")
        maven("https://maven.aliyun.com/repository/central/")
        maven("https://maven.aliyun.com/repository/gradle-plugin/")
        maven("https://maven.aliyun.com/repository/apache-snapshots/")
        maven("https://plugins.gradle.org/m2/")
        maven("https://jitpack.io/")
        mavenLocal()
        gradlePluginPortal()
        google()
        mavenCentral()
    }
    dependencies {
        classpath("com.google.code.gson:gson:+")
    }
}

plugins {
    id("com.android.application") version "8.1.2" apply false
    id("com.android.library") version "8.1.2" apply false
    id("com.google.devtools.ksp") version "2.0.10-1.0.24" apply false
    id("org.jetbrains.kotlin.android") version "2.0.10" apply false
    id("io.github.FlyJingFish.AndroidAop.android-aop") version "2.0.9" apply false
}

include(":app")
应用插件到模块

在app目录下的build.gradle.kts下输入以下代码

groovy 复制代码
plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
    id("com.google.devtools.ksp")
    id("io.github.FlyJingFish.AndroidAop.android-aop")
}

dependencies {
    api("androidx.appcompat:appcompat:1.7.0")
    api("io.github.FlyJingFish.AndroidAop:android-aop-core:2.0.9")
    api("io.github.FlyJingFish.AndroidAop:android-aop-annotation:2.0.9")
    ksp("io.github.FlyJingFish.AndroidAop:android-aop-ksp:2.0.9")
}

两步就集成完成了,下面我们开始实战吧

这里通过三个最常见的场景,来演示这个框架的强大功能

更详细的用法,请参考作者Github https://github.com/FlyJingFish/AndroidAOP

使用说明

该框架会在预编译期间自动根据注解生效,不需要自己去初始化SDK或注册对象之类的

直接新建一个AOP Interceptor类,通过注解指定要做什么就能生效

类和方法代理

将项目代码中的特定类和方法,替换为自己的类和方法

kotlin 复制代码
package com.android.code.aop

import com.flyjingfish.android_aop_annotation.anno.AndroidAopReplaceClass
import com.flyjingfish.android_aop_annotation.anno.AndroidAopReplaceMethod

@AndroidAopReplaceClass("android.util.Log")
object AndroidLogInterceptor {

    @JvmStatic
    @AndroidAopReplaceMethod("int e(java.lang.String,java.lang.String)")
    fun e(tag: String, msg: String): Int {
        println("AOP AndroidLogInterceptor: $tag $msg")
        return 0
    }
}
方法拦截

可以拦截任意方法,进行额外处理

通过类名+方法名+参数类型+继承关系来匹配,被命中的方法会被拦截

kotlin 复制代码
package com.android.code.aop

import com.flyjingfish.android_aop_annotation.ProceedJoinPoint
import com.flyjingfish.android_aop_annotation.anno.AndroidAopMatchClassMethod
import com.flyjingfish.android_aop_annotation.base.MatchClassMethod
import com.flyjingfish.android_aop_annotation.enums.MatchType

@AndroidAopMatchClassMethod(
    targetClassName = "android.app.Activity",
    methodName = ["void onCreate(android.os.Bundle)"],
    type = MatchType.LEAF_EXTENDS
)
class AndroidActivityCreateInterceptor : MatchClassMethod {

    override fun invoke(
        joinPoint: ProceedJoinPoint,
        methodName: String
    ): Any? {
        val target = joinPoint.target!!.javaClass.simpleName
        println("AOP AndroidActivityCreateInterceptor: $target onCreate")
        return joinPoint.proceed()
    }
}
自定义切入点

如果不想写表达式,可以在想要拦截的方法上加一个切入点注解

再定义一个切入点处理器,与注解绑定,来处理拦截工作即可

此外,注解还可以携带一些参数,用于扩展需要

kotlin 复制代码
package com.android.code.aop

import com.flyjingfish.android_aop_annotation.anno.AndroidAopPointCut

@AndroidAopPointCut(AndroidContentViewInterceptor::class)
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
annotation class AndroidContentViewPointcut(vararg val args: String = [])
kotlin 复制代码
@AndroidContentViewPointcut("HomeActivity")
override fun setContentView(view: View) {
    super.setContentView(view)
}
kotlin 复制代码
package com.android.code.aop

import android.graphics.Color
import android.view.View
import com.flyjingfish.android_aop_annotation.ProceedJoinPoint
import com.flyjingfish.android_aop_annotation.base.BasePointCut

class AndroidContentViewInterceptor : BasePointCut<AndroidContentViewPointcut> {

    override fun invoke(
        joinPoint: ProceedJoinPoint,
        annotation: AndroidContentViewPointcut
    ): Any? {
        val target = joinPoint.target!!.javaClass.simpleName
        val pointcut = annotation.args[0]
        val contentView = joinPoint.args!![0] as View
        contentView.setBackgroundColor(Color.YELLOW)
        println("AOP AndroidContentViewInterceptor: before target executed")
        println("AOP AndroidContentViewInterceptor: $target $pointcut setContentView")
        val result = joinPoint.proceed()
        println("AOP AndroidContentViewInterceptor: after target executed")
        return result
    }
}

可以看到,Activity的背景自动变成黄色了,完全不用改动原代码!

而且还能看到,在拦截过程中,我们是可以对原有方法执行流程进行调整的

或者进行预处理,后期处理

尾声

是不是觉得很强大呢,代码也十分简洁

以上特性已经足够满足一般AOP的需求了

如果有更复杂的需求,记得前往作者主页去看一看

相关推荐
闲暇部落1 小时前
kotlin内联函数——let,run,apply,also,with的区别
kotlin·内联函数
五味香2 小时前
Java学习,List 元素替换
android·java·开发语言·python·学习·golang·kotlin
十二测试录2 小时前
【自动化测试】—— Appium使用保姆教程
android·经验分享·测试工具·程序人生·adb·appium·自动化
Couvrir洪荒猛兽4 小时前
Android实训九 数据存储和访问
android
aloneboyooo4 小时前
Android Studio安装配置
android·ide·android studio
Jacob程序员4 小时前
leaflet绘制室内平面图
android·开发语言·javascript
2401_897907865 小时前
10天学会flutter DAY2 玩转dart 类
android·flutter
m0_748233645 小时前
【PHP】部署和发布PHP网站到IIS服务器
android·服务器·php
Yeats_Liao6 小时前
Spring 定时任务:@Scheduled 注解四大参数解析
android·java·spring
xidianjiapei0018 小时前
为何应将微服务从Java迁移到Kotlin:经验与见解【来自DZone】
java·微服务·kotlin