React Native与Android原生Activity页面跳转全攻略
React Native与Android原生Activity页面跳转全攻略
在移动开发领域,React Native(以下简称“RN”)凭借“一套代码,多端运行”的优势迅速流行。但在实际项目中,我们常常需要与 Android 原生模块打通,例如:从 RN 界面直接跳转到某个原生 Activity
,或者在原生 Activity
中返回结果后继续在 RN 中处理。本文将围绕以下几个核心场景展开详解,并提供完整代码示例、ASCII 图解与详细说明,帮助你轻松理解并快速上手:
- RN 调用原生 Activity(无参/有参)
- 原生 Activity 返回结果给 RN
- 原生侧启动 RN 界面(Deep Linking 与 Intent)
- React Navigation 与原生跳转的结合示例
一、环境与项目准备
React Native 环境
- 本文示例基于 React Native 0.65+,Node.js v14+,Android Studio 4.0+。
假设项目已经通过
npx react-native init MyApp
成功创建,并能正常运行:cd MyApp npx react-native run-android
Android 原生环境
- 使用 Android Studio 打开
MyApp/android
目录。 - 确保
minSdkVersion ≥ 21
,编译 SDK 版本与目标 SDK 版本均为 30 及以上。
- 使用 Android Studio 打开
目录结构示例
MyApp/ ├── android/ ← Android 原生工程 │ ├── app/ │ │ ├── src/ │ │ │ ├── main/ │ │ │ │ ├── java/com/myapp/ ← Java 源码 │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ ├── MyApp.java │ │ │ │ │ └── MyModule.java ← 我们将自定义 NativeModule │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── res/... │ └── build.gradle ├── ios/ ← iOS 工程(本文不涉及) ├── index.js ├── App.js └── package.json
AndroidManifest.xml
- 在
<application>
节点中,RN 默认的MainActivity
会包含<intent-filter>
,用于处理 Deep Linking 或启动入口。后续如果我们新建原生页面SecondActivity
,也需要在这里注册。
<!-- android/app/src/main/AndroidManifest.xml --> <application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="false" android:theme="@style/AppTheme"> <activity android:name=".MainActivity" android:label="@string/app_name" android:configChanges="keyboard|keyboardHidden|orientation|screenSize" android:windowSoftInputMode="adjustResize"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <!-- Deep Linking 示例方式 --> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="myapp" android:host="open" /> </intent-filter> </activity> <!-- 1. 新增: 原生 SecondActivity 注册 --> <activity android:name=".SecondActivity" android:label="Second Page" android:exported="true"> </activity> </application>
- 以上我们在
MainActivity
中配置了 Deep Linking(myapp://open
可唤起 RN 界面)。 - 同时新建原生
SecondActivity
,未来可在 RN 中通过 NativeModule 直接启动它。
- 在
二、场景一:React Native 调用原生 Activity(无参/有参)
RN 与原生的交互通常通过 NativeModule 进行桥接。我们需要在 Android 端实现一个自定义 Module,用来封装 startActivity()
的逻辑,再在 RN 端通过 NativeModules
调用。
2.1 在 Android 原生端:创建 MyModule.java
新建 Java 文件
在android/app/src/main/java/com/myapp/
下新建MyModule.java
:// android/app/src/main/java/com/myapp/MyModule.java package com.myapp; import android.app.Activity; import android.content.Intent; import android.util.Log; import androidx.annotation.NonNull; import com.facebook.react.bridge.ActivityEventListener; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Callback; import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.WritableMap; public class MyModule extends ReactContextBaseJavaModule implements ActivityEventListener { private static final String TAG = "MyModule"; private static final int REQUEST_CODE = 1234; private Promise mPendingPromise; public MyModule(@NonNull ReactApplicationContext reactContext) { super(reactContext); reactContext.addActivityEventListener(this); } @NonNull @Override public String getName() { return "MyModule"; // RN 端通过 NativeModules.MyModule 访问 } /** * 2.1.1 无参启动 SecondActivity */ @ReactMethod public void startSecondActivity() { Activity currentActivity = getCurrentActivity(); if (currentActivity == null) { Log.e(TAG, "Activity 为空,无法跳转"); return; } Intent intent = new Intent(currentActivity, SecondActivity.class); currentActivity.startActivity(intent); } /** * 2.1.2 带参数启动,并希望获取返回结果 (startActivityForResult) * @param message 要传递的字符串参数 * @param promise 用于回调给 JS 端结果 */ @ReactMethod public void startSecondActivityForResult(String message, Promise promise) { Activity currentActivity = getCurrentActivity(); if (currentActivity == null) { promise.reject("ACTIVITY_NULL", "Activity 为空,无法跳转"); return; } mPendingPromise = promise; // 保存 promise,待 onActivityResult 回调时使用 Intent intent = new Intent(currentActivity, SecondActivity.class); intent.putExtra("message", message); currentActivity.startActivityForResult(intent, REQUEST_CODE); } // 2.1.3 onActivityResult 回调处理 @Override public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_CODE) { if (mPendingPromise == null) return; if (resultCode == Activity.RESULT_OK) { String result = data.getStringExtra("result"); WritableMap map = Arguments.createMap(); map.putString("result", result); mPendingPromise.resolve(map); } else { mPendingPromise.reject("RESULT_ERROR", "SecondActivity 返回失败"); } mPendingPromise = null; } } @Override public void onNewIntent(Intent intent) { // 不需要处理 } }
getName()
:返回给 JS 侧使用的模块名,此处命名为"MyModule"
。startSecondActivity()
:无参启动。startSecondActivityForResult(String, Promise)
:带一个message
参数并期望在SecondActivity
结束时通过Promise
将结果回调给 JS。onActivityResult(...)
:当原生Activity
返回结果时,使用mPendingPromise.resolve(...)
或reject(...)
将结果传回 JS。
新建
SecondActivity.java
在同一目录下新建SecondActivity.java
,用于测试跳转与返回。// android/app/src/main/java/com/myapp/SecondActivity.java package com.myapp; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.TextView; import androidx.annotation.Nullable; public class SecondActivity extends Activity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); // 我们示例将新建对应布局 TextView tvMessage = findViewById(R.id.tv_message); Button btnReturn = findViewById(R.id.btn_return); // 获取从 RN 传过来的 message String message = getIntent().getStringExtra("message"); if (message != null) { tvMessage.setText("来自RN的参数: " + message); } else { tvMessage.setText("Hello from SecondActivity"); } // 点击按钮后返回结果给 RN btnReturn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent resultIntent = new Intent(); resultIntent.putExtra("result", "原生Activity返回的数据"); setResult(RESULT_OK, resultIntent); finish(); } }); } }
setContentView(R.layout.activity_second)
:需要在android/app/src/main/res/layout/
下新建一个activity_second.xml
布局。
创建
activity_second.xml
布局<!-- android/app/src/main/res/layout/activity_second.xml --> <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="16dp" android:gravity="center"> <TextView android:id="@+id/tv_message" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello from SecondActivity" android:textSize="18sp" /> <Button android:id="@+id/btn_return" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="24dp" android:text="点击返回给RN" /> </LinearLayout>
注册
MyModule
到 React Native
在MainApplication.java
中,将MyModule
添加到包列表。// android/app/src/main/java/com/myapp/MainApplication.java package com.myapp; import android.app.Application; import com.facebook.react.PackageList; import com.facebook.react.ReactApplication; import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactPackage; import com.facebook.react.shell.MainReactPackage; import com.facebook.soloader.SoLoader; import java.util.List; public class MainApplication extends Application implements ReactApplication { private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { @Override public boolean getUseDeveloperSupport() { return BuildConfig.DEBUG; } @Override protected List<ReactPackage> getPackages() { List<ReactPackage> packages = new PackageList(this).getPackages(); // 1. 手动添加 MyPackage packages.add(new MyPackage()); return packages; } @Override protected String getJSMainModuleName() { return "index"; } }; @Override public ReactNativeHost getReactNativeHost() { return mReactNativeHost; } @Override public void onCreate() { super.onCreate(); SoLoader.init(this, /* native exopackage */ false); } }
创建
MyPackage.java
// android/app/src/main/java/com/myapp/MyPackage.java package com.myapp; import com.facebook.react.ReactPackage; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.uimanager.ViewManager; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class MyPackage implements ReactPackage { @Override public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) { List<NativeModule> modules = new ArrayList<>(); modules.add(new MyModule(reactContext)); return modules; } @Override public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) { return Collections.emptyList(); } }
MyPackage
将MyModule
注册进 RN 桥接。
Gradle 同步并编译
cd android ./gradlew clean ./gradlew assembleDebug cd ..
- 确保编译无误,再运行
npx react-native run-android
,以验证原生修改未出错。
- 确保编译无误,再运行
2.2 在 React Native 端:调用 MyModule
引入 NativeModules
在 RN 端(例如App.js
)引入并调用:// App.js import React, { useState } from 'react'; import { View, Text, Button, StyleSheet, NativeModules } from 'react-native'; const { MyModule } = NativeModules; export default function App() { const [result, setResult] = useState(null); // 无参跳转 const goToSecond = () => { MyModule.startSecondActivity(); }; // 带参跳转并接收返回 const goToSecondForResult = async () => { try { const res = await MyModule.startSecondActivityForResult('你好,原生!'); setResult(res.result); } catch (e) { console.error(e); } }; return ( <View style={styles.container}> <Text style={styles.title}>React Native 与原生 Activity 跳转示例</Text> <Button title="无参跳转到 SecondActivity" onPress={goToSecond} /> <View style={styles.spacer} /> <Button title="带参跳转并返回结果" onPress={goToSecondForResult} /> {result && ( <> <View style={styles.spacer} /> <Text style={styles.resultText}>返回结果:{result}</Text> </> )} </View> ); } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 16 }, title: { fontSize: 20, fontWeight: 'bold', marginBottom: 24, textAlign: 'center' }, spacer: { height: 16 }, resultText: { marginTop: 16, fontSize: 16, color: 'green' }, });
- 点击第一个按钮,RN 调用
MyModule.startSecondActivity()
,直接打开SecondActivity
; - 点击第二个按钮,RN 调用
MyModule.startSecondActivityForResult('你好,原生!')
,并等待返回结果; SecondActivity
中拿到参数后在界面显示,点击“返回给RN”按钮,将结果通过Intent
携带并回传,RN 端await
后显示在页面上。
示例对话图解:
[RN App] --(startSecondActivityForResult)--> [SecondActivity] | | |<-------- onActivityResult(result)---------| | setResult("原生Activity返回的数据")
- 点击第一个按钮,RN 调用
三、场景二:原生 Activity 启动 React Native 界面(Deep Linking 与 Intent)
除了 RN 调用原生页面,有时需要在原生侧直接跳转到 RN 界面。例如:在某个业务模块里,点击“编辑”按钮需要从原生 Activity 跳回 RN 页面并携带参数。常见做法包括:
- Deep Linking(使用 URI 方式指向 RN 页面)
- Explicit Intent + 标记跳转参数
下面分别介绍这两种方式。
3.1 Deep Linking(URI Scheme)
Deep Linking 通过 URL Scheme 或 App Link 唤起应用,并由 RN 的 Linking
模块监听到对应路径,从而导航到 JS 路由中指定页面。
在
AndroidManifest.xml
中配置 Deep Link
我们在MainActivity
的<intent-filter>
中已添加如下内容:<intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="myapp" android:host="open" android:pathPrefix="/profile" /> </intent-filter>
当其他原生页面或外部应用执行:
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("myapp://open/profile?userId=42")); startActivity(intent);
就会触发 RN 的主 Activity 启动并携带该 URI。
在 RN 端监听 Linking
在App.js
或最顶部的组件中,使用Linking
模块监听url
事件,并根据路径进行导航(例如使用 React Navigation)。// App.js import React, { useEffect } from 'react'; import { View, Text, Linking, Alert } from 'react-native'; import { NavigationContainer } from '@react-navigation/native'; import { createStackNavigator } from '@react-navigation/stack'; function HomeScreen({ navigation }) { return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Home Screen</Text> </View> ); } function ProfileScreen({ route }) { const { userId } = route.params || {}; return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Profile Screen for userId: {userId}</Text> </View> ); } const Stack = createStackNavigator(); export default function App() { useEffect(() => { // 1. 获取应用初始启动时可能携带的 URL Linking.getInitialURL().then((url) => { if (url) { handleDeepLink({ url }); } }); // 2. 监听在应用已启动时的新 URL const subscription = Linking.addEventListener('url', handleDeepLink); return () => subscription.remove(); }, []); const handleDeepLink = ({ url }) => { // 解析 URL,例如: myapp://open/profile?userId=42 const parsed = Linking.parse(url); if (parsed.path === 'open/profile') { const userId = parsed.queryParams.userId; // 通过 navigation 导航到 ProfileScreen,传递参数 navigationRef.current?.navigate('Profile', { userId }); } else { Alert.alert('未知 Deep Link', url); } }; // 需定义 navigationRef 用于从外部调用 navigation const navigationRef = React.useRef(); return ( <NavigationContainer ref={navigationRef}> <Stack.Navigator initialRouteName="Home"> <Stack.Screen name="Home" component={HomeScreen} /> <Stack.Screen name="Profile" component={ProfileScreen} /> </Stack.Navigator> </NavigationContainer> ); }
Linking.getInitialURL()
:当应用冷启动时,如果包含 Deep Link,会在这里拿到 URL。Linking.addEventListener('url', callback)
:当应用后台时再次唤起带有 Deep Link 的 URL,会触发此监听器。Linking.parse(url)
:RN 内置解析函数,将 URL 拆分为{ scheme, hostname, path, queryParams }
。- 由此我们可以根据
path === 'open/profile'
导航到ProfileScreen
,并传递userId
参数。
Deep Linking 图解:
┌──────────────────────────┐ │ 原生/第三方 App 或 网页 │ │ Intent(url=myapp://open/profile?userId=42) │ └──────────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ Android 系统根据 <intent-filter> 匹配 │ │ → 唤起 RN MainActivity 并携带该 URL │ └─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ React Native: Linking.getInitialURL() │ │ → 获取 URL 并分发到 handleDeepLink │ └─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ RN NavigationContainer: navigate('Profile', { userId: 42 }) │ └─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ 加载 ProfileScreen 并显示 userId 信息 │ └─────────────────────────────────────────┘
3.2 Explicit Intent(原生直接跳转 RN 页面)
有时不想依赖 URL Scheme,希望在原生 Activity
中直接启动 RN 页面并携带参数。我们可以在原生侧构造一个 Intent
启动 MainActivity
(RN 主 Activity),并在 Intent
中放置额外参数。然后在 RN 端通过 getInitialURL()
或 Linking.getInitialURL()
获取这些参数。
在原生侧启动 MainActivity
例如在另一个原生Activity
(如NativeTriggerActivity
)中:// android/app/src/main/java/com/myapp/NativeTriggerActivity.java package com.myapp; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button; public class NativeTriggerActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_native_trigger); Button btnOpenRN = findViewById(R.id.btn_open_rn); btnOpenRN.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(NativeTriggerActivity.this, MainActivity.class); // 给 RN 传递参数: intent.putExtra("screen", "Profile"); intent.putExtra("userId", "99"); startActivity(intent); finish(); } }); } }
MainActivity
(ReactActivity)默认会加载index.js
,并且如果Intent
中带有额外键值对,它们会注入到 RN 侧的启动 URL 或 initialProps 中。
在
MainActivity.java
处理 Intent 并传递给 JSMainActivity
通常继承自ReactActivity
,我们可以重写getLaunchOptions()
方法,将Intent
中的参数传递给 JS 端:// android/app/src/main/java/com/myapp/MainActivity.java package com.myapp; import android.content.Intent; import android.os.Bundle; import com.facebook.react.ReactActivity; import com.facebook.react.ReactActivityDelegate; import com.facebook.react.ReactRootView; import java.util.HashMap; import java.util.Map; public class MainActivity extends ReactActivity { @Override protected String getMainComponentName() { return "MyApp"; } // 重写:向 JS 传递 initialProps @Override protected Bundle getLaunchOptions() { Intent intent = getIntent(); Bundle initialProps = new Bundle(); if (intent != null && intent.hasExtra("screen")) { initialProps.putString("screen", intent.getStringExtra("screen")); } if (intent != null && intent.hasExtra("userId")) { initialProps.putString("userId", intent.getStringExtra("userId")); } return initialProps; } }
在 RN 端读取 initialProps 并导航
在index.js
或App.js
中,通过AppRegistry.registerComponent
时,RN 会自动将initialProps
传入根组件App
。我们可在App.js
中读取这些 props 并启动导航。// App.js import React, { useEffect } from 'react'; import { View, Text, Platform } from 'react-native'; import { NavigationContainer } from '@react-navigation/native'; import { createStackNavigator } from '@react-navigation/stack'; function HomeScreen() { return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Home Screen</Text> </View> ); } function ProfileScreen({ route }) { const { userId } = route.params || {}; return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Profile for userId: {userId}</Text> </View> ); } const Stack = createStackNavigator(); export default function App(props) { const { screen, userId } = props; // 从 native getLaunchOptions 传入 return ( <NavigationContainer> <Stack.Navigator initialRouteName="Home"> <Stack.Screen name="Home" component={HomeScreen} /> <Stack.Screen name="Profile" component={ProfileScreen} /> </Stack.Navigator> {/* 一旦组件挂载,就检查初始参数,进行导航 */} {screen === 'Profile' && Platform.OS === 'android' && ( // 注意:需要访问 navigationRef 才能导航,在此简化示例 <RedirectToProfile userId={userId} /> )} </NavigationContainer> ); } // RedirectToProfile.js(简化示例,实际需使用 navigationRef) import { useEffect } from 'react'; import { useNavigation } from '@react-navigation/native'; export function RedirectToProfile({ userId }) { const navigation = useNavigation(); useEffect(() => { if (userId) { navigation.navigate('Profile', { userId }); } }, [userId]); return null; }
MainActivity.getLaunchOptions()
会将原生传进来的screen
、userId
作为initialProps
传给 RN 根组件。- 在 RN 渲染时,
props.screen === 'Profile'
,通过一个类似RedirectToProfile
的逻辑立即导航到ProfileScreen
,并传递userId
。
显式 Intent 跳转图解:
┌─────────────────────────────┐ │ NativeTriggerActivity 点击 │ │ Intent(MainActivity, extras.{screen:Profile,userId:99}) │ └─────────────────────────────┘ │ ▼ ┌─────────────────────────────┐ │ MainActivity 加载 RN Bundle │ │ getLaunchOptions 读取 extras │ │ → initialProps = {screen, userId} │ └─────────────────────────────┘ │ ▼ ┌─────────────────────────────┐ │ RN App.js 收到 props.screen=Profile │ │ 立即 navigate('Profile',{userId}) │ └─────────────────────────────┘ │ ▼ ┌─────────────────────────────┐ │ ProfileScreen 显示 userId=99 │ └─────────────────────────────┘
四、场景三:React Navigation 与原生跳转结合示例
大多数 RN 项目都会使用 React Navigation 管理界面跳转。在上述 Deep Linking 与 Explicit Intent 中,我们对 React Navigation 的集成方式略显简化。接下来给出一个更完整的、在 App 启动时即检查 initialProps 并导航到指定页面的示例。
4.1 安装 React Navigation
yarn add @react-navigation/native @react-navigation/stack
yarn add react-native-screens react-native-safe-area-context
在 Android 端,确保在 MainActivity.java
中添加 ReactActivityDelegate
配置以启用 react-native-screens
:
// android/app/src/main/java/com/myapp/MainActivity.java
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactRootView;
import com.swmansion.rnscreens.RNScreensPackage; // 引入 react-native-screens
public class MainActivity extends ReactActivity {
@Override
protected String getMainComponentName() {
return "MyApp";
}
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new ReactActivityDelegate(this, getMainComponentName()) {
@Override
protected ReactRootView createRootView() {
// 启用 react-native-screens
return new ReactRootView(MainActivity.this);
}
};
}
@Override
protected Bundle getLaunchOptions() {
Intent intent = getIntent();
Bundle initialProps = new Bundle();
if (intent != null && intent.hasExtra("screen")) {
initialProps.putString("screen", intent.getStringExtra("screen"));
}
if (intent != null && intent.hasExtra("userId")) {
initialProps.putString("userId", intent.getStringExtra("userId"));
}
return initialProps;
}
}
4.2 App.js 完整示例
// App.js
import React, { useEffect, useRef, useState } from 'react';
import { View, Text, Button, Platform } from 'react-native';
import { NavigationContainer, useNavigationContainerRef } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
function HomeScreen() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
</View>
);
}
function ProfileScreen({ route }) {
const { userId } = route.params || {};
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Profile Screen for userId: {userId}</Text>
</View>
);
}
const Stack = createStackNavigator();
export default function App(props) {
const navigationRef = useNavigationContainerRef();
const [initialRoute, setInitialRoute] = useState('Home');
const [initialParams, setInitialParams] = useState({});
useEffect(() => {
// 1. 读取 initialProps
const { screen, userId } = props;
if (screen === 'Profile' && userId) {
setInitialRoute('Profile');
setInitialParams({ userId });
}
}, [props]);
// 2. 使用 React Navigation 设置 initialState
const linking = {
prefixes: [], // 不再使用 Deep Linking 示例
config: {
initialRouteName: initialRoute,
screens: {
Home: 'home',
Profile: 'profile/:userId',
},
},
};
return (
<NavigationContainer ref={navigationRef} linking={linking}>
<Stack.Navigator initialRouteName={initialRoute}>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen
name="Profile"
component={ProfileScreen}
initialParams={initialParams} // 这里传入 initialParams
/>
</Stack.Navigator>
</NavigationContainer>
);
}
initialRoute
与initialParams
来自于原生getLaunchOptions()
中传递的 props。- 在
Stack.Navigator
中,将initialRouteName={initialRoute}
,并对Profile
屏幕设置initialParams={initialParams}
。
这样在启动时,若 props.screen === 'Profile'
,RN 会直接打开 ProfileScreen
并显示 userId
,否则进入默认的 HomeScreen
。
五、常见问题与性能优化
5.1 Activity 频繁重启
- 如果 RN 端多次调用
startActivity()
/startActivityForResult()
,可能会造成多个SecondActivity
重叠或频繁打开。 - 建议在跳转前先检查当前是否已有该
Activity
在栈顶,必要时可设置launchMode="singleTop"
或在 Intent 中添加Intent.FLAG_ACTIVITY_SINGLE_TOP
等 flag。
5.2 回退栈管理
RN 与原生 Activity 共存时,Back 按钮的行为需要特别注意:
- 在
SecondActivity
中按 “返回” 会自动调用finish()
回到 RN。 - 如果在 RN 页面中集成了硬件返回键监听(
BackHandler
),需要先判断是否要拦截返回事件,否则可能会同时触发 RN 和原生的 Back 逻辑,导致页面回退异常。
- 在
5.3 性能与内存
- 每次启动
Activity
都会涉及 RN JS Bundle 再次初始化,可能会有短暂的白屏或性能抖动。 - 可以使用 Single Activity + Fragment 方案(将原生页面做成 Fragment),然后在同一个 Activity 内切换 Fragment,与 RN 协作更顺畅。
- 对于简单交互,考虑使用 React Native Navigation 这类原生导航库,直接将 RN 界面作为原生
Activity
或Fragment
,以获得更好的性能与过渡动画。
六、小结与学习路径
本文从最基础的 RN ↔ 原生 Activity 跳转案例入手,详细介绍了以下核心内容:
RN 调用原生
Activity
- 无参跳转:
getCurrentActivity().startActivity(intent)
- 有参跳转并返回结果:
startActivityForResult(intent, requestCode)
+Promise
回调
- 无参跳转:
原生
Activity
调用 RN 界面- Deep Linking:通过
LINKING
与<intent-filter>
实现 URL Scheme 唤起 RN 并导航 - Explicit Intent:在原生侧构造
Intent
并通过getLaunchOptions()
将参数传给 RN。
- Deep Linking:通过
React Navigation 与原生跳转结合
- 在
App.js
中根据initialProps
决定初始路由与参数,结合navigationRef
实现“原生 → RN”导航。
- 在
常见问题与优化
- Activity 重叠、回退冲突、性能优化等实战建议。
通过本文示例,你应能够完成以下几项关键能力:
- 在 RN 代码中通过
NativeModules
调用 Android 原生页面,并在原生页面中返回结果给 JS。 - 在 Android 原生侧通过 Intent/Deep Linking 唤起 RN 页面,并将参数传递给 JS 层。
- 在 RN 端结合 React Navigation,根据不同启动参数控制首屏路由和参数传递。
- 解决混合导航场景下的回退、栈管理与性能问题。
下一步推荐学习内容:
- React Native Navigation(Wix、React Navigation)更深入的自定义动画与原生交互。
- 单 Activity + Fragment 架构:将原生页面封装为
Fragment
,与 RN 同在一个Activity
中管理。 - 跨平台 Deep Linking:在 iOS 与 Android 上同时配置 Deep Linking、Universal Links 与 App Links,打造统一路径方案。
- React Native 原生 UI 组件开发:学习如何自定义原生
View
或Fragment
,并在 RN 中使用requireNativeComponent
引用。
希望本文能帮你构建 React Native 与 Android 原生无缝衔接的页面导航体系,让你快速在项目中实现混合导航、原生跳转与 RN 交互。
评论已关闭