使用平台特定API
在本文中,你将学习如何在开发多平台应用程序和库时使用平台特定的API。
Kotlin多平台库
在编写使用平台特定API的代码之前,请检查是否可以使用多平台库替代。这类库提供了一个通用的Kotlin API,并为不同平台提供了不同的实现。
已有许多可用的库可用于实现网络、日志记录、分析以及访问设备功能等。更多信息,请参阅此精选列表。
预期与实际函数和属性
Kotlin提供了一种语言机制,在开发通用逻辑时访问平台特定的API: 预期与实际声明。
通过这种机制,多平台模块的通用源集定义一个预期声明,每个平台源集必须提供与预期声明对应的实际声明。编译器确保通用源集中标记为expect
关键字的每个声明在所有目标平台源集中都有对应的标记为actual
关键字的声明。
这适用于大多数Kotlin声明,如函数、类、接口、枚举、属性和注解。本节重点介绍使用预期与实际函数和属性。
在此示例中,你将在通用源集中定义一个预期的platform()
函数,并在平台源集中提供实际实现。在为特定平台生成代码时,Kotlin编译器会合并预期与实际声明。它生成一个带有实际实现的platform()
函数。预期与实际声明应在同一包中定义,并在生成的平台代码中合并为_一个声明_。生成的平台代码中对预期platform()
函数的任何调用都将调用正确的实际实现。
示例:生成UUID
假设你正在使用Kotlin Multiplatform开发iOS和Android应用程序,并希望生成一个通用唯一标识符(UUID)。
为此,在Kotlin Multiplatform模块的通用源集中使用expect
关键字声明预期函数randomUUID()
。 不要包含任何实现代码。
在每个平台特定的源集(iOS和Android)中,为通用模块中预期的randomUUID()
函数提供实际实现。使用actual
关键字标记这些实际实现。
以下代码片段展示了Android和iOS的实现。平台特定代码使用actual
关键字和相同的函数名称:
Android实现使用Android上可用的API,而iOS实现使用iOS上可用的API。你可以从Kotlin/Native代码访问iOS API。
在为Android生成平台代码时,Kotlin编译器会自动合并预期与实际声明,并生成一个带有实际Android特定实现的randomUUID()
函数。iOS也会重复相同的过程。
为简单起见,本示例及以下示例使用简化的源集名称“common”、“ios”和“android”。通常,这指的是commonMain
、 iosMain
和androidMain
,类似的逻辑可以在测试源集commonTest
、 iosTest
和androidTest
中定义。
与预期与实际函数类似,预期与实际属性允许你在不同平台上使用不同的值。预期与实际函数和属性在简单场景中最有用。
通用代码中的接口
如果平台特定逻辑过于庞大和复杂,你可以通过在通用代码中定义一个接口来表示它,然后在平台源集中提供不同的实现来简化代码。
平台源集中的实现使用其对应的依赖项:
当你需要一个通用接口时,可以通过以下选项之一注入适当的平台实现,每个选项将在下面详细解释:
使用预期与实际函数
通过不同入口点提供实现
使用依赖注入框架
预期与实际函数
定义一个返回该接口值的预期函数,然后定义返回其子类的实际函数:
当你在通用代码中调用platform()
函数时,它可以处理Platform
类型的对象。当你在Android上运行此通用代码时, platform()
调用返回AndroidPlatform
类的实例。在iOS上运行时, platform()
返回IOSPlatform
类的实例。
不同入口点
如果你控制入口点,可以在不使用预期与实际声明的情况下构建每个平台工件的实现。为此,在共享的Kotlin Multiplatform模块中定义平台实现,但在平台模块中实例化它们:
在Android上,你应该创建AndroidPlatform
的实例并将其传递给application()
函数,而在iOS上,你应该类似地创建并传递IOSPlatform
的实例。这些入口点不必是你的应用程序的入口点,但这是你可以调用共享模块特定功能的地方。
通过预期与实际函数或直接通过入口点提供正确的实现,在简单场景中效果很好。然而,如果你的项目中使用了依赖注入框架,我们建议在简单情况下使用它以确保一致性。
依赖注入框架
现代应用程序通常使用依赖注入(DI)框架来创建松耦合的架构。DI框架允许根据当前环境将依赖项注入组件。
任何支持Kotlin Multiplatform的DI框架都可以帮助你为不同平台注入不同的依赖项。
例如, Koin是一个支持Kotlin Multiplatform的依赖注入框架:
在这里,Koin DSL创建了定义注入组件的模块。你使用expect
关键字在通用代码中声明一个模块,然后使用actual
关键字为每个平台提供平台特定的实现。框架负责在运行时选择正确的实现。
当你使用DI框架时,所有依赖项都通过该框架注入。同样的逻辑适用于处理平台依赖项。如果你已经在项目中使用了DI,我们建议继续使用它,而不是手动使用预期与实际函数。这样可以避免混合两种不同的依赖注入方式。
你也不必总是在Kotlin中实现通用接口。你可以在其他语言中实现它,例如Swift,在另一个_平台模块_中。如果选择这种方法,你应该使用DI框架从iOS平台模块提供实现:
这种方法仅在你将实现放在平台模块中时才有效。它的可扩展性不高,因为你的Kotlin Multiplatform模块不能自给自足,你需要在不同的模块中实现通用接口。
下一步?
有关预期/实际机制的更多示例和信息,请参阅预期与实际声明。