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

Android自定义@TargetFlavor注解:类TargetApi适配多产品风味

Got it, let's build that custom @TargetFlavor annotation you need to handle flavor-specific code references—just like how @TargetApi works for API levels! Here's a step-by-step solution tailored to your scenario:


Step 1: Define the Custom @TargetFlavor Annotation

First, create the annotation in your main source set so all flavors can access it. We'll make it repeatable too, in case you need to target multiple flavors:

package com.yourproject.annotations

import kotlin.annotation.AnnotationRetention.SOURCE
import kotlin.annotation.AnnotationTarget.*

// Main annotation to mark flavor-specific code
@Retention(SOURCE)
@Target(CLASS, FUNCTION, PROPERTY, EXPRESSION, CONSTRUCTOR)
@Repeatable(TargetFlavorContainer::class)
annotation class TargetFlavor(
    val value: String // Name of the target flavor (e.g., "device1")
)

// Container annotation to support multiple @TargetFlavor entries
@Retention(SOURCE)
@Target(CLASS, FUNCTION, PROPERTY, EXPRESSION, CONSTRUCTOR)
annotation class TargetFlavorContainer(
    val value: Array<TargetFlavor>
)

Step 2: Add a Lint Rule to Suppress Unresolved References (Pro Approach)

To replicate the exact behavior of @TargetApi (where the IDE/compiler ignores errors when the target condition isn't met), we'll build a custom Lint rule. This automatically suppresses "Unresolved reference" errors when compiling for non-target flavors.

2.1 Set Up a Lint Module

Create a new lint module in your project, and add this to its build.gradle.kts:

plugins {
    id("java-library")
    id("kotlin")
}

dependencies {
    compileOnly("com.android.tools.lint:lint-api:30.1.2") // Match your Android Studio version
    compileOnly("com.android.tools.lint:lint-checks:30.1.2")
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
}

tasks.jar {
    manifest {
        attributes("Lint-Registry-v2" to "com.yourproject.lint.CustomIssueRegistry")
    }
}

2.2 Write the Lint Detector

This detector will check for @TargetFlavor annotations and suppress unresolved references when the current flavor doesn't match the target:

package com.yourproject.lint

import com.android.tools.lint.detector.api.*
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtNameReferenceExpression

class TargetFlavorDetector : Detector(), SourceCodeScanner {

    override fun getApplicableAnnotationNames(): List<String> {
        return listOf("com.yourproject.annotations.TargetFlavor")
    }

    override fun visitAnnotationUsage(
        context: JavaContext,
        element: PsiElement,
        annotation: PsiElement,
        usageType: AnnotationUsageType
    ) {
        // Get current flavor from BuildConfig
        val buildConfig = context.findClass("com.yourproject.BuildConfig")
        val currentFlavor = buildConfig?.findFieldByName("FLAVOR")
            ?.initialValue?.toString()?.removeSurrounding("\"")

        // Get target flavor from the annotation
        val targetFlavor = (annotation as KtAnnotationEntry)
            .valueArguments.firstOrNull()
            ?.getArgumentExpression()?.text?.removeSurrounding("\"")

        // Suppress unresolved reference if flavors don't match
        if (currentFlavor != targetFlavor && element is KtNameReferenceExpression && element.resolve() == null) {
            context.report(
                issue = ISSUE,
                location = context.getLocation(element),
                message = "Ignoring unresolved reference for flavor: $targetFlavor",
                fix = LintFix.suppress().build()
            )
        }
    }

    companion object {
        val ISSUE = Issue.create(
            id = "TargetFlavorUnresolvedRef",
            briefDescription = "Suppress unresolved references for flavor-specific code",
            explanation = "Code marked with @TargetFlavor will have unresolved references suppressed when compiling for non-target flavors",
            category = Category.CORRECTNESS,
            priority = 5,
            severity = Severity.INFORMATIONAL,
            implementation = Implementation(TargetFlavorDetector::class.java, Scope.JAVA_FILE_SCOPE)
        )
    }
}

2.3 Register the Lint Rule

Create a registry to enable the detector:

package com.yourproject.lint

import com.android.tools.lint.client.api.IssueRegistry
import com.android.tools.lint.detector.api.Issue

class CustomIssueRegistry : IssueRegistry() {
    override val issues: List<Issue> = listOf(TargetFlavorDetector.ISSUE)
}

Add this to your app module's build.gradle.kts:

dependencies {
    lintChecks(project(":lint"))
}

Step 3: Use @TargetFlavor in Your Code

Now you can mark flavor-specific code to suppress errors in non-target flavors:

import com.yourproject.annotations.TargetFlavor
import com.yourproject.BuildConfig

fun navigateToExclusivePage() {
    if (BuildConfig.FLAVOR == "device1") {
        @TargetFlavor("device1")
        Navigator.navigate(Device1ExclusivePage())
    }
}

When you switch to device2 or device3, Android Studio will automatically suppress the "Unresolved reference" error for Device1ExclusivePage, just like @TargetApi handles API-level mismatches.


Simplified Alternative (No Lint Required)

If you want a quicker setup without writing custom Lint rules, combine @Suppress with your annotation. It’s less dynamic but works for most cases:

fun navigateToExclusivePage() {
    if (BuildConfig.FLAVOR == "device1") {
        @Suppress("UNRESOLVED_REFERENCE")
        @TargetFlavor("device1")
        Navigator.navigate(Device1ExclusivePage())
    }
}

The @Suppress annotation will hide the error, and the if condition ensures the code never runs in non-target flavors.


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 09:51:04