在过去的一年中,我的主要工作聚焦于将一个基于React Native(RN)框架开发的Android应用逐步转化为原生应用。我们采取了分阶段实施的策略,首先着手将应用中的二级和三级页面的核心功能以原生代码的形式进行重构。在这一过程中,我们通过在React Native页面中拉起原生Activity来实现这一目标,采用的是多Activity的应用架构。具体而言,每一个经过改造的功能模块在用户界面(UI)层面都对应一个独立的Activity。需要注意的是,这一原生化过程仅支持Android平台,因此未涉及iOS平台的相关处理。
通过将关键功能模块以原生代码的形式实现,我们旨在提升应用的性能和用户体验。这种方法不仅确保了应用在不同设备上的兼容性,而且能够充分利用原生平台的最新特性和优化,使得应用运行更为流畅。此外,原生化过程还有助于开发者更精确地控制应用资源的使用,减少不必要的电量消耗和内存占用,从而延长设备的使用寿命。
经过一段时间的精心改造,我们已经基本实现了应用核心功能模式的原生化。接下来的步骤是将应用中的MainActivity/RNActivity替换为新的原生Activity实例(NewMainActivity)。这一过程相对简单,只需为NewMainActivity配置启动时的Launcher intent-filter即可。随后,我们可以移除与React Native(RN)相关的代码,包括业务代码和RN框架代码。
尽管如此,我们仍然希望能够复用React Native的某些功能或页面,因此需要在原生Activity中启动RNActivity,并能够指定显示的React Native页面(即进行路由控制)。
为此,我们需要解决两个技术问题:首先,如何在启动RNActivity时传递参数,确保这些参数能够传递至React Native中的js根组件;其次,如何在React Native应用页面中动态配置路由,以便打开特定页面。
在React Native应用开发中,通常情况下,RNActivity会负责加载JavaScript代码。这些JavaScript代码的入口文件通常被命名为index.js。在index.js文件中,开发者会注册整个React Native应用的主组件或根组件。这个根组件通常会通过单独的文件来定义,而这个单独的文件一般被命名为app.js。
在app.js文件中,开发者会定义一个根组件,这个根组件在整个应用中起着至关重要的作用。它不仅承载了应用的初始界面,还负责管理整个应用的导航流程。为了实现页面之间的跳转和导航管理,开发者通常会在根组件中使用navigation组件。通过这个navigation组件,开发者可以方便地控制页面之间的跳转逻辑,从而实现一个流畅且用户友好的导航体验。
如上图所示,我们可以看到,首先遇到的第一个技术问题相对来说比较简单。这个问题涉及到两个Activity之间的参数传递。具体来说,当我们在一个Activity中需要启动另一个Activity时,可以通过Intent对象来传递所需的参数。在调用startActivity方法时,我们只需要在Intent对象中添加需要传递的参数,然后将这个Intent对象传递给startActivity方法即可。这种方式在Android开发中非常常见,也是实现Activity间通信的一种基本方法。
然而,第二个技术问题相对来说就比较复杂了。这个问题涉及到如何将RNActivity中的参数传递到根组件app中。app组件获取到相关的参数后,怎么配置路由指定显示的页面,由显示页面时还要传参数给页面。我们就一步一步来实现。
在这里,我们不再深入探讨在初始化React Native Activity时如何设置参数,因为该过程相对直接。我们假定已经传递了两个参数:一个名为pageName的数据和一个名为userId的参数。随后,我们需在React Native Activity的onCreate方法中处理这两个参数。同时,在getLaunchOptions方法中,我们应将这些参数纳入initialProperties中。应用程序的props将使我们能够访问initialProperties中设定的参数。因此,我们可以在应用程序的其他部分利用这些参数,以便根据传递的参数执行相应的操作和处理。我们通过设置Stack.Navigator的initialRouteName 属性来指定打开/导航至特定页面,同时利用Stack.Screen的initialParams属性来传递打开页面时所需的参数,具体代码示例如下:
1、需要在React Native的Android原生桥接代码中处理从NewMainActivity传递过来的参数,并将它们放入ReactActivityDelegate的getLaunchOptions中。以下是如何实现这一点的步骤:
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;
public class MyReactActivityDelegate extends ReactActivityDelegate {
private String pageName;
private String userId;
public MyReactActivityDelegate(ReactActivity activity, String mainComponentName) {
super(activity, mainComponentName);
}
// 这个方法用于设置启动参数
public void setLaunchOptions(String pageName, String userId) {
this.pageName = pageName;
this.userId = userId;
}
@Override
protected WritableMap getLaunchOptions() {
WritableMap launchOptions = Arguments.createMap();
if (userId != null) {
launchOptions.putString("userId", userId);
}
if (pageName != null) {
launchOptions.putString("pageName", pageName);
}
return launchOptions;
}
}
2、然后,在你的RNActivity中,你需要使用MyReactActivityDelegate来启动React应用。
import com.facebook.react.ReactActivity;
public class RNActivity extends ReactActivity {
@Override
protected String getMainComponentName() {
return "myApp"; // 这里应该是你的主组件名
}
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
// 创建自定义的ReactActivityDelegate实例
MyReactActivityDelegate delegate = new MyReactActivityDelegate(this, getMainComponentName());
// 从Intent中获取参数
String pageName = getIntent().getStringExtra("pageName");
String userId = getIntent().getStringExtra("userId");
// 设置启动参数
delegate.setLaunchOptions(pageName, userId);
return delegate;
}
}
3、在React Native代码中,你可以在App.js中接收这些参数,并根据参数来设置路由。
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import React from 'react';
const Stack = createNativeStackNavigator();
function App({ userId, pageName }) {
// 使用解构赋值从props中获取userId和pageName
return (
<NavigationContainer>
<Stack.Navigator initialRouteName={pageName}>
<Stack.Screen
name="Home"
component={HomePage}
initialParams={{ userId }}
/>
{/* 其他页面路由配置 */}
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;