You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Jacoco报告中排除所有Jetpack Compose的Preview函数?

批量排除Jetpack Compose Preview函数的Jacoco覆盖率统计方案

方案1:通过Gradle Jacoco配置结合ASM过滤带@Preview注解的方法

这种方式无需修改业务代码,直接在模块构建脚本中添加过滤逻辑,自动识别并排除所有带有@Preview注解的函数。

Kotlin DSL 配置示例

tasks.withType<JacocoReport> {
    afterEvaluate {
        classDirectories.setFrom(files(classDirectories.files.map { classDir ->
            fileTree(classDir) {
                // 可选:先按文件名过滤减少ASM分析工作量
                exclude("**/*Preview*.*")
                exclude("**/PreviewKt.class")
                
                // 核心:通过ASM扫描类文件,排除含@Preview注解的方法所属类
                filter { file ->
                    file.inputStream().use { inputStream ->
                        val classReader = org.objectweb.asm.ClassReader(inputStream)
                        var hasPreviewMethod = false
                        
                        val classVisitor = object : org.objectweb.asm.ClassVisitor(org.objectweb.asm.Opcodes.ASM9) {
                            override fun visitMethod(
                                access: Int,
                                name: String?,
                                desc: String?,
                                signature: String?,
                                exceptions: Array<out String>?
                            ): org.objectweb.asm.MethodVisitor? {
                                return object : org.objectweb.asm.MethodVisitor(org.objectweb.asm.Opcodes.ASM9) {
                                    override fun visitAnnotation(desc: String?, visible: Boolean) {
                                        if (desc == "Landroidx/compose/ui/tooling/preview/Preview;") {
                                            hasPreviewMethod = true
                                        }
                                        super.visitAnnotation(desc, visible)
                                    }
                                }
                            }
                        }
                        
                        classReader.accept(classVisitor, org.objectweb.asm.ClassReader.SKIP_DEBUG or org.objectweb.asm.ClassReader.SKIP_FRAMES)
                        !hasPreviewMethod
                    }
                }
            }
        }))
    }
}

Groovy 配置示例

tasks.withType(JacocoReport) {
    afterEvaluate {
        classDirectories.setFrom(files(classDirectories.files.collect { classDir ->
            fileTree(classDir) {
                exclude "**/*Preview*.*"
                exclude "**/PreviewKt.class"
                
                filter { file ->
                    def inputStream = file.newInputStream()
                    def classReader = new org.objectweb.asm.ClassReader(inputStream)
                    boolean hasPreviewMethod = false
                    
                    def classVisitor = new org.objectweb.asm.ClassVisitor(org.objectweb.asm.Opcodes.ASM9) {
                        @Override
                        org.objectweb.asm.MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
                            return new org.objectweb.asm.MethodVisitor(org.objectweb.asm.Opcodes.ASM9) {
                                @Override
                                void visitAnnotation(String desc, boolean visible) {
                                    if (desc == "Landroidx/compose/ui/tooling/preview/Preview;") {
                                        hasPreviewMethod = true
                                    }
                                    super.visitAnnotation(desc, visible)
                                }
                            }
                        }
                    }
                    
                    classReader.accept(classVisitor, org.objectweb.asm.ClassReader.SKIP_DEBUG | org.objectweb.asm.ClassReader.SKIP_FRAMES)
                    inputStream.close()
                    return !hasPreviewMethod
                }
            }
        }))
    }
}

方案2:基于命名约定的简单过滤

如果项目中所有Preview函数遵循统一命名规范(比如函数名/文件名含Preview),可以直接用文件过滤规则,这种方式更轻量高效:

Kotlin DSL 示例

tasks.withType<JacocoReport> {
    classDirectories.setFrom(files(classDirectories.files.map {
        fileTree(it) {
            exclude("**/*Preview*.class")
            exclude("**/PreviewKt.class")
        }
    }))
}

Groovy 示例

tasks.withType(JacocoReport) {
    classDirectories.setFrom(files(classDirectories.files.collect {
        fileTree(it) {
            exclude "**/*Preview*.class"
            exclude "**/PreviewKt.class"
        }
    }))
}

额外说明:Release变体无需配置

默认情况下,Compose编译器在Release构建变体中会自动移除所有带@Preview注解的函数,因此Release变体的Jacoco统计不会包含这些函数,无需添加排除规则,仅Debug变体需要配置上述方案。


内容的提问来源于stack exchange,提问作者Elye

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 04:40:23