【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的需求了

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

相关推荐
alexhilton1 天前
在Jetpack Compose中创建CRT屏幕效果
android·kotlin·android jetpack
2501_940094021 天前
emu系列模拟器最新汉化版 安卓版 怀旧游戏模拟器全集附可运行游戏ROM
android·游戏·安卓·模拟器
下位子1 天前
『OpenGL学习滤镜相机』- Day9: CameraX 基础集成
android·opengl
参宿四南河三1 天前
Android Compose SideEffect(副作用)实例加倍详解
android·app
火柴就是我1 天前
mmkv的 mmap 的理解
android
没有了遇见1 天前
Android之直播宽高比和相机宽高比不支持后动态获取所支持的宽高比
android
shenshizhong1 天前
揭开 kotlin 中协程的神秘面纱
android·kotlin
vivo高启强1 天前
如何简单 hack agp 执行过程中的某个类
android
沐怡旸1 天前
【底层机制】 Android ION内存分配器深度解析
android·面试
你听得到111 天前
肝了半个月,我用 Flutter 写了个功能强大的图片编辑器,告别image_cropper
android·前端·flutter