美文网首页React NativeReact Native 大神之路前端
将React Native整合进Android项目超详细图文教程

将React Native整合进Android项目超详细图文教程

作者: 工程师milter | 来源:发表于2016-09-24 11:24 被阅读7121次

    不得不吐个槽,按照官方文档,已经不可能顺利实现React Native与已有项目的整合,因为世界变化太快,Android Studio发展到了2.2,React Native 已经是0.33版。为了帮助大家顺利攻克这个知识点,我写了这个教程。

    一、基础信息

    在整合的过程中,一些坑是与所用手机型号、Android Studio版本等特定信息紧密相关的,所以有必要明确我所用的配置:

    • Android Stuidio 2.2稳定版
    • 64位win7操作系统
    • 红米note3双网通普配版
    • React Native 0.33版

    二、具体步骤

    1、创建Android项目

    这一步按照AS新建项目向导一步步完成即可,完成后,需要做如下准备工作:

    • 在app module下的build.gradle文件的dependencies中添加React Native 依赖:compile "com.facebook.react:react-native:+"
    • 在Manifest文件中添加权限和Activity:
    <uses-permission android:name="android.permission.INTERNET" />
    <application ...>
    <activity    android:name="com.facebook.react.devsupport.DevSettingsActivity" />
    </application>
    

    我们新建的项目的一些关键配置如下:

    • compile SDK 和target SDK都是24
    • 所用的支持包的版本是:
      compile 'com.android.support:appcompat-v7:24.2.1'

    Note:网上有文章讲,使用的appcompat-v7支持包版本必须是23.0.1,compile SDK和target SDK也必须是23 。经我测试,使用最新版本24也可以。如果你的机器上不行,出现了这样的错误:

    Caused by: java.lang.IllegalAccessError: Method 'void 
    android.support.v4.net.ConnectivityManagerCompat.<init>()' 
    is inaccessible to class 
    'com.facebook.react.modules.netinfo.NetInfoModule' 
    (declaration of 'com.facebook.react.modules.netinfo.NetInfoModule' 
    appears in /data/app/com.milter.www.awesomeproject2-2/base.apk)
    

    可以尝试将appcompat-v7、compile SDK、target SDK调整为23,这是保险的做法。

    2、将Android项目变成一个React Native项目

    整合后的项目,实际上是三种项目的混合体,首先它是一个普通的Android项目,其次它是一个React Native项目,最后它还是一个Node.js项目。这就是为什么整合比较困难。

    下面,我们需要将上步中创建的Android项目变成一个React Native项目。

    • 创建并修改package.json文件
      进入Android项目的根目录,按住shift键并右键单击鼠标,从弹出的菜单中选择:**在此处打开命令窗口(W) ** 选项,在当前目录下打开一个命令窗口,如下所示:
    command_line_window.png

    图中的ReactNativeWithNativeApp就是第一步中创建的Android项目的名字。

    在上面的命令行窗口中输入:
    npm init
    这个命令会引导你在ReactNativeWithNativeApp目录下创建一个package.json文件。如图所示:

    packagejson.png

    package.json文件的内容如下:

    {
      "name": "reactnativewithnativeapp",
      "version": "1.0.0",
      "description": "integrate RN with existing app",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "author": "milter",
      "license": "ISC",
      "dependencies": {
        "react-native": "^0.33.1"
      }
    }
    
    

    修改上面的 package.json文件,将其中的

     "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      }
    

    修改为:

    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
     ,"start": "node node_modules/react-native/local-cli/cli.js start" 
    }
    

    这样修改后,我们在项目根目录的命令行窗口中输入命令:
    npm start
    就相当于执行如下命令:
    node node_modules/react-native/local-cli/cli.js start

    Note:创建package.json文件,意味着原先的Android项目同时也变成了一个Node.js项目,下面,我们给这个Node.js项目引入react native 模块,进一步将它变成一个react native 项目

    • 引入React Native 模块
      还是在原先的命令行窗口中,输入如下命令:
      npm install --save react react-native

    执行这个命令后,在项目根目录(ReactNativeWithNativeApp)下会创建一个node_modules目录里面内容如下:

    node-modules.png

    里面主要有react-native模块以及它所依赖的模块,还是挺多的。

    • 创建.flowconfig文件
      同一命令行窗口下,执行如下命令:
    curl -o .flowconfig https://raw.githubusercontent.com/facebook/react-native/master/.flowconfig
    

    这一命令的作用是将命令中url指向的.flowconfig文件下载到项目的根目录。在上面的图packagejson中可以看到这个下载后的文件。

    如果你不知道怎么才能使用curl命令,可参考这篇文章:
    windows(64位)下使用curl命令

    Tips:如果你不想使用curl命令,这里给一个简单的方法:在根目录中建一个.flowconfig文件,用记事本打开它。
    在浏览器中打开网址:
    https://raw.githubusercontent.com/facebook/react-native/master/.flowconfig
    将网页内容拷贝进打开的.flowconfig文件中,保存并关闭它。
    Tips:在window系统下创建.flowconfig文件的方法是:

    • 在项目根目录下打开cmd命令行窗口
    • 输入 copy con .flowconfig
    • 按下回车
    • 按下Ctrl+Z
    • 再按下回车
    • OK!

    至此,我们的Android项目已经变成了一个Android项目和React Native项目的合体项目。下面,我们将创建React Native项目程序并整合到Android项目中。

    3、创建RN程序

    在根目录下创建index.android.js文件,内容如下:

    'use strict';
    
    import React from 'react';
    import {
      AppRegistry,
      StyleSheet,
      Text,
      View
    } from 'react-native';
    
    class HelloWorld 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('HelloWorld', () => HelloWorld);
    

    这个文件是一个完全的React Native程序,现在我们需要把它整合到Android项目中去。
    整合思路:在Android项目中创建一个Activity,然后利用React Native提供的工具,将上面的index.android.js程序包装进该Activity的contentView中。

    4、将RN程序整合进Android项目

    • 基础配置
      在项目根目录的build.gradle中(注意:不是app模块中的build.gradle文件)添加依赖,如下所示:
    allprojects {
        repositories {
            jcenter()
           maven {
                // All of React Native (JS, Android binaries) is installed from npm
                url "$projectDir/../node_modules/react-native/android"
            }
        }
    

    maven是我们添加的内容。

    • 整合
      主要是修改MainActivity内容,如下所示:
    /* 这里省略了包名和import语句*/
    public class MainActivity extends AppCompatActivity
            implements DefaultHardwareBackBtnHandler {
    
        private ReactRootView mReactRootView;
        private ReactInstanceManager mReactInstanceManager;
        private LifecycleState mLifecycleState
                = LifecycleState.BEFORE_RESUME;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
          /* 下面的版本判断代码官方文档中没有,
            如果不添加,在6.0以上的Android版本中会报错 */
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                if (!Settings.canDrawOverlays(this)) {
                    Intent serviceIntent = new Intent(
                            Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
                    startActivity(serviceIntent);
                }
            }
            mReactRootView = new ReactRootView(this);
            mReactInstanceManager = ReactInstanceManager.builder()
                    .setApplication(getApplication())
                    .setBundleAssetName("index.android.bundle")
                    .setJSMainModuleName("index.android")
                    .addPackage(new MainReactPackage())
                    .setUseDeveloperSupport(BuildConfig.DEBUG)
                    .setInitialLifecycleState(mLifecycleState)
                    .build();
    //下面代码中的"HelloWorld"来自index.android.js文件中最后一行代码
            mReactRootView.startReactApplication(mReactInstanceManager,
                    "HelloWorld", null);
    
            setContentView(mReactRootView);
        }
    
        @Override
        protected void onPause() {
            super.onPause();
    
            mLifecycleState = LifecycleState.BEFORE_RESUME;
    
            if (mReactInstanceManager != null) {
                mReactInstanceManager.onHostPause();
            }
        }
    
        @Override
        protected void onResume() {
            super.onResume();
    
            mLifecycleState = LifecycleState.RESUMED;
    
            if (mReactInstanceManager != null) {
                mReactInstanceManager.onHostResume(this, this);
            }
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
    
            mReactRootView.unmountReactApplication();
            mReactRootView = null;
    
            if (mReactInstanceManager != null) {
                mReactInstanceManager.destroy();
            }
        }
    
        @Override
        public void onActivityResult(int requestCode, int resultCode,
                                     Intent data) {
            if (mReactInstanceManager != null) {
                mReactInstanceManager.onActivityResult(this,requestCode,
                        resultCode, data);
            }
        }
    
        @Override
        public void onBackPressed() {
            if (mReactInstanceManager != null) {
                mReactInstanceManager.onBackPressed();
            }
            else {
                super.onBackPressed();
            }
        }
    
        @Override
        public void invokeDefaultOnBackPressed() {
            super.onBackPressed();
        }
    }
    

    三、项目试运行

    现在可以尝试运行下整合的项目。
    首先,在项目根目录下的命令行窗口运行如下命令:
    npm start
    前面讲过,这个命令就相当于执行如下命令:
    node node_modules/react-native/local-cli/cli.js start

    然后,就可以点击Android Studio界面上的运行按钮啦!

    不要高兴的太早,你很可能会遇到这个错误:

    java.lang.UnsatisfiedLinkError: could find DSO to load: libreactnativejni.so
    

    这个错误的原因是React Native提供的libreactnativejni.so文件是32位,而我们的项目中用了一些不兼容的64位so文件,二者混在一起产生的。

    解决的办法就是禁止使用那些64位的so文件。为此,我们需要先做点准备工作。

    第一,在项目根目录下的gradle.properties文件最后加上这样一句:
    android.useDeprecatedNdk=true
    第二、在app module下的build.gradle文件中添加如下内容:

    android {
        ...
        defaultConfig {
            ...
            ndk{
                abiFilters "armeabi-v7a", "x86"
            }
            ...
        }
    ...
    }
    

    第三、找出不兼容的64位so文件并禁止它们
    在目录...\ReactNativeWithNativeApp\app\build\outputs\apk下找到app-debug.apk,并把它解压,查看一下,解压后的文件的lib目录下有没有这个目录:
    arm64-v8a

    一般情况下是没有的,此时我们就真正大功告成了!!!

    如果有这个目录,看看里面的so文件,都是我们要禁止的,禁止的方法如下:
    假设里面有一个 1.so文件,我们要在app module下的build.gradle文件中做如下修改:

    android {
        ...
        defaultConfig {
            ...
            ndk{
                abiFilters "armeabi-v7a", "x86"
            }
            packagingOptions {
                exclude "lib/arm64-v8a/1.so"            
            }
            ...
        }
    ...
    }
    

    如果arm64-v8a目录下还有2.so、3.so等文件,处理方法与1.so一样。

    好了,撒花庆祝!!:)

    该项目已分享到GitHub,地址是:
    https://github.com/like4hub/ReactNativeWithNativeApp

    相关文章

      网友评论

      • 西门吹雪少帅:可以留个联系方式么,遇到问题纠结一天了。求助呀。
      • 西门吹雪少帅:效果是什么呀,怎么我这边运行后,说是在其他应用上层显示。
      • 7ff35fdfb50f:android stulio打包的APK里的react native内容第一次得在服务器下运行才能显示出来吗?
        7ff35fdfb50f:@milter index.android.js里放的只是一个日历组件,并不需要网络。
        还是说RN的内容并没有在apk里,得去服务器里获取过来?
        我对rn并不是很熟,有说错的请见谅
        工程师milter: @南京丶 rn组件就在手机上的,日历组件最后会转换成手机上的native components
        工程师milter: @南京丶 你要是不从服务器上拉取数据就应该不用吧
      • 7ff35fdfb50f:看了你的教程帮我解决了两个大问题,谢谢了
        工程师milter:@南京丶 荣幸之至哈!
      • MoTalksCn_林墨:milter叔,我刚想做什么,你就出什么教程,你总走在我前面。:joy::joy::joy:
      • 慢慢学:按住shift键并右键单击鼠标,从弹出的菜单中选择:在此处打开命令窗口(W) 选项

        还可以这样,学习了
        慢慢学:@慢慢学 我找到办法了,用双引号包裹起来就好了
        慢慢学:@milter .flowconfig这种没有文件名的文件怎么在windows上成功创建呢
        工程师milter: @慢慢学 很方便的用法😏😀
      • EitanLiu:果然没有迭代1.0以上的都不靠谱
        工程师milter: @小耳朵图图是我 是的,bug还很多
      • 乐楽樂:多谢分享
        工程师milter:@乐楽樂 :smile:
      • 8739bac722f8:还没试,先赞一个
        工程师milter:@逆袭_ 我也给你赞一个 :+1:
      • dfe147f4a102:有余力就去试试
        工程师milter:@_v君 试就大胆试

      本文标题:将React Native整合进Android项目超详细图文教程

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