[ReactNative翻译]使用 React Native 轻松开发 Android 棕地应用程序

149 阅读5分钟

我们先来回答一个问题:什么是棕地应用?

所谓 "棕地",是指利用现有元素建造的东西。棕地 "一词最初用于城市规划,描述可以重新利用的区域,后来被移动应用开发所采用。棕地应用程序基本上是指并非从零开始构建的应用程序。

它的实际含义是什么?这意味着我们希望将 React Native 应用程序作为已使用 Java 或 Kotlin 等技术开发的本地应用程序的一部分来运行。换句话说,我们希望将 React Native 集成到现有的应用程序中。 ‍

它是如何工作的?

假设我们有一个原生 Android 应用程序,我们想使用已经实现的 React Native 应用程序创建一个新的屏幕。该屏幕将导航到 React Native 应用程序中的另一个屏幕。

1.gif ‍ 为了让它正常工作,我们需要做几件事。

在项目中添加 React Native:

  • 首先,我们需要在项目的 build.gradle 中为本地 React Native maven 目录添加一个条目。 让我们把它添加到 allprojects 块,位于其他 maven 仓库之上
allprojects {
    repositories {
        maven {
            // All of React Native (JS, Android binaries) is installed from npm
            url "$rootDir/../node_modules/react-native/android"
        }
    }
}
  • 将 node_modules 中的 react-native 添加为应用程序 build.gradle 的依赖项(感谢上一点的修改)
dependencies {
  implementation 'com.android.support:appcompat-v7:27.1.1'
  implementation "com.facebook.react:react-native:+"
}

  • 在应用程序的 build.gradle 末尾添加自动链接命令
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle");
applyNativeModulesAppBuildGradle(project, "../")

运行 React Native 应用程序:

为了运行 React Native 应用程序,我们需要

  • 创建一个实现 DefaultHardwareBackBtnHandler 接口的 Activity,并使用 Theme.AppCompat.Light.NoActionBar 主题,因为某些 React Native UI 组件依赖于它。
public class ReactNativeActivity extends Activity implements DefaultHardwareBackBtnHandler { 
  private ReactRootView mReactRootView;
  private ReactInstanceManager mReactInstanceManager;
 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
  }
  @Override
  public void invokeDefaultOnBackPressed() {
    super.onBackPressed();
  }
}
```‍

```xml
<activity
  android:name=".ReactNativeActivity"
  android:theme="@style/Theme.AppCompat.Light.NoActionBar"
/>
  • 在 onCreate 方法中创建 ReactRootView 实例
mReactRootView = new ReactRootView(this);
  • 使用所需参数创建 ReactInstanceManager 实例
mReactInstanceManager = ReactInstanceManager.builder()
  .setCurrentActivity(this)
  .setApplication(getApplication())
  .setBundleAssetName("index.android.bundle")
  .setJSMainModulePath("index")
  .build();
  • 使用 startReactApplication 方法在创建的根视图中启动 react-native 应用程序,其中第二个参数与 index.js 文件中的 AppRegistry.registerComponent() 中的字符串相同。
mReactRootView.startReactApplication(mReactInstanceManager, "MainComponent", null);
  • 将 react-native 根视图设置为活动内容视图
setContentView(mReactRootView);

现在,我们可以通过启动 ReactNativeActivity 来运行我们的 react-native 应用程序,但我们还需要添加一些代码才能让所有功能正常运行:)

  • 将活动生命周期回调传递给 ReactInstanceManager 和 ReactRootView
@Override
protected void onPause() {
  super.onPause();
  if (mReactInstanceManager != null) {
    mReactInstanceManager.onHostPause(this);
  }
}
 
@Override
protected void onResume() {
  super.onResume();
  if (mReactInstanceManager != null) {
    mReactInstanceManager.onHostResume(this, this);
  }
}
 
@Override
protected void onDestroy() {
  super.onDestroy();
  if (mReactInstanceManager != null) {
    mReactInstanceManager.onHostDestroy(this);
  }
  if (mReactRootView != null) {
    mReactRootView.unmountReactApplication();
  }
}
  • 传递返回按钮事件,以避免在按下硬件返回按钮时关闭 ReactNativeActivity,并在 JS 端支持硬件返回按钮
@Override
 public void onBackPressed() {
  if (mReactInstanceManager != null) {
    mReactInstanceManager.onBackPressed();
  } else {
    super.onBackPressed();
  }
}

注:即使做了这样的改动,我们也没有一种无需使用硬件返回按钮即可返回原生屏幕的机制。这个问题可以通过 react-native 原生模块来解决,该模块会将方法暴露给 JS,从而完成 ReactNativeActivity。

其他要点:

  • 在调试模式下,我们需要打包程序来提供 JS 捆绑包。要做到这一点,我们需要互联网权限。在 AndroidManifest.xml 文件中添加以下一行。
<uses-permission android:name="android.permission.INTERNET" />

注意:从 Android 9(API 级别 28)开始,默认情况下禁用明文流量。这将阻止您的应用程序连接到 React Native 打包器。要解决这个问题,我们需要在调试 AndroidManifest.xml 中添加 usesCleartextTraffic 选项。

<application
  android:usesCleartextTraffic="true"
  tools:targetApi="28"
/>
  • 要在 react-native 应用程序中启用开发菜单,请在 AndroidManifest.xml 中添加 DevSettingsActivity
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />

只有在调试构建时,我们才会从开发服务器重新加载 JS 代码,因此在发布构建时可以删除所有这些附加点。

我认为,几乎所有的 react-native Android 应用都会重复上述步骤。但就不能做得更简单一些吗?我们真的需要关心 onPause、onDestroy 等函数的传递吗?

当然,这可能更简单:) 我们可以使用 react-native-brownfield 软件包,它可以帮助我们解决导航问题,并提供运行 react-native 应用程序的良好 API。

如何使用 React Native brownfield?

‍ 我们想要实现与之前完全相同的效果,但我们将使用 react-native-brownfield 库

1.gif

在项目中添加 React Native:

理论与我在本文前半部分所述的相同,但增加了一些功能。为了使用 react-native-brownfield 在原生环境中运行 react-native 应用程序,我们需要将该软件包作为依赖项添加到 package.json 中:

npm install @callstack/react-native-brownfield

‍ 或

yarn add @callstack/react-native-brownfield

接下来,我们需要添加 react-native 应用程序,就像在添加 react-native 到项目点时所做的那样。

其他步骤:

这里也需要添加所有附加步骤,但仅用于调试构建。

运行 React Native 应用程序:

现在好戏开始了) 我们可以使用 ReactNativeBrownfield 软件包,而无需自己创建根视图和实例管理器。由于 build.gradle 和 react-native-brownfield 已添加了自动链接脚本,因此我们可以使用该软件包,而无需付出额外的努力。

  • 在 AndroidManifest.xml 文件中添加 ReactNativeActivity
<activity android:name="com.callstack.reactnativebrownfield.ReactNativeActivity"/>
  • 以上下文作为第一个参数初始化 react-native 应用程序(如下代码段所示)
List<ReactPackage> packages = new PackageList(getApplication()).getPackages();
HashMap<String, Object> options = new HashMap<>();
options.put("packages", packages);
options.put("mainModuleName", "example/index");
 
ReactNativeBrownfield.initialize(this, options);
  • 启动 react-native 应用程序
ReactNativeBrownfield.getShared().startReactNative(init -> {
    Log.d("RN", "react-native app has started :)");
});
  • 以 context 和 moduleName 为参数创建 ReactNativeActivity Intent,并启动活动。
Intent intent = ReactNativeActivity.createReactActivityIntent(
  this,
  "MainComponent"
);
startActivity(intent);

瞧! 我们已经准备就绪) 我们不必担心传递活动生命周期、返回按钮事件或从 react-native 返回到原生屏幕的原生模块。所有这些都由 react-native-brownfield 软件包处理。

结论:

‍ 最后,在现有的原生应用中使用 react-native 是一件很流行的事情,尤其是那些有多个团队在多个平台上开发同一应用的大型企业。 有了 react-native-brownfield,集成变得更容易、更愉快,而且每个 react-native-brownfield 应用程序中都存在的一些问题也迎刃而解:)

使用 React Native brownfield 的工作示例位于示例目录中。

作为 React Native Show 的一部分,我们发布了一个播客,专门介绍使用 React Native 进行棕地开发的未来。


www.deepl.com 翻译