美文网首页React Native安卓开发学习iOS开发
Android/iOS已有原生项目集成ReactNative

Android/iOS已有原生项目集成ReactNative

作者: kevinlandering | 来源:发表于2017-06-18 20:32 被阅读1064次
    1. 背景
      近期由于项目需要,在已经开发了几个版本的原生App中集成了ReactNative,新版的模块都使用RN开发。
      此次集成的工作大部分是参照RN中文文档进行的,但是中文文档也有一些坑和描述不充分的地方,所以我参照着文档把自己集成的过程以及遇到的问题整理了下,希望能给遇到相同问题的人起到参考作用。
      ReactNative中文文档官方地址:http://reactnative.cn/docs/0.45/integration-with-existing-apps.html#content
    2. 集成过程
      2.1 Android
      2.1.1 在应用中添加js代码
      打开控制台,进入项目根目录,执行以下几个命令:
    $npm init
    $npm install --save react react-native
    $curl -o .flowconfig https://raw.githubusercontent.com/facebook/react-native/master/.flowconfig
    

    npm init命令是创建package.json文件,之后会要求输入信息,除了name之外其他的直接按enter默认就行了。
    npm install则创建了node_modules目录并把react和react-native下载到了其中。执行此命令时,要是控制台输出了版本不一致的警告信息,例如:

    npm WARN react-native@0.45.1 requires a peer of react@16.0.0-alpha.12 but none was installed.
    

    则继续执行 npm i -S react@16.0.0-alpha.12 (这里版本跟警告信息一致)。
    至于第三步curl命令,其实质是下载.flowconfig配置文件,这个文件用于约束js代码的写法。非必需,可跳过。
    执行完成之后我的package.json是这样的:

    {
    "name": "test",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
    },
    "author": "",
    "license": "ISC",
    "dependencies": {
    "react": "^16.0.0-alpha.12",
    "react-native": "^0.45.1"
    }
    }
    

    接下来还需要把启动脚本放进去:
    "start": "node node_modules/react-native/local-cli/cli.js start"
    注意下启动脚本的路径,如果集成的项目目录结构不一样的话会报找不到启动脚本之类的错误,到时根据具体目录结构修改就好了。

    2.1.2 在项目根目录下创建index.android.js文件:

    'use strict';
    import React from 'react';
    import {
      AppRegistry,
      StyleSheet,
      Text,
      View
    } from 'react-native';
    class test extends React.Component {
      render() {
        return (
          <View style={styles.container}>
            <Text style={styles.hello}>Hello, My Hybrid App!</Text>
          </View>
        )
      }
    }
    var styles = StyleSheet.create({
      container: {
        flex: 1,
        justifyContent: 'center',
      },
      hello: {
        fontSize: 20,
        textAlign: 'center',
        margin: 10,
      },
    });
    AppRegistry.registerComponent('test', () => test);
    

    2.1.3 打开你的android项目,在app中 build.gradle 文件中添加 React Native 依赖:

    dependencies {
         ...
         compile "com.facebook.react:react-native:+" // From node_modules.
     }
    

    接着,在 AndroidManifest.xml 清单文件中声明网络权限:

    <uses-permission android:name="android.permission.INTERNET" />
    

    如果需要访问 DevSettingsActivity 界面,也需要在 AndroidManifest.xml 中声明:

    <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
    

    在项目的 build.gradle 文件中为 React Native 添加一个 maven 依赖的入口,必须写在 "allprojects" 代码块中:

    allprojects {
        repositories {
            ...
            maven {
                // All of React Native (JS, Android binaries) is installed from npm
                url "../node_modules/react-native/android"
            }
        }
        ...
    }
    

    这里maven路径官网上写的是$rootDir/../node_modules/react-native/android,这个跟目录结构有关,跟我一样在根目录上操作的话是不需要$rootDir的

    2.1.4 同步gradle
    遇到以下错误:

    Warning:Conflict with dependency 'com.google.code.findbugs:jsr305'. Resolved versions for app (3.0.0) and test app (2.0.1) differ. See http://g.co/androidstudio/app-test-app-conflict for details.
    

    在build.bundle里面加入:

    allprojects {
        ...
        configurations.all {
            resolutionStrategy.force 'com.google.code.findbugs:jsr305:3.0.0'
        }
    }
    

    2.1.5 添加原生代码
    2.1.5.1 新建一个Activity,代码如下:

    public class MyReactActivity 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")
                    .setJSMainModuleName("index.android")
                    .addPackage(new MainReactPackage())
                    .setUseDeveloperSupport(BuildConfig.DEBUG)
                    .setInitialLifecycleState(LifecycleState.RESUMED)
                    .build();
    
            // 注意这里的HelloWorld必须对应“index.android.js”中的
            // “AppRegistry.registerComponent()”的第一个参数
            mReactRootView.startReactApplication(mReactInstanceManager, "test", null);
    
            setContentView(mReactRootView);
        }
    
        @Override
        public void invokeDefaultOnBackPressed() {
            super.onBackPressed();
        }
    
        @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();
            }
        }
        @Override
        public void onBackPressed() {
            if (mReactInstanceManager != null) {
                mReactInstanceManager.onBackPressed();
            } else {
                super.onBackPressed();
            }
        }
        @Override
        public boolean onKeyUp(int keyCode, KeyEvent event) {
            if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) {
                mReactInstanceManager.showDevOptionsDialog();
                return true;
            }
            return super.onKeyUp(keyCode, event);
        }
    }
    

    2.1.5.2 在AndroidManifest.xml中声明:

    <activity
                android:name=".MyReactActivity"
                android:label="@string/app_name"
                android:theme="@style/Theme.AppCompat.Light.NoActionBar">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
    

    应用在真机上调试时需开启悬浮窗权限才能正确显示红屏错误,官网上有前往开启的代码,但是我觉得有点麻烦了,直接进应用管理中心开启就好了,真正发布的时候也不需要显示红屏错误。

    2.1.6 运行程序
    2.1.6.1 在根目录下使用npm start命令开启后台服务,然后运行应用,不出意外的话应该可以看到一片红色。
    这是后台启动的窗口:

    111.png
    App首次启动页面: S70618-170654.jpg

    红屏没有出现的话注意检查下悬浮窗权限是否开启。
    摇晃下手机,会出现以下界面:

    S70618-170659.jpg

    点击Dev Setting进入设置页面,再点击 Debug server host &...设置服务器的IP跟端口,这个时候要注意下手机跟电脑要在同一个局域网下:

    ![S70618-171637.jpg](http:https://img.haomeiwen.com/i6495829/8cc83a0eb2f6fd0c.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

    设置之后返回到页面,再次摇晃下手机,就出现成功的页面啦!

    S70618-171646.jpg S70618-172035.jpg

    2.2 iOS
    2.2.1 新建package.json文件
    可以新建也可以直接前面android项目的package.json拷贝到根目录下。
    注意这里iOS使用的最新版本的react native在安装pod之后会有点问题,可能是官网上的中文文档没有及时更新的原因,我这里暂时用的还是之前我集成的版本:

    {
      "name": "test",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "start": "node ../ExampleIOS/node_modules/react-native/local-cli/cli.js start"
      },
      "author": "",
      "license": "ISC",
      "dependencies": {
        "react": "^16.0.0-alpha.6",
        "react-native": "^0.43.4"
      }
    }
    

    官网在一开始建议把iOS代码都放到一个 ios/文件夹下,但这对于一个已经开发多次的项目来说操作并不简单,所以这一步也可以不做,只需把package.json里面启动脚本的路径修改一下即可。
    修改之后的路径为:

    "start": "node ../ExampleIOS/node_modules/react-native/local-cli/cli.js start"
    

    2.2.2 安装依赖包
    在根目录下执行命令:

    npm install
    

    安装过程中可能会遇到跟2.1.1一样的警告信息,这时操作也一样。
    2.2.3 安装pod
    2.2.3.1 如果你的项目还没安装pod则先执行pod init,然后在创建了的Podfile文件下加入:

    # 'node_modules'目录一般位于根目录中
        # 但是如果你的结构不同,那你就要根据实际路径修改下面的`:path`
        pod 'React', :path => '../ExampleIOS/node_modules/react-native', :subspecs => [
          'Core',
          'DevSupport', # 如果RN版本 >= 0.43,则需要加入此行才能开启开发者菜单
          'RCTText',
          'RCTNetwork',
          'RCTWebSocket', # 这个模块是用于调试功能的
          # 在这里继续添加你所需要的模块
        ]
        # 如果你的RN版本 >= 0.42.0,请加入下面这行
        pod "Yoga", :path => "../ExampleIOS/node_modules/react-native/ReactCommon/yoga"
    

    然后执行pod install 命令开始安装。看到以下图片就说明安装成功了。

    333.png
    2.2.3.2 如果你已经安装了pod,则直接在Podfile中加入上面的内容,然后使用pod update --verbose --no-repo-update命令更新pod就可以了。
    在这里有个需要注意的地方,安装pod之后就要关闭项目,使用.xcworkspace文件打开项目了。

    2.2.4 代码集成
    2.2.4.1 创建index.ios.js文件
    在项目根目录下创建index.ios.js文件,内容跟之前的index.android.js一样即可。

    'use strict';
    
    import React from 'react';
    import {
      AppRegistry,
      StyleSheet,
      Text,
      View
    } from 'react-native';
    
    class test extends React.Component {
      render() {
        return (
          <View style={styles.container}>
            <Text style={styles.hello}>Hello, My Hybrid App!</Text>
          </View>
        )
      }
    }
    var styles = StyleSheet.create({
      container: {
        flex: 1,
        justifyContent: 'center',
      },
      hello: {
        fontSize: 20,
        textAlign: 'center',
        margin: 10,
      },
    });
    
    AppRegistry.registerComponent('test', () => test);
    

    2.2.4.2 在需要挑战的ViewContrller中引入RCTRootView.h,跳转代码:

    NSURL *jsCodeLocation = [NSURL
                                 URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios"];
        RCTRootView *rootView =
        [[RCTRootView alloc] initWithBundleURL : jsCodeLocation
                             moduleName        : @"test"
                             initialProperties :@{}
                              launchOptions    : nil];
        UIViewController *vc = [[UIViewController alloc] init];
        vc.view = rootView;
        [self presentViewController:vc animated:YES completion:nil];
    

    2.2.5 使用npm start开启后台,启动程序:

    3335.png
    3336.png

    由于苹果的安全访问限制可能会出现访问不到的原因,这时候需要往info.plist加入下面这段代码:

    <key>NSExceptionDomains</key>
        <dict>
            <key>localhost</key>
            <dict>
                <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
                <true/>
            </dict>
        </dict>
    

    然后重新运行程序就可以看到以下结果啦!!

    3337.png 33338.png
    1. 大功告成!接下来我把项目上传到github,有需要的可以下下来参考下:
      Android:
      https://git.oschina.net/GStick/androidiosfeatreactnative.git
      iOS:
      https://git.oschina.net/GStick/androidiosfeatreactnative.git
    2. 之后会继续更新集成之后开发中用到的东西,像加载离线bundle文件、交互之类的,有时间的话还是Android/iOS双管齐下,敬请期待。

    相关文章

      网友评论

        本文标题:Android/iOS已有原生项目集成ReactNative

        本文链接:https://www.haomeiwen.com/subject/jmmiqxtx.html