Kahn's blogs

现有原生Android应用集成RN(React Native Androi...

2017/09/12

现有原生Android应用集成RN(React Native Android)开发环境

官网教程
中文教程

环境搭建

因为本人使用的是macbook,所以只记录mac上的搭建过程,windows自行观看教程。本文只讲把RN集成到现有的原生开发环境,如果新建的RN android项目,应该很简单吧?

基础软件包

Homebrew,Nodejs,Watchman这些工具包都是必备的。装完上面的软件(应该本来就有吧?),再有就是RN相关的了,react-native-cli,RN的命令行工具

基础开发运行环境

  • JDK 1.8
  • AS 2.0
  • Android SDK 6.0 API23

集成到原生项目中

  • 先说下基本注意事项:命令执行的目录不能乱,要注意工程目录app
  1. 工程目录下,执行npm init,把该项目初始化为RN项目,命令行中会出现提示,让输入工程的相关信息,第一个为name,不能大写,这个name为RN界面的入口。后面的都先不设置,一路回车,最后问你yes or no,输入yes!工程就初始化好了,工程目录会多一个package.json文件,这个就是RN的配置文件,刚刚输入的信息都在里面。大概长这个样子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    {
    "name": "leanrn1",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start",
    "test": "echo \"Error: no test specified\" && exit 1"
    },
    "author": "",
    "license": "ISC",
    "dependencies": {
    "react": "^16.0.0-alpha.12",
    "react-native": "^0.48.2"
    }
    }

    生成后,scripts节点中没有start节点,要补上,测试用的。

  2. 下载RN库。在工程目录下,执行npm install --save react react-native,新本的npm支持使用–save保持安装包是最新的。执行上述命令就形成了一个坑,下文有特意讲坑的地方,使用官方命令npm install --save react@16.0.0-alpha.12 react-native可脱坑。不管是执行哪个命令吧,执行完后工程目录下会多出node_modules文件夹。

  3. 接着输入curl -o .flowconfig https://raw.githubusercontent.com/facebook/react-native/master/.flowconfig,下载这个配置文件我也不知道有啥用。flow是facebook的一种格式,这个配置文件是解析flow格式的json用的?- -!
  4. 工程目录下新建一个index.android.js,内容可以copy官方教程中的,代码为

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    'use strict';
    import React from 'react';
    import {
    AppRegistry,
    StyleSheet,
    Text,
    View
    } from 'react-native';
    class leanrn1 extends React.Component {
    render() {
    return (
    <View style={styles.container}>
    <Text style={styles.hello}>Hello, World</Text>
    </View>
    )
    }
    }
    var styles = StyleSheet.create({
    container: {
    flex: 1,
    justifyContent: 'center',
    },
    hello: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
    },
    });
    AppRegistry.registerComponent('leanrn1', () => leanrn1);

    其实这个就是RN的js文件了注意AppRegistry.registerComponent('leanrn1', () => leanrn1);中的leanrn1。

  5. 配置android工程对RN库的依赖。在app中的build.gradle文件中配置

    1
    2
    3
    4
    5
    6
    dependencies节点中添加
    compile "com.facebook.react:react-native:+"
    android节点中添加(如果sync后有冲突的话)
    configurations.all {
    resolutionStrategy.force 'com.google.code.findbugs:jsr305:1.3.9'
    }

    工程目录的build.gradle中的repositories添加

    1
    2
    3
    4
    maven {
    // All of React Native (JS, Android binaries) is installed from npm
    url "$rootDir/node_modules/react-native/android"
    }

    url的目录位置就是工程目录中的node_modules位置,如果不对,请按自己情况修过,如果不会改,在mave中print一下url,慢慢改!然后再添加网络相关权限。据说6.0以后还要添加系统对话框权限,蒙层权限七七八八。

  6. 添加测试代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    package com.leanrn1.kahn.leanrn1;
    import android.app.Activity;
    import android.os.Bundle;
    import android.support.v7.app.AppCompatActivity;
    import android.view.KeyEvent;
    import com.facebook.react.ReactInstanceManager;
    import com.facebook.react.ReactRootView;
    import com.facebook.react.common.LifecycleState;
    import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
    import com.facebook.react.shell.MainReactPackage;
    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();
    mReactRootView.startReactApplication(mReactInstanceManager, "leanrn1", null);
    setContentView(mReactRootView);
    }
    @Override
    public void invokeDefaultOnBackPressed() {
    super.onBackPressed();
    }
    @Override
    protected void onPause() {
    super.onPause();
    if (mReactInstanceManager != null) {
    mReactInstanceManager.onHostPause();
    }
    }
    @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);
    }
    }
    AndroidManifest.xml中添加
    <activity
    android:name=".MyReactActivity"
    android:label="@string/app_name"
    android:theme="@style/Theme.AppCompat.Light.NoActionBar">
    </activity>

    以上为测试代码

  7. 工程目录下运行npm start命令。该会开启一个本地的http服务器,对外提供的服务就是访问本机的8081端口就可以下载index.android.js文件。RN库本质就是加载js代码,转换成.bundle文件,解析.bundle文件生成原生库的控件。在Welcomeactivity中配置一下跳转,跳转到刚刚6的测试activity。

  8. 最后,然后愉快的sync工程,运行项目。如果,没闪退,没报错,没红屏,那么恭喜你,自行去搜索RN的开发教程吧。下面的就不用看了。

红屏系列

  • Can’t find variable: __fbBatchedBridge
    • 对不起,你的本地测试服务没开,请在项目根目录运行npm start
  • Unable to load script from assets’index.android.bundle’……
    • 这个就有点恶心了。要去main下新建assets文件夹,然后用命令从本地测试服务(npm start启动的)新建一个.bundle文件。curl "http://localhost:8081/index.android.bundle?platform=android" -o "android/app/src/main/assets/index.android.bundle",上面命令后面的“”是你的assets路径。再试试,不行就没招了。我试过,直接解决了这个问题,然后把这个文件删掉,再运行,还是可以,一脸懵逼啊,到底是不是这种方式解决的都不知道了。
  • Cannot read property ‘ReactCurrentOwner’ of undefined
    • 一般是因为rn的版本不对。上面说到直接使用npm install --save react react-native下载的版本可能不对。使用该命令install时,如果版本不对,会出现一个警告,react版本不对,会告诉你一个对的版本。也可以去package.json文件中查看react节点的版本号,发现确实不是。使用警告中提示的版本再单独安装pm install -save react@16.0.0-alpha.12,安装完后,再去看package.json,这回应该对了吧。

配置系列

  • 如果用真机调试,可能要重定向一下手机访问的端口号。
    • 使用命令adb reverse tcp:8081 tcp:8081
  • 点击menu菜单,使用调试功能时闪退
    • <activity android:name="com.facebook.react.devsupport.DevSettingsActivity"/>配置之~
  • 别忘了.gitignore(在该文件里添加排除项,node_modules/ 和 npm-debug.log)
  • 如果想5.0以下运行,使用RN的activity继承AppCompatActivity。
  • 闪退后打印爆出android.support.v4.net相关错误
    • gradle.properties (在文件末尾添加,android.useDeprecatedNdk=true)
    • app/build.gradle (将 ‘com.android.support:appcompat-v7:24.2.1’ 改为 ‘com.android.support:appcompat-v7:23.0.1’)

升级降级及版本核对

  1. 查看当前的RN版本,react-native --version,npm包中的版本信息使用npm info react-native
  2. 使用react-native upgrade命令可以直接升级。
  3. 升降级使用npm install --save react-native@xxx,xxx为具体版本,运行完后,再运行下2试试?
  4. 在package.json中修改版本,再执行npm install也可达到升降级的效果。个人感觉还是直接指定版本比较靠谱。

##用到的命令汇总

1
2
3
4
5
6
7
8
9
10
11
12
13
14
brew install watchman
npm init
npm install --save react
npm install --save react-native
curl -o .flowconfig https://raw.githubusercontent.com/facebook/react-native/master/.flowconfig
npm start
adb reverse tcp:8081 tcp:8081
npm install --save react react-native
npm install -save react@16.0.0-alpha.12
curl "http://localhost:8081/index.android.bundle?platform=android" -o "~/Dev/my/asproject/LeanRn1/app/src/main/assets/index.android.bundle"
react-native --version
npm info react-native