Android 生命周期监听器

了解允许你的库使用 Expo 模块 API 钩子 Android 活动和应用功能的机制。


为了响应与应用相关的某些 Android 系统事件,例如传入链接和配置更改,有必要在 MainActivity.java 和/或 MainApplication.java 中重写相应的生命周期回调。

🌐 To respond to certain Android system events relevant to an app, such as inbound links and configuration changes, it is necessary to override the corresponding lifecycle callbacks in MainActivity.java and/or MainApplication.java.

React Native 模块 API 并未提供任何机制来钩入这些方法,因此 React Native 库的设置说明中通常会包括将代码复制到这些文件中的步骤。为了简化并自动化设置和维护,Expo 模块 API 提供了一种机制,允许你的库钩入 ActivityApplication 函数。

🌐 The React Native module API does not provide any mechanism to hook into these, and so setup instructions for React Native libraries often include steps to copy code into these files. To simplify and automate setup and maintenance, the Expo Modules API provides a mechanism that allows your library to hook into Activity or Application functions.

开始使用

🌐 Get started

首先,你需要创建一个 Expo 模块或在库中使用 React Native 模块 API 集成 Expo 模块 API。了解更多

🌐 First, you need to have created an Expo module or integrated the Expo modules API in the library using the React Native module API. Learn more.

在你的模块中,创建一个实现 Package 接口的具体类。在大多数情况下,你只需要实现 createReactActivityLifecycleListenerscreateApplicationLifecycleListeners 方法。

🌐 Inside your module, create a concrete class that implements the Package interface. For most cases, you only need to implement the createReactActivityLifecycleListeners or createApplicationLifecycleListeners methods.

Activity 生命周期监听器

🌐 Activity lifecycle listeners

你可以使用 ReactActivityLifecycleListener 钩子进入 Activity 生命周期。ReactActivityLifecycleListener 使用其 ReactActivityDelegate 钩子进入 React Native 的 ReactActivity 生命周期,并提供类似于 Android Activity 生命周期的体验。

🌐 You can hook into the Activity lifecycle using ReactActivityLifecycleListener. ReactActivityLifecycleListener hooks into React Native's ReactActivity lifecycle using its ReactActivityDelegate and provides a similar experience to the Android Activity lifecycle.

当前支持以下 Activity 生命周期回调:

🌐 The following Activity lifecycle callbacks are currently supported:

  • onCreate
  • onResume
  • onPause
  • onDestroy
  • onNewIntent
  • onBackPressed

要创建 ReactActivityLifecycleListener,你应该在派生的 Package 类中实现 createReactActivityLifecycleListeners。例如,MyLibPackage

🌐 To create a ReactActivityLifecycleListener, you should implement createReactActivityLifecycleListeners in your derived Package class. For example, MyLibPackage.

// android/src/main/java/expo/modules/mylib/MyLibPackage.kt package expo.modules.mylib import android.content.Context import expo.modules.core.interfaces.Package import expo.modules.core.interfaces.ReactActivityLifecycleListener class MyLibPackage : Package { override fun createReactActivityLifecycleListeners(activityContext: Context): List<ReactActivityLifecycleListener> { return listOf(MyLibReactActivityLifecycleListener()) } }
// android/src/main/java/expo/modules/mylib/MyLibPackage.java package expo.modules.mylib; import android.content.Context; import expo.modules.core.interfaces.Package; import expo.modules.core.interfaces.ReactActivityLifecycleListener; import java.util.Collections; import java.util.List; public class MyLibPackage implements Package { @Override public List<? extends ReactActivityLifecycleListener> createReactActivityLifecycleListeners(Context activityContext) { return Collections.singletonList(new MyLibReactActivityLifecycleListener()); } }

MyLibReactActivityLifecycleListener 是一个 ReactActivityLifecycleListener 派生类,你可以在生命周期中挂接它。你只能重写你所需要的方法。

// android/src/main/java/expo/modules/mylib/MyLibReactActivityLifecycleListener.kt package expo.modules.mylib import android.app.Activity import android.os.Bundle import expo.modules.core.interfaces.ReactActivityLifecycleListener class MyLibReactActivityLifecycleListener : ReactActivityLifecycleListener { override fun onCreate(activity: Activity, savedInstanceState: Bundle?) { // Your setup code in `Activity.onCreate`. doSomeSetupInActivityOnCreate(activity) } }
// android/src/main/java/expo/modules/mylib/MyLibReactActivityLifecycleListener.java package expo.modules.mylib; import android.app.Activity; import android.os.Bundle; import expo.modules.core.interfaces.ReactActivityLifecycleListener; public class MyLibReactActivityLifecycleListener implements ReactActivityLifecycleListener { @Override public void onCreate(Activity activity, Bundle savedInstanceState) { // Your setup code in `Activity.onCreate`. doSomeSetupInActivityOnCreate(activity); } }

你也可以重写其他生命周期方法。下面的示例展示了如何在单个监听器类中重写多个生命周期方法。它基于 expo-linking 模块,该模块使用不同的生命周期方法来处理深度链接。你可以只实现你用例中需要的方法:

🌐 You can also override other lifecycle methods. The example below shows how to override multiple lifecycle methods in a single listener class. It is based on expo-linking module, which uses different lifecycle methods to handle deep links. You can implement only the methods you need for your use case:

// android/src/main/java/expo/modules/mylib/MyLibReactActivityLifecycleListener.kt package expo.modules.mylib import android.app.Activity import android.content.Intent import android.os.Bundle import expo.modules.core.interfaces.ReactActivityLifecycleListener class MyLibReactActivityLifecycleListener : ReactActivityLifecycleListener { override fun onCreate(activity: Activity?, savedInstanceState: Bundle?) { // Called when the activity is first created // Initialize your setup here, for example handling deep links val deepLinkUrl = activity?.intent?.data if (deepLinkUrl != null) { handleDeepLink(deepLinkUrl.toString()) } } override fun onResume(activity: Activity) { // Called when the activity comes to the foreground // For example, track when user returns to the app trackAppStateChange("active") } override fun onPause(activity: Activity) { // Called when the activity goes to the background // For example, pause ongoing operations such as track analytics trackAppStateChange("inactive") } override fun onDestroy(activity: Activity) { // Called when the activity is being destroyed // Clean up resources here cleanup() } override fun onNewIntent(intent: Intent?): Boolean { // Called when app receives a new intent while already running // For example, handle new deep links while app is open val newUrl = intent?.data if (newUrl != null) { handleDeepLink(newUrl.toString()) return true } return false } override fun onBackPressed(): Boolean { // Called when user presses the back button // Return true to prevent default back behavior return handleCustomBackNavigation() } // Now, you can add private functions to handle // your logic for deep links, app state tracking, clean up, and so on. }
// android/src/main/java/expo/modules/mylib/MyLibReactActivityLifecycleListener.java package expo.modules.mylib; import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import expo.modules.core.interfaces.ReactActivityLifecycleListener; public class MyLibReactActivityLifecycleListener implements ReactActivityLifecycleListener { @Override public void onCreate(Activity activity, Bundle savedInstanceState) { // Called when the activity is first created // Initialize your setup here, for example handling deep links Uri deepLinkUrl = activity.getIntent().getData(); if (deepLinkUrl != null) { handleDeepLink(deepLinkUrl.toString()); } } @Override public void onResume(Activity activity) { // Called when the activity comes to the foreground // For example, track when user returns to the app trackAppStateChange("active"); } @Override public void onPause(Activity activity) { // Called when the activity goes to the background // For example, pause ongoing operations such as track analytics trackAppStateChange("inactive"); } @Override public void onDestroy(Activity activity) { // Called when the activity is being destroyed // Clean up resources here cleanup(); } @Override public boolean onNewIntent(Intent intent) { // Called when app receives a new intent while already running // For example, handle new deep links while app is open Uri newUrl = intent.getData(); if (newUrl != null) { handleDeepLink(newUrl.toString()); return true; } return false; } @Override public boolean onBackPressed() { // Called when user presses the back button // Return true to prevent default back behavior return handleCustomBackNavigation(); } // Now, you can add private functions to handle // your logic for deep links, app state tracking, clean up, and so on. }

生命周期监听器到 JavaScript 事件流

🌐 Lifecycle listeners to JavaScript event flow

生命周期监听器是独立于你的 Expo 模块实例存在的单例类。要在生命周期监听器和你的模块之间进行通信(例如,将事件发送到你的应用的 JavaScript 代码),你需要观察模块的事件,并在事件发生时通知生命周期监听器。一个典型的流程可能包含以下步骤:

🌐 Lifecycle listeners are singleton classes that exist independently of your Expo module instances. To communicate between a lifecycle listener and your module (for example, to send events to your app's JavaScript code), you need to observe events from your module and notify the lifecycle listener when events occur. A typical flow may consist of the following steps:

  • 系统集成:生命周期监听器捕获包含 URL 数据的 Android 意图
  • 观察者模式:单例生命周期监听器与模块实例进行通信
  • 事件桥接:模块将结构化事件发送到 JavaScript
  • 内存管理:弱引用可以防止内存泄漏
  • 类型安全和 React 集成:通过 TypeScript 支持正确的事件类型,并提供自定义 Hook,轻松访问深层链接事件

你的自定义模块实现可能不需要事件流程中的所有上述内容。然而,你可以将此模式应用于其他系统事件,例如应用状态更改、配置更改,或需要将 Android 生命周期事件桥接到你的 React Native 应用的自定义业务逻辑。

🌐 Your custom module implementation might not need all of the above from the event flow. However, you can adapt this pattern for other system events like app state changes, configuration changes, or custom business logic that needs to bridge Android lifecycle events to your React Native app.

以下示例演示了如何使用生命周期监听器将 Android 系统事件桥接到你的 React Native 应用中。它基于 expo-linking,使用生命周期监听器创建一个深度链接处理程序,当应用被打开或接收到新意图时捕获 URL。

🌐 The following example demonstrates how to use lifecycle listeners to bridge Android system events to your React Native app. It is based on expo-linking, which uses lifecycle listeners to create a deep link handler that captures URLs when an app is opened or receives new intents.

1

模块注册

🌐 Module registration

首先创建一个模块类来注册你的生命周期监听器:

🌐 Start by creating a module class that registers your lifecycle listener:

// android/src/main/java/expo/modules/deeplinkhandler/DeepLinkHandlerPackage.kt package expo.modules.deeplinkhandler import android.content.Context import expo.modules.core.interfaces.Package import expo.modules.core.interfaces.ReactActivityLifecycleListener class DeepLinkHandlerPackage : Package { override fun createReactActivityLifecycleListeners(activityContext: Context?): List<ReactActivityLifecycleListener> { return listOf(DeepLinkHandlerActivityLifecycleListener()) } }
// android/src/main/java/expo/modules/deeplinkhandler/DeepLinkHandlerPackage.java package expo.modules.deeplinkhandler; import android.content.Context; import expo.modules.core.interfaces.Package; import expo.modules.core.interfaces.ReactActivityLifecycleListener; import java.util.Collections; import java.util.List; public class DeepLinkHandlerPackage implements Package { @Override public List<? extends ReactActivityLifecycleListener> createReactActivityLifecycleListeners(Context activityContext) { return Collections.singletonList(new DeepLinkHandlerActivityLifecycleListener()); } }

2

带有观察者通知的活动生命周期监听器

🌐 Activity lifecycle listener with observer notifications

创建一个生命周期监听器来捕获深度链接并通知模块观察者:

🌐 Create a lifecycle listener that captures deep links and notifies the module observers:

// android/src/main/java/expo/modules/deeplinkhandler/DeepLinkHandlerActivityLifecycleListener.kt package expo.modules.deeplinkhandler import android.app.Activity import android.content.Intent import android.net.Uri import android.os.Bundle import expo.modules.core.interfaces.ReactActivityLifecycleListener class DeepLinkHandlerActivityLifecycleListener : ReactActivityLifecycleListener { override fun onCreate(activity: Activity?, savedInstanceState: Bundle?) { handleIntent(activity?.intent) } override fun onNewIntent(intent: Intent?): Boolean { handleIntent(intent) return true } private fun handleIntent(intent: Intent?) { val url = intent?.data if (url != null) { // Store the initial URL for later retrieval DeepLinkHandlerModule.initialUrl = url // Notify all observers about the new deep link DeepLinkHandlerModule.urlReceivedObservers.forEach { observer -> observer(url) } } } }
// android/src/main/java/expo/modules/deeplinkhandler/DeepLinkHandlerActivityLifecycleListener.java package expo.modules.deeplinkhandler; import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import expo.modules.core.interfaces.ReactActivityLifecycleListener; public class DeepLinkHandlerActivityLifecycleListener implements ReactActivityLifecycleListener { @Override public void onCreate(Activity activity, Bundle savedInstanceState) { handleIntent(activity.getIntent()); } @Override public boolean onNewIntent(Intent intent) { handleIntent(intent); return true; } private void handleIntent(Intent intent) { if (intent == null) return; Uri url = intent.getData(); if (url != null) { // Store the initial URL for later retrieval DeepLinkHandlerModule.initialUrl = url; // Notify all observers about the new deep link for (java.util.function.Consumer<Uri> observer : DeepLinkHandlerModule.urlReceivedObservers) { observer.accept(url); } } } }

3

带事件发送的 Expo 模块

🌐 Expo module with event sending

创建一个维护观察者并向 JavaScript 发送事件的模块:

🌐 Create a module that maintains observers and sends events to JavaScript:

// android/src/main/java/expo/modules/deeplinkhandler/DeepLinkHandlerModule.kt package expo.modules.deeplinkhandler import android.net.Uri import androidx.core.os.bundleOf import expo.modules.kotlin.modules.Module import expo.modules.kotlin.modules.ModuleDefinition import java.lang.ref.WeakReference class DeepLinkHandlerModule : Module() { companion object { var initialUrl: Uri? = null var urlReceivedObservers: MutableSet<((Uri) -> Unit)> = mutableSetOf() } private var urlReceivedObserver: ((Uri) -> Unit)? = null override fun definition() = ModuleDefinition { Name("DeepLinkHandler") Events("onUrlReceived") Function("getInitialUrl") { initialUrl?.toString() } OnStartObserving("onUrlReceived") { val weakModule = WeakReference(this@DeepLinkHandlerModule) val observer: (Uri) -> Unit = { uri -> weakModule.get()?.sendEvent( "onUrlReceived", bundleOf( "url" to uri.toString(), "scheme" to uri.scheme, "host" to uri.host, "path" to uri.path ) ) } urlReceivedObservers.add(observer) urlReceivedObserver = observer } OnStopObserving("onUrlReceived") { urlReceivedObservers.remove(urlReceivedObserver) } } }
// android/src/main/java/expo/modules/deeplinkhandler/DeepLinkHandlerModule.java package expo.modules.deeplinkhandler; import android.net.Uri; import androidx.core.os.Bundle; import expo.modules.kotlin.modules.Module; import expo.modules.kotlin.modules.ModuleDefinition; import java.lang.ref.WeakReference; import java.util.HashSet; import java.util.Set; import java.util.function.Consumer; public class DeepLinkHandlerModule extends Module { public static Uri initialUrl = null; public static Set<Consumer<Uri>> urlReceivedObservers = new HashSet<>(); private Consumer<Uri> urlReceivedObserver; @Override public ModuleDefinition definition() { return ModuleDefinition.create() .name("DeepLinkHandler") .events("onUrlReceived") .function("getInitialUrl", () -> { return initialUrl != null ? initialUrl.toString() : null; }) .onStartObserving("onUrlReceived", () -> { WeakReference<DeepLinkHandlerModule> weakModule = new WeakReference<>(this); Consumer<Uri> observer = uri -> { DeepLinkHandlerModule module = weakModule.get(); if (module != null) { Bundle bundle = new Bundle(); bundle.putString("url", uri.toString()); bundle.putString("scheme", uri.getScheme()); bundle.putString("host", uri.getHost()); bundle.putString("path", uri.getPath()); module.sendEvent("onUrlReceived", bundle); } }; urlReceivedObservers.add(observer); urlReceivedObserver = observer; }) .onStopObserving("onUrlReceived", () -> { urlReceivedObservers.remove(urlReceivedObserver); }); } }

4

TypeScript 接口和 React 使用

🌐 TypeScript interface and React usage

为你的模块定义一个 TypeScript 接口,将 Android 生命周期事件桥接到 JavaScript:

🌐 Define a TypeScript interface for your module to bridge the Android lifecycle events to JavaScript:

DeepLinkHandler.ts
import { requireNativeModule, NativeModule } from 'expo-modules-core'; export type DeepLinkEvent = { url: string; scheme?: string; host?: string; path?: string; }; type DeepLinkHandlerModuleEvents = { onUrlReceived(event: DeepLinkEvent): void; }; declare class DeepLinkHandlerNativeModule extends NativeModule<DeepLinkHandlerModuleEvents> { getInitialUrl(): string | null; } const DeepLinkHandler = requireNativeModule<DeepLinkHandlerNativeModule>('DeepLinkHandler'); export default DeepLinkHandler;

创建一个 React 钩子,以便轻松访问深层链接事件:

🌐 Create a React hook for an easy access to the deep link events:

useDeepLinkHandler.ts
import { useEffect, useState } from 'react'; import DeepLinkHandler, { DeepLinkEvent } from './DeepLinkHandler'; export function useDeepLinkHandler(): { initialUrl: string | null; url: string | null; event: DeepLinkEvent | null; } { const [initialUrl] = useState<string | null>(DeepLinkHandler.getInitialUrl()); const [event, setEvent] = useState<DeepLinkEvent | null>(null); useEffect(() => { const subscription = DeepLinkHandler.addListener('onUrlReceived', event => { setEvent(event); }); return () => subscription.remove(); }, []); return { initialUrl, url: event?.url ?? initialUrl, event, }; }

在你的 React 组件中使用它:

🌐 Use it in your React component:

App.tsx
import { Text, View, StyleSheet } from 'react-native'; import { useDeepLinkHandler } from './useDeepLinkHandler'; export function App() { const { initialUrl, url, event } = useDeepLinkHandler(); return ( <View style={styles.container}> <Text>Initial URL: {initialUrl || 'None'}</Text> <Text>Current URL: {url || 'None'}</Text> {event && ( <View style={styles.textContainer}> <Text>Latest Deep Link:</Text> <Text>Scheme: {event.scheme}</Text> <Text>Host: {event.host}</Text> <Text>Path: {event.path}</Text> </View> )} </View> ); } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', }, textContainer: { marginTop: 20, }, });

5

模块配置

🌐 Module configuration

最后,在 expo-module.config.json 中配置你的模块,将模块连接到生命周期监听器:

🌐 Finally, configure your module in expo-module.config.json to connect the module to the lifecycle listener:

expo-module.config.json
{ "platforms": ["android"], "android": { "modules": ["expo.modules.deeplinkhandler.DeepLinkHandlerModule"] } }

Application 生命周期监听器

🌐 Application lifecycle listeners

你可以使用 ApplicationLifecycleListener 来钩子 Application 生命周期。

🌐 You can hook into the Application lifecycle using ApplicationLifecycleListener.

当前支持以下 Application 生命周期回调:

🌐 The following Application lifecycle callbacks are currently supported:

  • onCreate
  • onConfigurationChanged

要创建一个 ApplicationLifecycleListener,你应该在派生的 Package 类中实现 createApplicationLifecycleListeners。例如,MyLibPackage

🌐 To create an ApplicationLifecycleListener, you should implement createApplicationLifecycleListeners in your derived Package class. For example, MyLibPackage.

// android/src/main/java/expo/modules/mylib/MyLibPackage.kt package expo.modules.mylib import android.content.Context import expo.modules.core.interfaces.ApplicationLifecycleListener import expo.modules.core.interfaces.Package class MyLibPackage : Package { override fun createApplicationLifecycleListeners(context: Context): List<ApplicationLifecycleListener> { return listOf(MyLibApplicationLifecycleListener()) } }
// android/src/main/java/expo/modules/mylib/MyLibPackage.java import android.content.Context; import java.util.Collections; import java.util.List; import expo.modules.core.interfaces.ApplicationLifecycleListener; import expo.modules.core.interfaces.Package; public class MyLibPackage implements Package { @Override public List<? extends ApplicationLifecycleListener> createApplicationLifecycleListeners(Context context) { return Collections.singletonList(new MyLibApplicationLifecycleListener()); } }

MyLibApplicationLifecycleListener 是一个 ApplicationLifecycleListener 派生类,可以钩入 Application 的生命周期回调。你只应重写你需要的方法(由于可能的维护成本)。

// android/src/main/java/expo/modules/mylib/MyLibApplicationLifecycleListener.kt package expo.modules.mylib import android.app.Application import expo.modules.core.interfaces.ApplicationLifecycleListener class MyLibApplicationLifecycleListener : ApplicationLifecycleListener { override fun onCreate(application: Application) { // Your setup code in `Application.onCreate`. doSomeSetupInApplicationOnCreate(application) } }
// android/src/main/java/expo/modules/mylib/MyLibApplicationLifecycleListener.java package expo.modules.mylib; import android.app.Application; import expo.modules.core.interfaces.ApplicationLifecycleListener; public class MyLibApplicationLifecycleListener implements ApplicationLifecycleListener { @Override public void onCreate(Application application) { // Your setup code in `Application.onCreate`. doSomeSetupInApplicationOnCreate(application); } }

已知的问题

🌐 Known issues

为什么没有 onStartonStop 活动监听器

🌐 Why there are no onStart and onStop Activity listeners

在当前的实现中,我们不是从 MainActivity 设置钩子,而是从 ReactActivityDelegate 设置。MainActivityReactActivityDelegate 之间存在一些细微的差别。由于 ReactActivityDelegate 没有 onStartonStop,我们暂时还不支持它们。

🌐 In the current implementation, we do not set up the hooks from MainActivity but from ReactActivityDelegate. There are some slight differences between MainActivity and ReactActivityDelegate. Since ReactActivityDelegate does not have onStart and onStop, we don't yet support them here.

接口稳定性

🌐 Interface stability

监听器接口可能会在不同的 Expo SDK 版本之间发生变化。我们对向后兼容性的策略是始终添加新的接口,并为计划移除的接口添加 @Deprecated 注解。我们的接口都是基于 Java 8 接口的默认方法;你不必也不应该实现所有方法。这将有助于降低你在不同 Expo SDK 版本之间的模块维护成本。

🌐 The listener interfaces may change from time to time between Expo SDK releases. Our strategy for backward compatibility is always to add new interfaces and add @Deprecated annotation for interfaces we plan to remove. Our interfaces are all based on Java 8 interface default method; you don't have to and should not implement all methods. Doing this will benefit your module's maintenance cost between Expo SDKs.