在整个应用程序中处理多个实验性注释

Handling multiple experimental annotations throughout an app

我有一个应用程序大量使用 Jetpack Compose 的实验性功能,所以我必须在可组合项上声明一堆注释。由于这些注释要求调用者也声明它们,所以我最终遇到了 activity 和以下代码的情况:

import androidx.appcompat.app.AppCompatActivity

import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.ui.ExperimentalComposeUiApi

import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi
import com.google.accompanist.pager.ExperimentalPagerApi
import com.google.accompanist.permissions.ExperimentalPermissionsApi
…

class MainActivity : AppCompatActivity() {

    @ExperimentalPermissionsApi
    @ExperimentalComposeUiApi
    @ExperimentalPagerApi
    @ExperimentalMaterialNavigationApi
    @ExperimentalMaterialApi
    override fun onCreate(savedInstanceState: Bundle?) {
        // … wiring up compose code (which propagates the experimental annotations)

避免这种情况的另一种方法是使用 @OptIn 代替,但由于每个声明只允许一个,所以它不适用于我的具有多个实验性功能的情况。

无论如何……这很好用——在 Kotlin 1.5 中。

使用 Kotlin 1.6 我遇到编译错误:

Opt-in requirement marker annotation on override requires the same marker on base declaration

但基础声明在标准 API 中,我无法更改。 我怎样才能编译(并像以前一样工作)?

我厌倦了我的代码被所有这些注释污染了。摆脱它们并编译代码的最简单方法就是将其添加到顶部 build.gradle 文件中 - 这并不详尽。只需为您需要的每个注释添加更多编译器参数:

allprojects {
    tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
        kotlinOptions {
            freeCompilerArgs += [
                    "-Xuse-experimental=kotlin.ExperimentalUnsignedTypes",
                    "-Xuse-experimental=kotlinx.coroutines.ExperimentalCoroutinesApi",
                    "-Xuse-experimental=kotlinx.coroutines.InternalCoroutinesApi",
                    "-Xuse-experimental=androidx.compose.animation.ExperimentalAnimationApi",
                    "-Xuse-experimental=androidx.compose.ExperimentalComposeApi",
                    "-Xuse-experimental=androidx.compose.material.ExperimentalMaterialApi",
                    "-Xuse-experimental=androidx.compose.runtime.ExperimentalComposeApi",
                    "-Xuse-experimental=androidx.compose.ui.ExperimentalComposeUiApi",
                    "-Xuse-experimental=coil.annotation.ExperimentalCoilApi",
                    "-Xuse-experimental=kotlinx.serialization.ExperimentalSerializationApi",
                    "-Xuse-experimental=com.google.accompanist.pager.ExperimentalPagerApi"
            ]
        }
    }
}

Kotlin DSL 中 @Johanns 的一个未弃用的变体(带有我正在使用的一些其他注释):

弃用警告:

w: '-Xuse-experimental' is deprecated and will be removed in a future release, please use -opt-in instead

tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile::class).all {
        kotlinOptions {
            freeCompilerArgs = freeCompilerArgs + listOf(
                // Avoid having to stutter experimental annotations all over the codebase
                "-Xopt-in=androidx.compose.animation.ExperimentalAnimationApi",
                "-Xopt-in=androidx.compose.material.ExperimentalMaterialApi",
                "-Xopt-in=androidx.compose.runtime.ExperimentalComposeApi",
                "-Xopt-in=androidx.compose.ui.ExperimentalComposeUiApi",
                "-Xopt-in=com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi",
                "-Xopt-in=com.google.accompanist.pager.ExperimentalPagerApi",
                "-Xopt-in=com.google.accompanist.permissions.ExperimentalPermissionsApi",
                "-Xopt-in=kotlin.ExperimentalUnsignedTypes",
                "-Xopt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
                "-Xopt-in=kotlinx.coroutines.InternalCoroutinesApi"
            )
        }
    }

An alternative to avoid this situation would be to use the @OptIn instead but since only one is allowed per declaration it doesn't work out for my case with multiple experimental features.

您可以将多个实验性特征用逗号分隔成 @OptIn.

例如@OptIn(ExperimentalComposeUiApi::class, ExperimentalFoundationApi::class, ExperimentalMaterialApi::class)

TLDR;

@ExperimentalAnimationApi
这个注释的意思是——“这是一个实验性的 API,所有用户都必须明确选择使用”。应用程序开发人员很少遇到这种情况。

@OptIn(ExperimetalAnimationApi::class)
此注释的意思是 - “我选择使用实验性 api”。它不会强制此 method/class 的用户在他们的代码中添加注释。

Opt-In Requirements | Kotlin

问题

@ExperimentalAnimationApi
@Composable
fun MyCode()

表示MyCode是实验性动画api,如果您想使用MyCode,请明确选择加入ExperimentalAnimationApi

@Composable
fun MyOtherCode() {
  MyCode() // ERROR!, doesn't compile and shows red underlines in IDE
}

通常会导致我们的代码中有太多 @ExperimentalAnimationApi 注释。 不要这样做

解决方案(修复编译,IDE 警告)

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun MyCode() {
  // experimental animation api usage
}

不会 强制调用者添加任何注释

@Composable
fun MyOtherCode() {
  MyCode() // safe, compiles and doesn't show any errors in IDE
}

作为应用开发者,我们几乎总是要
使用 @OptIn(ExperimentalAnimationApi::class)
@ExperimentalAnimationApi,

除非我们的代码本身在其 public 表面将实验性声明公开为 return 类型或函数参数等

仅修复编译的解决方案

或者我们可以添加

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
  kotlinOptions {
    freeCompilerArgs = freeCompilerArgs + listOf(
      "-opt-in=androidx.compose.animation.ExperimentalAnimationApi",
    )
  }
}

但这不会删除 IDE 下划线等。所以不是很有用。