Expo 模块 API 的 API 参考。
原生模块 API 是一个抽象层,位于 JSI 和 React Native 所构建的其他底层原语之上。它采用现代语言(Swift 和 Kotlin)构建,并提供易于使用且方便的 API,该 API 尽可能在跨平台上保持一致。
¥The native modules API is an abstraction layer on top of JSI and other low-level primitives that React Native is built upon. It is built with modern languages (Swift and Kotlin) and provides an easy-to-use and convenient API that is consistent across platforms where possible.
¥Definition components
正如你可能在 开始使用 页面上的代码片段中注意到的那样,每个模块类必须实现 definition
函数。模块定义由描述模块功能和行为的 DSL 组件组成。
¥As you might have noticed in the snippets on the Get Started page, each module class must implement the definition
function.
The module definition consists of the DSL components that describe the module's functionality and behavior.
Name
设置 JavaScript 代码将用来引用该模块的模块名称。接受一个字符串作为参数。这可以从模块的类名推断出来,但为了清楚起见,建议明确设置它。
¥Sets the name of the module that JavaScript code will use to refer to the module. Takes a string as an argument. This can be inferred from the module's class name, but it's recommended to set it explicitly for clarity.
Name("MyModuleName")
Constants
设置模块的常量属性。可以采用字典或返回字典的闭包。
¥Sets constant properties on the module. Can take a dictionary or a closure that returns a dictionary.
// Created from the dictionary
Constants([
"PI": Double.pi
])
// or returned by the closure
Constants {
return [
"PI": Double.pi
]
}
// Passed as arguments
Constants(
"PI" to kotlin.math.PI
)
// or returned by the closure
Constants {
return@Constants mapOf(
"PI" to kotlin.math.PI
)
}
Function
定义将导出到 JavaScript 的原生同步函数。同步意味着当函数在 JavaScript 中执行时,其原生代码在同一线程上运行,并阻止脚本的进一步执行,直到原生函数返回。
¥Defines a native synchronous function that will be exported to JavaScript. Synchronous means that when the function is executed in JavaScript, its native code is run on the same thread and blocks further execution of the script until the native function returns.
¥Arguments
名称:String
— 你将从 JavaScript 调用的函数的名称。
¥name: String
— Name of the function that you'll call from JavaScript.
主体:(args...) -> ReturnType
— 调用函数时运行的闭包。
¥body: (args...) -> ReturnType
— The closure to run when the function is called.
该函数最多可以接收 8 个参数。这是由于 Swift 和 Kotlin 中泛型的限制,因为该组件必须为每个数量单独实现。
¥The function can receive up to 8 arguments. This is due to the limitations of generics in both Swift and Kotlin because this component must be implemented separately for each arity.
有关函数体中可以使用哪些类型的更多详细信息,请参阅 参数类型 部分。
¥See the Argument types section for more details on what types can be used in the function body.
Function("syncFunction") { (message: String) in
return message
}
Function("syncFunction") { message: String ->
return@Function message
}
import { requireNativeModule } from 'expo-modules-core';
// Assume that we have named the module "MyModule"
const MyModule = requireNativeModule('MyModule');
function getMessage() {
return MyModule.syncFunction('bar');
}
AsyncFunction
定义一个始终返回 Promise
的 JavaScript 函数,并且默认情况下将其原生代码分派到与 JavaScript 运行时运行时不同的线程上。
¥Defines a JavaScript function that always returns a Promise
and whose native code is by default dispatched on a different thread than the JavaScript runtime runs on.
¥Arguments
名称:String
— 你将从 JavaScript 调用的函数的名称。
¥name: String
— Name of the function that you'll call from JavaScript.
主体:(args...) -> ReturnType
— 调用函数时运行的闭包。
¥body: (args...) -> ReturnType
— The closure to run when the function is called.
如果最后一个参数的类型是 Promise
,该函数将等待 Promise 被解析或拒绝,然后再将响应传递回 JavaScript。否则,该函数将立即使用返回值进行解析,如果抛出异常则将被拒绝。该函数最多可以接收 8 个参数(包括 Promise)。
¥If the type of the last argument is Promise
, the function will wait for the promise to be resolved or rejected before the response is passed back to JavaScript. Otherwise, the function is immediately resolved with the returned value or rejected if it throws an exception.
The function can receive up to 8 arguments (including the promise).
有关函数体中可以使用哪些类型的更多详细信息,请参阅 参数类型 部分。
¥See the Argument types section for more details on what types can be used in the function body.
在以下情况下,建议使用 AsyncFunction
而不是 Function
:
¥It is recommended to use AsyncFunction
over Function
when it:
执行 I/O 绑定任务,例如发送网络请求或与文件系统交互
¥does I/O bound tasks such as sending network requests or interacting with the file system
需要在不同的线程上运行,例如 UI 相关任务的主 UI 线程
¥needs to be run on a different thread, for example, the main UI thread for UI-related tasks
是一个广泛或持久的操作,会阻塞 JavaScript 线程,从而降低应用的响应能力
¥is an extensive or long-lasting operation that would block the JavaScript thread which in turn would reduce the responsiveness of the application
AsyncFunction("asyncFunction") { (message: String) in
return message
}
// or
AsyncFunction("asyncFunction") { (message: String, promise: Promise) in
promise.resolve(message)
}
AsyncFunction("asyncFunction") { message: String ->
return@AsyncFunction message
}
// or
// Make sure to import `Promise` class from `expo.modules.kotlin` instead of `expo.modules.core`.
AsyncFunction("asyncFunction") { message: String, promise: Promise ->
promise.resolve(message)
}
import { requireNativeModule } from 'expo-modules-core';
// Assume that we have named the module "MyModule"
const MyModule = requireNativeModule('MyModule');
async function getMessageAsync() {
return await MyModule.asyncFunction('bar');
}
可以通过对该组件的结果调用 .runOnQueue
函数来更改 AsyncFunction
的原生队列。
¥It is possible to change the native queue of AsyncFunction
by calling the .runOnQueue
function on the result of that component.
AsyncFunction("asyncFunction") { (message: String) in
return message
}.runOnQueue(.main)
AsyncFunction("asyncFunction") { message: String ->
return@AsyncFunction message
}.runOnQueue(Queues.MAIN)
¥Kotlin coroutines
AsyncFunction
可以在 Android 上接收可悬挂的主体。但是,它必须在 Coroutine
块之后以中缀表示法传递。你可以在 协程概述 上阅读有关可挂起函数和协程的更多信息。
¥AsyncFunction
can receive a suspendable body on Android. However, it has to be passed in the infix notation after the Coroutine
block. You can read more about suspendable functions and coroutines on coroutine overview.
具有可挂起主体的 AsyncFunction
无法接收 Promise
作为参数。它使用挂起机制来执行异步调用。该函数将立即使用所提供的可挂起块的返回值进行解析,如果抛出异常则将被拒绝。该函数最多可以接收 8 个参数。
¥AsyncFunction
with a suspendable body can't receive Promise
as an argument. It uses a suspension mechanism to execute asynchronous calls.
The function is immediately resolved with the returned value of the provided suspendable block or rejected if it throws an exception. The function can receive up to 8 arguments.
默认情况下,挂起函数在模块的协程范围内调度。此外,从主体块调用的所有其他可挂起函数都在同一范围内运行。该作用域的生命周期与模块的生命周期绑定 - 当模块被释放时,所有未完成的挂起函数都将被取消。
¥By default, suspend functions are dispatched on the module's coroutine scope. Moreover, every other suspendable function called from the body block is run within the same scope. This scope's lifecycle is bound to the module's lifecycle - all unfinished suspend functions will be canceled when the module is deallocated.
AsyncFunction("suspendFunction") Coroutine { message: String ->
// You can execute other suspendable functions here.
// For example, you can use `kotlinx.coroutines.delay` to delay resolving the underlying promise.
delay(5000)
return@Coroutine message
}
Events
定义模块可以发送到 JavaScript 的事件名称。
¥Defines event names that the module can send to JavaScript.
注意:该组件可以在
View
块内部使用来定义回调名称。见View callbacks
¥Note: This component can be used inside of the
View
block to define callback names. SeeView callbacks
Events("onCameraReady", "onPictureSaved", "onBarCodeScanned")
Events("onCameraReady", "onPictureSaved", "onBarCodeScanned")
请参阅 发送事件 了解如何将事件从原生代码发送到 JavaScript/TypeScript。
¥See Sending events to learn how to send events from the native code to JavaScript/TypeScript.
Property
直接在表示原生模块的 JavaScript 对象上定义新属性。它与在模块对象上调用 Object.defineProperty
相同。
¥Defines a new property directly on the JavaScript object that represents a native module. It is the same as calling Object.defineProperty
on the module object.
要声明只读属性,你可以使用需要两个参数的简写语法:
¥To declare a read-only property, you can use a shorthanded syntax that requires two arguments:
名称:String
— 你将从 JavaScript 使用的属性的名称。
¥name: String
— Name of the property that you'll use from JavaScript.
获取器:() -> PropertyType
— 调用属性的 getter 时运行的闭包。
¥getter: () -> PropertyType
— The closure to run when the getter for a property was called.
Property("foo") {
return "bar"
}
Property("foo") {
return@Property "bar"
}
对于可变属性,getter 和 setter 闭包都是需要的(使用下面的语法也可以声明仅具有 setter 的属性):
¥In the case of the mutable property, both the getter and the setter closure are needed (using the syntax below is also possible to declare a property with only a setter):
名称:String
— 你将从 JavaScript 使用的属性的名称。
¥name: String
— Name of the property that you'll use from JavaScript.
获取器:() -> PropertyType
— 调用属性的 getter 时运行的闭包。
¥getter: () -> PropertyType
— The closure to run when the getter for a property was called.
设置者:(newValue: PropertyType) -> void
— 调用属性的 setter 时运行的闭包。
¥setter: (newValue: PropertyType) -> void
— The closure to run when the setter for a property was called.
Property("foo")
.get { return "bar" }
.set { (newValue: String) in
// do something with new value
}
Property("foo")
.get { return@get "bar" }
.set { newValue: String ->
// do something with new value
}
import { requireNativeModule } from 'expo-modules-core';
// Assume that we have named the module "MyModule"
const MyModule = requireNativeModule('MyModule');
// Obtain the property value
MyModule.foo;
// Set a new value
MyModule.foo = 'foobar';
View
使模块能够用作原生视图。被接受为视图定义一部分的定义组件:Prop
、Events
、GroupView
和 AsyncFunction
。
¥Enables the module to be used as a native view. Definition components that are accepted as part of the view definition: Prop
, Events
, GroupView
and AsyncFunction
.
视图定义中的 AsyncFunction
被添加到表示原生视图的 React 组件的 React ref 中。此类异步函数会自动接收原生视图的实例作为第一个参数,并默认在 UI 线程上运行。
¥AsyncFunction
in the view definition is added to the React ref of the React component representing the native view.
Such async functions automatically receive an instance of the native view as the first argument and run on the UI thread by default.
¥Arguments
viewType — 将渲染的原生视图的类。注意:在 Android 上,提供的类必须继承自 ExpoView
,在 iOS 上,它是可选的。参见 Extending ExpoView
。
¥viewType — The class of the native view that will be rendered. Note: On Android, the provided class must inherit from the ExpoView
, on iOS it's optional. See Extending ExpoView
.
定义:() -> ViewDefinition
— 视图定义的构建器。
¥definition: () -> ViewDefinition
— A builder of the view definition.
View(UITextView.self) {
Prop("text") { ... }
AsyncFunction("focus") { (view: UITextView) in
view.becomeFirstResponder()
}
}
View(TextView::class) {
Prop("text") { ... }
AsyncFunction("focus") { view: TextView ->
view.requestFocus()
}
}
信息 计划支持渲染 SwiftUI 视图。现在,你可以使用
UIHostingController
并将其内容视图添加到你的 UIKit 视图中。¥Support for rendering SwiftUI views is planned. For now, you can use
UIHostingController
and add its content view to your UIKit view.
信息 从 SDK 49 开始,视图函数可用。
¥View functions are available as of SDK 49.
Prop
为给定名称的 view prop 定义一个 setter。
¥Defines a setter for the view prop of given name.
¥Arguments
名称:String
— 你想要定义 setter 的视图 prop 的名称。
¥name: String
— Name of view prop that you want to define a setter.
设置者:(view: ViewType, value: ValueType) -> ()
- 视图重新渲染时调用的闭包。
¥setter: (view: ViewType, value: ValueType) -> ()
— Closure that is invoked when the view rerenders.
该属性只能在 ViewManager
闭包内使用。
¥This property can only be used within a ViewManager
closure.
Prop("background") { (view: UIView, color: UIColor) in
view.backgroundColor = color
}
Prop("background") { view: View, @ColorInt color: Int ->
view.setBackgroundColor(color)
}
注意 尚不支持函数类型(回调)的 props。
¥Props of function type (callbacks) are not supported yet.
OnCreate
定义模块初始化后立即调用的模块生命周期监听器。如果你需要在模块初始化时设置某些内容,请使用它而不是模块的类初始值设定项。
¥Defines module's lifecycle listener that is called right after module initialization. If you need to set up something when the module gets initialized, use this instead of module's class initializer.
OnDestroy
定义模块的生命周期监听器,当模块即将被释放时调用该监听器。使用它代替模块的类析构函数。
¥Defines module's lifecycle listener that is called when the module is about to be deallocated. Use it instead of module's class destructor.
OnStartObserving
定义添加第一个事件监听器时调用的函数。
¥Defines the function that is invoked when the first event listener is added.
OnStopObserving
定义删除所有事件监听器时调用的函数。
¥Defines the function that is invoked when all event listeners are removed.
OnAppContextDestroys
定义模块的生命周期监听器,当拥有该模块的应用上下文即将被释放时调用该监听器。
¥Defines module's lifecycle listener that is called when the app context owning the module is about to be deallocated.
OnAppEntersForeground
定义当应用即将进入前台模式时调用的监听器。
¥Defines the listener that is called when the app is about to enter the foreground mode.
注意 此功能在 Android 上不可用 — 你可能需要使用
OnActivityEntersForeground
。¥This function is not available on Android — you may want to use
OnActivityEntersForeground
instead.
OnAppEntersBackground
定义当应用进入后台模式时调用的监听器。
¥Defines the listener that is called when the app enters the background mode.
注意 此功能在 Android 上不可用 — 你可能需要使用
OnActivityEntersBackground
。¥This function is not available on Android — you may want to use
OnActivityEntersBackground
instead.
OnAppBecomesActive
定义当应用再次激活时(OnAppEntersForeground
之后)调用的监听器。
¥Defines the listener that is called when the app becomes active again (after OnAppEntersForeground
).
注意 此功能在 Android 上不可用 — 你可能需要使用
OnActivityEntersForeground
。¥This function is not available on Android — you may want to use
OnActivityEntersForeground
instead.
OnActivityEntersForeground
定义活动恢复后立即调用的活动生命周期监听器。
¥Defines the activity lifecycle listener that is called right after the activity is resumed.
注意 此功能在 iOS 上不可用 — 你可能需要使用
OnAppEntersForeground
。¥This function is not available on iOS — you may want to use
OnAppEntersForeground
instead.
OnActivityEntersBackground
定义活动暂停后立即调用的活动生命周期监听器。
¥Defines the activity lifecycle listener that is called right after the activity is paused.
注意 此功能在 iOS 上不可用 — 你可能需要使用
OnAppEntersBackground
。¥This function is not available on iOS — you may want to use
OnAppEntersBackground
instead.
OnActivityDestroys
定义当拥有 JavaScript 上下文的活动即将被销毁时调用的活动生命周期监听器。
¥Defines the activity lifecycle listener that is called when the activity owning the JavaScript context is about to be destroyed.
注意 此功能在 iOS 上不可用 — 你可能需要使用
OnAppEntersBackground
。¥This function is not available on iOS — you may want to use
OnAppEntersBackground
instead.
GroupView
使视图能够用作视图组。作为组视图定义的一部分接受的定义组件:AddChildView
、GetChildCount
、GetChildViewAt
、RemoveChildView
、RemoveChildViewAt
。
¥Enables the view to be used as a view group. Definition components that are accepted as part of the group view definition: AddChildView
, GetChildCount
, GetChildViewAt
, RemoveChildView
, RemoveChildViewAt
.
¥Arguments
viewType — 原生视图的类。请注意,提供的类必须继承自 Android ViewGroup
。
¥viewType — The class of the native view. Note that the provided class must inherit from the Android ViewGroup
.
定义:() -> ViewGroupDefinition
— 视图组定义的构建器。
¥definition: () -> ViewGroupDefinition
— A builder of the view group definition.
该属性只能在 View
闭包内使用。
¥This property can only be used within a View
closure.
GroupView<ViewGroup> {
AddChildView { parent, child, index -> ... }
}
AddChildView
定义将子视图添加到视图组的操作。
¥Defines action that adds a child view to the view group.
¥Arguments
行动:(parent: ParentType, child: ChildType, index: Int) -> ()
— 将子视图添加到视图组的操作。
¥action: (parent: ParentType, child: ChildType, index: Int) -> ()
— An action that adds a child view to the view group.
该属性只能在 GroupView
闭包内使用。
¥This property can only be used within a GroupView
closure.
AddChildView { parent, child: View, index ->
parent.addView(child, index)
}
GetChildCount
定义检索视图组中子视图数量的操作。
¥Defines action the retrieves the number of child views in the view group.
¥Arguments
行动:(parent: ParentType) -> Int
— 返回子视图数量的函数。
¥action: (parent: ParentType) -> Int
— A function that returns number of child views.
该属性只能在 GroupView
闭包内使用。
¥This property can only be used within a GroupView
closure.
GetChildCount { parent ->
return@GetChildCount parent.childCount
}
GetChildViewAt
定义从视图组中检索特定索引处的子视图的操作。
¥Defines action that retrieves a child view at a specific index from the view group.
¥Arguments
行动:(parent: ParentType, index: Int) -> ChildType
— 从视图组中检索特定索引处的子视图的函数。
¥action: (parent: ParentType, index: Int) -> ChildType
— A function that retrieves a child view at a specific index from the view group.
该属性只能在 GroupView
闭包内使用。
¥This property can only be used within a GroupView
closure.
GetChildViewAt { parent, index ->
parent.getChildAt(index)
}
RemoveChildView
定义从视图组中删除特定子视图的操作。
¥Defines action that removes a specific child view from the view group.
¥Arguments
行动:(parent: ParentType, child: ChildType) -> ()
— 从视图组中删除特定子视图的函数。
¥action: (parent: ParentType, child: ChildType) -> ()
— A function that remove a specific child view from the view group.
该属性只能在 GroupView
闭包内使用。
¥This property can only be used within a GroupView
closure.
RemoveChildView { parent, child: View ->
parent.removeView(child)
}
RemoveChildViewAt
定义从视图组中删除特定索引处的子视图的操作。
¥Defines action that removes a child view at a specific index from the view group.
¥Arguments
行动:(parent: ParentType, child: ChildType) -> ()
— 从视图组中删除特定索引处的子视图的函数。
¥action: (parent: ParentType, child: ChildType) -> ()
— A function that removes a child view at a specific index from the view group.
该属性只能在 GroupView
闭包内使用。
¥This property can only be used within a GroupView
closure.
RemoveChildViewAt { parent, index ->
parent.removeViewAt(child)
}
¥Argument types
从根本上讲,只有原始数据和可序列化数据可以在运行时之间来回传递。然而,通常原生模块需要接收自定义数据结构 - 比字典/映射更复杂,其中值是未知 (Any
) 类型,因此每个值都必须自行验证和转换。Expo Modules API 提供的协议使数据对象的处理更加方便,提供自动验证,最后确保每个对象成员的原生类型安全。
¥Fundamentally, only primitive and serializable data can be passed back and forth between the runtimes. However, usually native modules need to receive custom data structures — more sophisticated than just the dictionary/map where the values are of unknown (Any
) type and so each value has to be validated and cast on its own. The Expo Modules API provides protocols to make it more convenient to work with data objects, to provide automatic validation, and finally, to ensure native type-safety on each object member.
Primitives
所有函数和视图属性设置器都接受 Swift 和 Kotlin 中所有常见的原始类型作为参数。这包括数组、字典/映射以及这些基本类型的选项。
¥All functions and view prop setters accept all common primitive types in Swift and Kotlin as the arguments. This includes arrays, dictionaries/maps and optionals of these primitive types.
语言 | 支持的原始类型 |
---|---|
迅速 | Bool 、Int 、Int8 、Int16 、Int32 、Int64 、UInt 、UInt8 、UInt16 、UInt32 、UInt64 、Float32 、Double 、String |
科特林 | Boolean , Int , Long , Float , Double , String , Pair |
Convertibles
可转换是原生类型,可以根据从 JavaScript 接收的某些特定类型的数据进行初始化。此类类型允许用作 Function
主体中的参数类型。例如,当 CGPoint
类型用作函数参数类型时,可以从两个数字 (x, y)
的数组或具有数字 x
和 y
属性的 JavaScript 对象创建其实例。
¥Convertibles are native types that can be initialized from certain specific kinds of data received from JavaScript. Such types are allowed to be used as an argument type in Function
's body. For example, when the CGPoint
type is used as a function argument type, its instance can be created from an array of two numbers (x, y)
or a JavaScript object with numeric x
and y
properties.
CoreGraphics
和 UIKit
系统框架中的一些常见 iOS 类型已经可以转换。
¥Some common iOS types from CoreGraphics
and UIKit
system frameworks are already made convertible.
原生 iOS 类型 | TypeScript |
---|---|
URL | string 带有 URL。如果未提供方案,则假定它是文件 URL。 |
CGFloat | number |
CGPoint | { x: number, y: number } 或 number[] 以及 x 和 y 坐标 |
CGSize | { width: number, height: number } 或 number[] 的宽度和高度 |
CGVector | 具有 dx 和 dy 向量微分的 { dx: number, dy: number } 或 number[] |
CGRect | { x: number, y: number, width: number, height: number } 或 number[] 以及 x、y、宽度和高度值 |
CGColor UIColor | 颜色十六进制字符串(#RRGGBB 、#RRGGBBAA 、#RGB 、#RGBA ),按照 C/SVG 规范 或 "transparent" 命名颜色 |
Data | Uint8Array SDK 50+ |
同样,java.io
、java.net
或 android.graphics
等软件包中的一些常见 Android 类型也可转换。
¥Similarly, some common Android types from packages like java.io
, java.net
, or android.graphics
are also made convertible.
原生安卓类型 | TypeScript |
---|---|
java.net.URL | string 带有 URL。注意必须提供方案 |
android.net.Uri java.net.URI | string 带有 URI。注意必须提供方案 |
java.io.File java.nio.file.Path (仅适用于 Android API 26) | string 带有文件路径 |
android.graphics.Color | 颜色十六进制字符串(#RRGGBB 、#RRGGBBAA 、#RGB 、#RGBA ),按照 C/SVG 规范 或 "transparent" 命名颜色 |
kotlin.Pair<A, B> | 具有两个值的数组,其中第一个值的类型为 A,第二个值的类型为 B |
kotlin.ByteArray | Uint8Array SDK 50+ |
Records
Record 是一种可转换类型,相当于字典 (Swift) 或映射 (Kotlin),但表示为结构体,其中每个字段都可以拥有其类型并提供默认值。这是一种用原生类型安全来表示 JavaScript 对象的更好方法。
¥Record is a convertible type and an equivalent of the dictionary (Swift) or map (Kotlin), but represented as a struct where each field can have its type and provide a default value. It is a better way to represent a JavaScript object with the native type safety.
struct FileReadOptions: Record {
@Field
var encoding: String = "utf8"
@Field
var position: Int = 0
@Field
var length: Int?
}
// Now this record can be used as an argument of the functions or the view prop setters.
Function("readFile") { (path: String, options: FileReadOptions) -> String in
// Read the file using given `options`
}
class FileReadOptions : Record {
@Field
val encoding: String = "utf8"
@Field
val position: Int = 0
@Field
val length: Int? = null
}
// Now this record can be used as an argument of the functions or the view prop setters.
Function("readFile") { path: String, options: FileReadOptions ->
// Read the file using given `options`
}
Enums
使用枚举,我们可以进一步使用上面的示例(使用 FileReadOptions
记录)并将支持的编码限制为 "utf8"
和 "base64"
。要使用枚举作为参数或记录字段,它必须表示原始值(例如 String
、Int
)并符合 Enumerable
。
¥With enums, we can go even further with the above example (with FileReadOptions
record) and limit supported encodings to "utf8"
and "base64"
. To use an enum as an argument or record field, it must represent a primitive value (for example, String
, Int
) and conform to Enumerable
.
enum FileEncoding: String, Enumerable {
case utf8
case base64
}
struct FileReadOptions: Record {
@Field
var encoding: FileEncoding = .utf8
// ...
}
// Note: the constructor must have an argument called value.
enum class FileEncoding(val value: String) : Enumerable {
utf8("utf8"),
base64("base64")
}
class FileReadOptions : Record {
@Field
val encoding: FileEncoding = FileEncoding.utf8
// ...
}
Eithers
在某些用例中,你希望为单个函数参数传递各种类型。这就是任一类型可能派上用场的地方。它们充当几种类型之一的值的容器。
¥There are some use cases where you want to pass various types for a single function argument. This is where Either types might come in handy. They act as a container for a value of one of a couple of types.
Function("foo") { (bar: Either<String, Int>) in
if let bar: String = bar.get() {
// `bar` is a String
}
if let bar: Int = bar.get() {
// `bar` is an Int
}
}
Function("foo") { bar: Either<String, Int> ->
bar.get(String::class).let {
// `it` is a String
}
bar.get(Int::class).let {
// `it` is an Int
}
}
当前提供了开箱即用的三种 Either 类型的实现,允许你使用最多四种不同的子类型。
¥The implementation for three Either types is currently provided out of the box, allowing you to use up to four different subtypes.
Either<FirstType, SecondType>
— 两种类型之一的容器。
¥Either<FirstType, SecondType>
— A container for one of two types.
EitherOfThree<FirstType, SecondType, ThirdType>
— 三种类型之一的容器。
¥EitherOfThree<FirstType, SecondType, ThirdType>
— A container for one of three types.
EitherOfFour<FirstType, SecondType, ThirdType, FourthType>
— 四种类型之一的容器。
¥EitherOfFour<FirstType, SecondType, ThirdType, FourthType>
— A container for one of four types.
JavaScript values
还可以使用 JavaScriptValue
类型,它是可以用 JavaScript 表示的任何值的持有者。当你想要改变给定参数或想要省略类型验证和转换时,此类型非常有用。请注意,使用 JavaScript 特定类型仅限于同步函数,因为 JavaScript 运行时中的所有读取和写入都必须在 JavaScript 线程上进行。从不同线程对这些值的任何访问都会导致崩溃。
¥It's also possible to use a JavaScriptValue
type which is a holder for any value that can be represented in JavaScript.
This type is useful when you want to mutate the given argument or when you want to omit type validations and conversions.
Note that using JavaScript-specific types is restricted to synchronous functions as all reads and writes in the JavaScript runtime must happen on the JavaScript thread.
Any access to these values from different threads will result in a crash.
除了原始值之外,JavaScriptObject
类型可用于仅允许对象类型,而 JavaScriptFunction<ReturnType>
类型可用于回调。
¥In addition to the raw value, the JavaScriptObject
type can be used to allow only object types and JavaScriptFunction<ReturnType>
for callbacks.
Function("mutateMe") { (value: JavaScriptValue) in
if value.isObject() {
let jsObject = value.getObject()
jsObject.setProperty("expo", value: "modules")
}
}
// or
Function("mutateMe") { (jsObject: JavaScriptObject) in
jsObject.setProperty("expo", value: "modules")
}
Function("mutateMe") { value: JavaScriptValue ->
if (value.isObject()) {
val jsObject = value.getObject()
jsObject.setProperty("expo", "modules")
}
}
// or
Function("mutateMe") { jsObject: JavaScriptObject ->
jsObject.setProperty("expo", "modules")
}
信息 JavaScript 类型从 SDK 49 开始可用。
¥JavaScript types are available as of SDK 49.
¥Native classes
Module
原生模块的基类。
¥A base class for a native module.
¥Properties
¥Methods
sendEvent(eventName, payload)
Name | Type | Description |
---|---|---|
eventName | string | The name of the JavaScript event |
payload | Android: Map<String, Any?> | Bundle
iOS: [String: Any?] | The event payload |
Sends an event with a given name and a payload to JavaScript. See Sending events
void
AppContext
应用上下文是单个 Expo 应用的接口。
¥The app context is an interface to a single Expo app.
¥Properties
constants
Provides access to app's constants from legacy module registry.
Android: ConstantsInterface? iOS: EXConstantsInterface?
permissions
Provides access to the permissions manager from legacy module registry.
Android: Permissions? iOS: EXPermissionsInterface?
imageLoader
Provides access to the image loader from the legacy module registry.
Android: ImageLoaderInterface? iOS: EXImageLoaderInterface?
barcodeScanner
Provides access to the bar code scanner manager from the legacy module registry.
ImageLoaderInterface?
camera
Provides access to the camera view manager from the legacy module registry.
CameraViewInterface?
font
Provides access to the font manager from the legacy module registry.
FontManagerInterface?
sensor
Provides access to the sensor manager from the legacy module registry.
SensorServiceInterface?
taskManager
Provides access to the task manager from the legacy module registry.
TaskManagerInterface?
activityProvider
Provides access to the activity provider from the legacy module registry.
ActivityProvider?
ExpoView
所有导出视图都应使用的基类。
¥A base class that should be used by all exported views.
在 iOS 上,ExpoView
扩展了 RCTView
,它处理某些样式(例如边框)和可访问性。
¥On iOS, ExpoView
extends the RCTView
which handles some styles (for example, borders) and accessibility.
¥Properties
ExpoView
¥Extending ExpoView
要使用 View
组件导出视图,你的自定义类必须继承自 ExpoView
。通过这样做,你将可以访问 AppContext
对象。这是与其他模块和 JavaScript 运行时通信的唯一方式。另外,你不能更改构造函数参数,因为提供的视图将由 expo-modules-core
初始化。
¥To export your view using the View
component, your custom class must inherit from the ExpoView
. By doing that you will get access to the AppContext
object. It's the only way of communicating with other modules and the JavaScript runtime. Also, you can't change constructor parameters, because provided view will be initialized by expo-modules-core
.
class LinearGradientView: ExpoView {}
public class LinearGradientModule: Module {
public func definition() -> ModuleDefinition {
View(LinearGradientView.self) {
// ...
}
}
}
class LinearGradientView(
context: Context,
appContext: AppContext,
) : ExpoView(context, appContext)
class LinearGradientModule : Module() {
override fun definition() = ModuleDefinition {
View(LinearGradientView::class) {
// ...
}
}
}
¥Guides
¥Sending events
虽然 JavaScript/TypeScript 到 Native 的通信主要由原生函数覆盖,但你可能还想让 JavaScript/TypeScript 代码了解某些系统事件,例如,当剪贴板内容发生更改时。
¥While JavaScript/TypeScript to Native communication is mostly covered by native functions, you might also want to let the JavaScript/TypeScript code know about certain system events, for example, when the clipboard content changes.
为此,在模块定义中,你需要提供模块可以使用 活动 定义组件发送的事件名称。之后,你可以在模块实例上使用 sendEvent(eventName, payload)
函数来发送带有一些负载的实际事件。例如,发送原生事件的最小剪贴板实现可能如下所示:
¥To do this, in the module definition, you need to provide the event names that the module can send using the Events definition component. After that, you can use the sendEvent(eventName, payload)
function on the module instance to send the actual event with some payload. For example, a minimal clipboard implementation that sends native events may look like this:
let CLIPBOARD_CHANGED_EVENT_NAME = "onClipboardChanged"
public class ClipboardModule: Module {
public func definition() -> ModuleDefinition {
Events(CLIPBOARD_CHANGED_EVENT_NAME)
OnStartObserving {
NotificationCenter.default.addObserver(
self,
selector: #selector(self.clipboardChangedListener),
name: UIPasteboard.changedNotification,
object: nil
)
}
OnStopObserving {
NotificationCenter.default.removeObserver(
self,
name: UIPasteboard.changedNotification,
object: nil
)
}
}
@objc
private func clipboardChangedListener() {
sendEvent(CLIPBOARD_CHANGED_EVENT_NAME, [
"contentTypes": availableContentTypes()
])
}
}
const val CLIPBOARD_CHANGED_EVENT_NAME = "onClipboardChanged"
class ClipboardModule : Module() {
override fun definition() = ModuleDefinition {
Events(CLIPBOARD_CHANGED_EVENT_NAME)
OnStartObserving {
clipboardManager?.addPrimaryClipChangedListener(listener)
}
OnStopObserving {
clipboardManager?.removePrimaryClipChangedListener(listener)
}
}
private val clipboardManager: ClipboardManager?
get() = appContext.reactContext?.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager
private val listener = ClipboardManager.OnPrimaryClipChangedListener {
clipboardManager?.primaryClipDescription?.let { clip ->
this@ClipboardModule.sendEvent(
CLIPBOARD_CHANGED_EVENT_NAME,
bundleOf(
"contentTypes" to availableContentTypes(clip)
)
)
}
}
}
要在 JavaScript/TypeScript 中订阅这些事件,你需要使用 EventEmitter
类封装原生模块,如下所示:
¥To subscribe to these events in JavaScript/TypeScript, you need to wrap the native module with EventEmitter
class as shown:
import { requireNativeModule, EventEmitter, Subscription } from 'expo-modules-core';
const ClipboardModule = requireNativeModule('Clipboard');
const emitter = new EventEmitter(ClipboardModule);
export function addClipboardListener(listener: (event) => void): Subscription {
return emitter.addListener('onClipboardChanged', listener);
}
¥View callbacks
某些事件与特定视图相关。例如,触摸事件应该只发送到被按下的底层 JavaScript 视图。在这种情况下,你不能使用 Sending events
中描述的 sendEvent
。expo-modules-core
引入了视图回调机制来处理视图绑定事件。
¥Some events are connected to a certain view. For example, the touch event should be sent only to the underlying JavaScript view which was pressed. In that case, you can't use sendEvent
described in Sending events
. The expo-modules-core
introduces a view callbacks mechanism to handle view-bound events.
要使用它,在视图定义中,你需要提供视图可以使用 活动 定义组件发送的事件名称。之后,你需要在视图类中声明一个类型为 EventDispatcher
的属性。声明的属性的名称必须与 Events
组件中导出的名称相同。稍后,你可以将其作为函数调用,并在 iOS 上传递 [String: Any?]
类型的有效负载,在 Android 上传递 Map<String, Any?>
类型的有效负载。
¥To use it, in the view definition, you need to provide the event names that the view can send using the Events definition component. After that, you need to declare a property of type EventDispatcher
in your view class. The name of the declared property has to be the same as the name exported in the Events
component. Later, you can call it as a function and pass a payload of type [String: Any?]
on iOS and Map<String, Any?>
on Android.
注意:在 Android 上,可以指定有效负载类型。如果类型不转换为对象,则有效负载将被封装并存储在
payload
键下:{payload: <provided value>}
。¥Note: On Android, it's possible to specify the payload type. In case of types that don't convert into objects, the payload will be encapsulated and stored under the
payload
key:{payload: <provided value>}
.
class CameraViewModule: Module {
public func definition() -> ModuleDefinition {
View(CameraView.self) {
Events(
"onCameraReady"
)
// ...
}
}
}
class CameraView: ExpoView {
let onCameraReady = EventDispatcher()
func callOnCameraReady() {
onCameraReady([
"message": "Camera was mounted"
]);
}
}
class CameraViewModule : Module() {
override fun definition() = ModuleDefinition {
View(ExpoCameraView::class) {
Events(
"onCameraReady"
)
// ...
}
}
}
class CameraView(
context: Context,
appContext: AppContext
) : ExpoView(context, appContext) {
val onCameraReady by EventDispatcher()
fun callOnCameraReady() {
onCameraReady(mapOf(
"message" to "Camera was mounted"
));
}
}
要在 JavaScript/TypeScript 中订阅这些事件,你需要将一个函数传递给原生视图,如下所示:
¥To subscribe to these events in JavaScript/TypeScript, you need to pass a function to the native view as shown:
import { requireNativeViewManager } from 'expo-modules-core';
const CameraView = requireNativeViewManager('CameraView');
export default function MainView() {
const onCameraReady = event => {
console.log(event.nativeEvent);
};
return <CameraView onCameraReady={onCameraReady} />;
}
提供的有效负载在 nativeEvent
密钥下可用。
¥Provided payload is available under the nativeEvent
key.
¥Examples
public class MyModule: Module {
public func definition() -> ModuleDefinition {
Name("MyFirstExpoModule")
Function("hello") { (name: String) in
return "Hello \(name)!"
}
}
}
class MyModule : Module() {
override fun definition() = ModuleDefinition {
Name("MyFirstExpoModule")
Function("hello") { name: String ->
return "Hello $name!"
}
}
}
有关实际模块的更多示例,你可以参考 GitHub 上已经使用此 API 的 Expo 模块:
¥For more examples from real modules, you can refer to Expo modules that already use this API on GitHub: