栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

安卓 AOP 用 AspectJ 太麻烦,问题多?试试 BytecodeUtil 吧

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

安卓 AOP 用 AspectJ 太麻烦,问题多?试试 BytecodeUtil 吧

使用 BytecodeUtil 实现 AOP

相信有几年发开经验的同学都知道 AOP 相比 OOP 在一些如集中登录,防止快速点击,方法调用日志这些场景下有着天然的优势。
然而当前流行的 AspectJ 对安卓项目兼容性太差,各种问题排查起来贼麻烦……
为了提升公司项目的开发效率基于 Transform + ASM 开发了 BytecodeUtil 这个编译时修改字节码的开发平台。
Transform 和 ASM 不了解可点这里

项目源码点这里

性能

BytecodeUtil 内部采用多线程并发处理 IO,并提供了过滤接口用于过滤你不想处理的 jar 内文件
目前在公司项目中需要处理的 Jar 包数为 238 个,总占用 74.3MB。如图:
在不过滤全量处理时需要 16s
合理配置项目所需的处理范围后缩短到了 3s

如何使用 依赖

由于发布到 mavenCentral 有点麻烦,目前暂时先点这下载 。

  1. 下载所需的代码包后在你的 Gradle 项目根目录下解压

  2. 配置根目录下的 build.gradle 来使用解压中的 jar 包

    // Top-level build file
    buildscript {
        // 解压的仓库地址
        ext.repos = uri('./repos')
        repositories {
        	// 使用仓库中的 jar
            maven { url repos }
            ... ...
        }
        dependencies {
        	... ...
            classpath "io.github.ysj001:bytecodeutil-plugin:1.0.4"
        }
    }
    
    allprojects {
        repositories {
        	// 使用仓库中的 jar
            maven { url repos }
        	... ...
        }
    }
    
  3. application 模块的 build.gradle 配置依赖

    apply plugin: 'com.android.application'
    // 使用 bytecodeutil 插件
    apply plugin: 'bytecodeutil-plugin'
    
    // 插件扩展
    bytecodeUtil {
        // 设置插件日志级别
        loggerLevel = 1
        // 挂载你所需的修改器
        modifiers = [
            // 如:挂载 AOP 修改器
            Class.forName("com.ysj.lib.bytecodeutil.plugin.core.modifier.aspect.AspectModifier")
    	]
        // 不需要处理的 jar 包内文件过滤器。合理配置可大幅提升编译速度
        notNeedJar = { entryName ->
            // 这里演示个比较通用的
            (entryName.startsWith("kotlin")
                    || entryName.startsWith("java")
                    || entryName.startsWith("org/intellij/")
                    || entryName.startsWith("org/jetbrains/")
                    || entryName.startsWith("org/junit/")
                    || entryName.startsWith("org/hamcrest/")
                    || entryName.startsWith("com/squareup/")
                    || entryName.startsWith("android")
                    || entryName.startsWith("com/google/android/"))
        }
    }
    
    ... ...
        
    dependencies {
    	... ...
        implementation "io.github.ysj001:bytecodeutil-api:1.0.4"
    }
    
混淆配置
-keepclassmembers class * {
    @com.ysj.lib.bytecodeutil.api.util.BCUKeep ;
}
AOP

需要挂载该修改器:com.ysj.lib.bytecodeutil.plugin.core.modifier.aspect.AspectModifier

介绍

Aspect Oriented Programming。面向切面编程,它的应用是指在不修改源代码的情况下给程序动态添加功能,并将这个功能统一横切到一处统一处理的一种编程方式,能充分体现了高内聚低耦合的编程思想。

案例(DEMO)
  • 演示在 MainActivity 的 onCreate 函数体开头插入如下打印 log 的方法

    // 使用该注解标识该类是切面类
    @Aspect
    object AopTest {
        // 使用该注解标识该方法是一个切入点方法,该方法会被织入到指定
        @Pointcut(
            target = "class:.*MainActivity",
            funName = "onCreate",
            funDesc = "\(Landroid/os/Bundle;\)V",
            position = POSITION_START, // 设置织入到目标方法的开头
        )
        fun log(jp: JoinPoint ) {
            Log.i(TAG, "捕获到: ${jp.target} args:${jp.args}")
        }
    }
    
  • 演示代理任意使用了 IntervalTrigger 注解的方法实现间隔触发

    // 自定义一个间隔触发的注解
    @Target(AnnotationTarget.FUNCTION)
    @Retention(AnnotationRetention.RUNTIME)
    annotation class IntervalTrigger(
        val intervalMS: Long = 1000
    )
    
    @Aspect
    object AopTest {
        var oldTriggerTime = 0L
        @Pointcut(
            target = "annotation:L.*IntervalTrigger;",
            position = POSITION_CALL, // 设置在调用位置代理源方法
        )
        fun log(callingPoint: CallingPoint) {
            val trigger = callingPoint.annotation(IntervalTrigger::class.java) ?: return
            val currentTimeMillis = System.currentTimeMillis()
            if (currentTimeMillis - oldTriggerTime < trigger.intervalMS) {
                Log.i(TAG, "log5: 禁止触发")
                return
            }
            oldTriggerTime = currentTimeMillis
            Log.i(TAG, "log5: 成功触发")
            callingPoint.call()
        }
    }
    
    // 如下:
    class MainActivity : AppCompatActivity() {
        @LogPositionReturn
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            findViewById(R.id.test).setOnClickListener {
                test3()
            }
        }
    
        @IntervalTrigger(500)
        private fun test3() {
            // todo something
        }
    }
    
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/281722.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号