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

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

相关推荐
千里马学框架2 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台2 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone2 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc3 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo3 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077003 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
ai2work3 天前
ch23 综合复刻:从零做一个最小可用版本(capstone)
kotlin
其实防守也摸鱼3 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
ai2work3 天前
ch21 签名、校验与发版
kotlin
AFinalStone3 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui