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:
@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> )
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) }
2.4 Link the Lint Module to Your App
Add this to your app module's build.gradle.kts:
dependencies { lintChecks(project(":lint")) }
@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.
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

