在React Native的应用场景中,有时候一个APP只有部分页面是由React Native实现的,好比:咱们经常使用的携程App,它的首页下的不少模块都是由React Native实现的,这种开发模式被称为混合开发。html
混合开发的一些其余应用场景:java
在原有项目中加入RN页面,在RN项目中加入原生页面node
原生页面中嵌入RN模块react
RN页面中嵌入原生模块android
以上这些都属于React Native混合开发的范畴,那么如何进行React Native混合开发呢?ios
在这篇文章中我将向你们介绍React Native混合开发的流程,须要掌握的技术,以及一些经验技巧,与该文章配套的还有React Native与Android 混合开发讲解的视频教程。git
React Native混合开发的教程咱们分为上下两篇,上篇主要介绍如何在现有的Android应用上进行React Native混合开发,下篇主要介绍如何在现有的iOS应用上进行React Native混合开发。github
将React Native集成到现有的Android应用中须要以下几个主要步骤:npm
在作混合开发以前咱们首先须要建立一个没有Android和iOS模块的React Native项目。咱们能够经过两种方式来建立一个这样的React Native项目:json
npm
安装react-native的方式添加一个React Native项目;react-native init
来初始化一个React Native项目;npm
安装react-native的方式添加一个React Native项目第一步:建立一个名为RNHybridApp
的目录,而后在该目录下添加一个包含以下信息的package.json
:
{ "name": "RNHybrid", "version": "0.0.1", "private": true, "scripts": { "start": "node node_modules/react-native/local-cli/cli.js start" } }
第二步:在为package.json添加react-native
在该目录下执行:
npm install --save react-native
执行完上述命令以后,你会看到以下警告:
其中,有一条警告npm WARN react-native@0.55.4 requires a peer of react@16.3.1 but none is installed
告诉咱们须要安装react@16.3.1
:
npm install --save react@16.3.1
至此,一个不含Android和iOS模块的React Native项目便建立好了。此过程所遇到的更多问题可查阅:React Native与Android 混合开发讲解的视频教程
提示:npm 会在你的目录下建立一个
node_modules
,node_modules
体积很大且是动态生成了,建议将其添加到.gitignore
文件中;
除了上述方式以外,咱们也能够经过react-native init
命令来初始化一个React Native项目。
react-native init RNHybrid
上述命令会初始化一个完成的名为RNHybrid的React Native项目,而后咱们将里面的android
和ios
目录删除,替换成已存在Android和iOS项目。
在上文中咱们已经建立了个一个React Native项目,接下来咱们来看一下如何将这个React Native项目和咱们已经存在的Native项目进行融合。
在进行融合以前咱们须要将已经存在的Native项目放到咱们建立的RNHybrid下,好比:我有一个名为RNHybridAndroid
的Android项目,将其放到RNHybrid目录下:
RNHybrid ├── RNHybridAndroid ├── package.json ├── node_modules └── .gitignore
接下来咱们须要为已经存在的RNHybridAndroid项目添加 React Native依赖,在RNHybrid/RNHybridAndroid/app/build.gradle
文件中添加以下代码:
dependencies { compile 'com.android.support:appcompat-v7:23.0.1' ... compile "com.facebook.react:react-native:+" // From node_modules }
而后,咱们为RNHybridAndroid项目配置使用的本地React Native maven目录,在RNHybrid/RNHybridAndroid/build.gradle
文件中添加以下代码:
allprojects { repositories { mavenLocal() maven { // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm url "$rootDir/../node_modules/react-native/android" } ... } ... }
提示:为确保你配置的目录正确,能够经过在Android Studio中运行Gradle sync 看是否有 “Failed to resolve: com.facebook.react:react-native:0.x.x” 的错误出现,没有错误则说明配置正确,不然说明配置路由有问题。 此过程所遇到的更多问题可查阅:React Native与Android 混合开发讲解的视频教程
接下来咱们为APP运行配置所须要的权限:检查你项目中的AndroidManifest.xml
文件中看是否有以下权限:
<uses-permission android:name="android.permission.INTERNET" />
若是没有,则须要将上述权限添加到AndroidManifest.xml
中。
另外,若是你须要用到RN的
Dev Settings
功能:
则须要在AndroidManifest.xml
文件中添加以下代码:
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
提示:上述图片就是RN 开发调试弹框中的
Dev Settings
功能,打开该功能会弹出上图的一个界面,这个界面就是DevSettingsActivity。
Android不能同时加载多种架构的so库,如今不少Android第三方sdks对abi的支持比较全,可能会包含armeabi, armeabi-v7a,x86, arm64-v8a,x86_64五种abi,若是不加限制直接引用会自动编译出支持5种abi的APK,而Android设备会从这些abi进行中优先选择某一个,好比:arm64-v8a,但若是其余sdk不支持这个架构的abi的话就会出现crash。以下图:
怎么解决呢:
在app/gradle
文件中添加以下代码:
defaultConfig { .... ndk { abiFilters "armeabi-v7a", "x86" } }
上述代码的意思是,限制打包的so库只包含armeabi-v7a
与x86
。此过程所遇到的更多问题可查阅:React Native与Android 混合开发讲解的视频教程
可参考:libgnustl_shared.so” is 32-bit instead of 64-bit
经过上述两步,咱们已经为RNHybridAndroid项目添加了React Native依赖,接下来咱们来开发一些JS代码。
在RNHybrid目录下建立一个index.js
文件并添加以下代码:
import { AppRegistry } from 'react-native'; import App from './App'; AppRegistry.registerComponent('App1', () => App);
上述代码,AppRegistry.registerComponent('App1', () => App);
目的是向React Native注册一个名为App1
的组件,而后我会在第四步给你们介绍如何在Android中加载并显示出这个组件。
另外,在上述代码中咱们引用了一个App.js
文件:
import React, { Component } from 'react'; import { Platform, StyleSheet, Text, View } from 'react-native'; type Props = {}; export default class App extends Component<Props> { render() { return ( <View style={styles.container}> <Text style={styles.welcome}> this is App </Text> </View> ); } } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#F5FCFF', }, welcome: { fontSize: 20, textAlign: 'center', margin: 10, } });
这个App.js
文件表明了咱们React Native的一个页面,在这个页面中显示了this is App
的文本内容。
以上就是为本次演示所添加的React Native代码,你也能够根据须要添加更多的React Native代码以及组件出来。
通过上述三、4步,咱们已经为RNHybridAndroid项目添加了React Native依赖,而且建立一些React Native代码和注册了一个名为App1
的组件,接下来咱们来学习下如何在RNHybridAndroid项目中使用这个App1
组件。
首先咱们须要建立一个Activity来做为React Native的容器,
public class RNPageActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler { private ReactRootView mReactRootView; private ReactInstanceManager mReactInstanceManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mReactRootView = new ReactRootView(this); mReactInstanceManager = ReactInstanceManager.builder() .setApplication(getApplication()) .setBundleAssetName("index.android.bundle") .setJSMainModulePath("index") .addPackage(new MainReactPackage()) .setUseDeveloperSupport(BuildConfig.DEBUG) .setInitialLifecycleState(LifecycleState.RESUMED) .build(); // 这个"App1"名字必定要和咱们在index.js中注册的名字保持一致AppRegistry.registerComponent() mReactRootView.startReactApplication(mReactInstanceManager, "App1", null); setContentView(mReactRootView); } @Override public void invokeDefaultOnBackPressed() { super.onBackPressed(); } }
参数说明
setBundleAssetName
:打包时放在assets
目录下的JS bundle包的名字,App release以后会从该目录下加载JS bundle;setJSMainModulePath
:JS bundle中主入口的文件名,也就是咱们上文中建立的那个index.js
文件;addPackage
:向RN添加Native Moudle,在上述代码中咱们添加了new MainReactPackage()
这个是必须的,另外,若是咱们建立一些其余的Native Moudle也须要经过addPackage
的方式将其注册到RN中。须要指出的是RN除了这个方法外,也提供了一个addPackages
方法用于批量向RN添加Native Moudle;setUseDeveloperSupport
:设置RN是否开启开发者模式(debugging,reload,dev memu),好比咱们经常使用开发者弹框;setInitialLifecycleState
:经过这个方法来设置RN初始化时所处的生命周期状态,通常设置成LifecycleState.RESUMED
就行,和下文讲的Activity容器的生命周期状态关联;mReactRootView.startReactApplication
:它的第一个参数是mReactInstanceManager
,第二个参数是咱们在index.js
中注册的组件的名字,第三个参数接受一个Bundle
来做为RN初始化时传递给JS的初始化数据,它的具体用法我会在React Android 混合开发讲解的视频教程中再具体的讲解;AndroidManifest.xml
注册一个RNPageActivityAndroid系统要求,每个要打开的Activity都要在AndroidManifest.xml
中进行注册:
<activity android:name=".RNPageActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
上述代码中咱们为
RNPageActivity
添加了一个@style/Theme.AppCompat.Light.NoActionBar
类型的theme,这也是React Native UI组件所要求的主题。
一个 ReactInstanceManager能够被多个activities或fragments共享,因此咱们须要在Activity的生命周期中回调ReactInstanceManager的对于的方法。
@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 public void onBackPressed() { if (mReactInstanceManager != null) { mReactInstanceManager.onBackPressed(); } else { super.onBackPressed(); } } @Override protected void onDestroy() { super.onDestroy(); if (mReactInstanceManager != null) { mReactInstanceManager.onHostDestroy(this); } if (mReactRootView != null) { mReactRootView.unmountReactApplication(); } }
从上述代码中你会发现有个不属于Activity生命周期中的方法onBackPressed
,添加它的目的主要是为了当用户单击手机的返回键以后将事件传递给JS,若是JS消费了这个事件,Native就再也不消费了,若是JS没有消费这个事件那么RN会回调invokeDefaultOnBackPressed
代码。
@Override public void invokeDefaultOnBackPressed() { super.onBackPressed(); }
此过程更细致的讲解可查阅:React Native与Android 混合开发讲解的视频教程
在RN中有个很好用的工具开发者菜单,咱们平时调试RN应用时对它的使用频率很高,接下来咱们来为RNHybridAndroid添加开着菜单。
public boolean onKeyUp(int keyCode, KeyEvent event) { if (getUseDeveloperSupport()) { if (keyCode == KeyEvent.KEYCODE_MENU) {//Ctrl + M 打开RN开发者菜单 mReactInstanceManager.showDevOptionsDialog(); return true; } } return super.onKeyUp(keyCode, event); }
经过上代码便可监听Ctrl + M
来打开RN开发者菜单。
另外,RN也提供了双击R来快速加载JS的功能,经过以下代码便可打开该功能:
public boolean onKeyUp(int keyCode, KeyEvent event) { if (getUseDeveloperSupport()) { if (keyCode == KeyEvent.KEYCODE_MENU) {//Ctrl + M 打开RN开发者菜单 mReactInstanceManager.showDevOptionsDialog(); return true; } boolean didDoubleTapR = Assertions.assertNotNull(mDoubleTapReloadRecognizer).didDoubleTapR(keyCode, getCurrentFocus()); if (didDoubleTapR) {//双击R 从新加载JS mReactInstanceManager.getDevSupportManager().handleReloadJS(); return true; } } return super.onKeyUp(keyCode, event); }
此过程更细致的讲解可查阅:React Native与Android 混合开发讲解的视频教程
在上述的代码中咱们都是经过ReactInstanceManager
来建立和加载JS的,而后重写了Activity的生命周期来对ReactInstanceManager
进行回调,另外,重写了onKeyUp
来启用开发者菜单等功能。
另外,查看RN的源码你会发如今RN sdk中有个叫ReactActivity
的Activity,该Activity是RN官方封装的一个RN容器。另外,在经过react-native init
命令初始化的一个项目中你会发现有个MainActivity
是继承ReactActivity
的,接下来咱们就来继承ReactActivity
来封装一个RN容器。
public class ReactPageActivity extends ReactActivity implements IJSBridge{ /** * Returns the name of the main component registered from JavaScript. * This is used to schedule rendering of the component. */ @Override protected String getMainComponentName() { return "App1"; } }
另外,咱们须要实现一个MainApplication
并添加以下代码:
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() { return Arrays.<ReactPackage>asList( new MainReactPackage() ); } @Override protected String getJSMainModuleName() { return "index"; } }; @Override public ReactNativeHost getReactNativeHost() { return mReactNativeHost; } @Override public void onCreate() { super.onCreate(); SoLoader.init(this, /* native exopackage */ false); } }
上述代码的主要做用是为ReactActivity
提供ReactNativeHost
,查看源码你会发如今ReactActivity
中使用了ReactActivityDelegate
,在ReactActivityDelegate
中会用到MainApplication
中提供的ReactNativeHost
:
protected ReactNativeHost getReactNativeHost() { return ((ReactApplication) getPlainActivity().getApplication()).getReactNativeHost(); }
另外实现了
MainApplication
以后须要在AndroidManifest.xml
中添加MainApplication
:
<application android:name=".MainApplication" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> ...
以上就是经过继承ReactActivity
的方式来做为RN容器的。
那么这两种方式各有什么特色:
ReactInstanceManager
的方式:灵活,可定制性强;ReactActivity
的方式:简单,可定制性差;此过程更细致的讲解可查阅:React Native与Android 混合开发讲解的视频教程
通过上述的步骤,咱们已经完成了对一个现有Android项目RNHybridAndroid添加了RN,而且经过两种方式分别建立了一个RNPageActivity
与ReactPageActivity
的Activity来加载咱们在JS中注册的名为App1
的RN 组件。
接下来咱们来启动RN服务器,运行RNHybridAndroid项目打开RNPageActivity
或ReactPageActivity
来查看效果:
npm start
在RNHybrid
的根目录运行上述命令,来启动一个RN本地服务:
而后咱们打开AndroidStudio,点击运行按钮或者经过快捷键Ctrl+R
来将RNHybridAndroid
安装到模拟器上:
咱们能够根据须要添加更多的React Native的组件:
import { AppRegistry } from 'react-native'; import App from './App'; import App2 from './App2'; AppRegistry.registerComponent('App1', () => App); AppRegistry.registerComponent('App2', () => App);
而后,在Native中根据须要加载指定名字的RN组件便可。
调试这种混合的RN应用和调试一个纯RN应用时同样的,都是经过上文中说讲到的RN 开发者菜单
,另外搭建也能够经过学习最新版React Native+Redux打造高质量上线App课程来掌握更多RN调试的技巧。
虽让,经过上述步骤,咱们将RN和咱们的RNHybridAndroid项目作了融合,但打包RNHybridAndroid你会发现里面并不包含JS部分的代码,若是要将JS代码打包进Android Apk包中,能够经过以下命令:
react-native bundle --platform android --dev false --entry-file index.js --bundle-output RNHybridAndroid/app/src/main/assets/index.android.bundle --assets-dest RNHybridAndroid/app/src/main/res/
参数说明
--platform android
:表明打包导出的平台为Android;--dev false
:表明关闭JS的开发者模式;-entry-file index.js
:表明js的入口文件为index.js
;--bundle-output
:后面跟的是打包后将JS bundle包导出到的位置;--assets-dest
:后面跟的是打包后的一些资源文件导出到的位置;提示:JS bundle必定要正确放到你的Android言语的assets目录下这个和咱们上文中配置的
setBundleAssetName("index.android.bundle")
进行对应。
经过上述步骤咱们完成了将RN代码打包并生成JS bundle,并放到了assets目录下,接下来咱们就能够来经过Android Studio或者命令的方式来release咱们的RN混合Android应用了。
我在以前发表过React Native发布APP之签名打包APK的博文, 须要的同窗能够去看一下,在这篇文章中就不在重复了。
更多React Native混合开发的实用技巧,可学习与此文章配套的视频课程:《React Native与Android 混合开发讲解》