React Native 集成到已有项目

Posted freeCodeSunny

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React Native 集成到已有项目相关的知识,希望对你有一定的参考价值。

前言

       React Native已经出现很久了,有很多应用也在进行尝试,前面我们也讲述了怎么创建React Native工程以及怎么搭建原生语言与js的开发环境。

       但是在实际应用中,很多项目都不是从零开始的,而是在已有项目中进行尝试,这就需要将React Native集成到已有项目,这里我们就来讲讲怎么集成到已有项目。

需求

       这里我们会用android Studio创建一个工程,改工程包含有一个主页面,里面里有一个跳转按钮,能够跳转到用React Native实现的页面。

创建Android工程

       这里由于React Native是从Android 4.1开始支持的,因此我们直接创建最小版本号为16的工程。

Supported operating systems are >= Android 4.1 (API 16) and >= ios 7.0.

       创建工程很容易就完成了,现在我们加一个跳转按钮不过当前还不能跳转。最终展示界面如下:

       Activity中处理代码如下:

public class MainActivity extends AppCompatActivity 

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setViewListener();
    

    private void setViewListener() 
        findViewById(R.id.to_react).setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View v) 
                Toast.makeText(MainActivity.this, "go  to  React", Toast.LENGTH_LONG).show();
            
        );
    

       目前这里弹出Toast 提示,之后改为跳转到React Native界面。

集成

       这里我们安装官方指定的步骤来进行集成,官方集成的链接为:查看链接,在集成之前希望你已经配置好了React Native 环境,比如Node.js, watchman等。下面我们来分步骤集成。

初始化

       首先在命令行进入到你所创建的工程下,第一步输入如下命令:

npm init

       这一步主要在工程下创建package.json文件,输入命令后界面输出如下内容:

       这里需要输入name,这里name就是JS中AppRegistry.registerComponent(‘RNDemo’, () => xxxx)中的RNDemo,也就是项目名。不过这里目前只能输入小写。之后会输入version等信息,可以跳过某些内容直接回车。需要输入的内容展现如下:

Press ^C at any time to quit.
name: (RNDemo1) rndeme1
version: (1.0.0) 1.0.0
description: deme
entry point: (index.js) index.android.js
test command: test
git repository: 
keywords: 
author: 
license: (ISC) 
About to write to /Users/doc/ReactNative/RNDemo1/package.json:


  "name": "rndeme1",
  "version": "1.0.0",
  "description": "deme",
  "main": "index.android.js",
  "scripts": 
    "test": "test"
  ,
  "author": "",
  "license": "ISC"


Is this ok? (yes) 

       这里输入yes就好了。最终这些内容都会生成在package.json中。我们打开package.json看看最终生成的内容:


  "name": "rndeme1",
  "version": "1.0.0",
  "description": "deme",
  "main": "index.android.js",
  "scripts": 
    "test": "test"
  ,
  "author": "",
  "license": "ISC"

       这里可以看到与我们上面输入的内容一致,只不过name这个单词拼写错了。。。

创建node module

       在上面的命令执行完成后,输入如下命令:

npm install –save react react-native // save 前面是两个横线

       等待一段时间,我们可以在项目的根目录发现生成了一个node module的文件夹,这一步也可以省略,直接从已有项目中拷贝过来。

创建.flowconfig

       flowconfig是给flow用的,flow的作用前面已经讲过了,主要用来做静态代码检查。我们可以输入如下命令在生成.flowconfig文件。

curl -o .flowconfig https://raw.githubusercontent.com/facebook/react-native/master/.flowconfig

       运行完成后,你可以在文件夹下看到创建了一个.flowconfig,不过这个文件是影藏的,可以Mac下你可以ls -al来查看。

修改package.json

       上面的文件创建完成后,我们需要修改package.json 文件,在scripts节点下添加如下的语句:

“start”: “node node_modules/react-native/local-cli/cli.js start”

       最终修改后的package.json如下:


  "name": "rndeme1",
  "version": "1.0.0",
  "description": "deme",
  "main": "index.android.js",
  "scripts": 
    "start": "node node_modules/react-native/local-cli/cli.js start",
    "test": "test"
  ,
  "author": "",
  "license": "ISC",
  "dependencies": 
    "react": "^15.3.2",
    "react-native": "^0.35.0"
  

       可以看到在创建node module时,已经修改了json文件,添加了react的依赖。

创建index.androd.js

       在工程的跟目录下创建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('rndeme1', () => HelloWorld);

       这代码直接从官方拷贝的,只需要主要registerComponent的第一个参数,改为我们上面输入的内容。

修改gradle

       因为我们将react集成到已有项目,而android项目是靠gradle来进行构建编译的,因此这里我们需要对应修改相应的内容。

       1:添加react-native依赖

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

       这里需要注意的是,这里修改是app目录下的build.gradle文件

       2:添加maven

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

       这里修改的是跟目录下的build.gradle,放置在allprojects节点下。

添加权限

       React Native是需要网络权限的,因为他是从远程服务器拉取的jsBundle。因此我们在AndroidManifest.xml下添加网络权限。

添加native code

       前面我们已经创建好js文件了,也配置好了其他的一些内容,这里我们需要添加一个activity来展示React Native,这里我们创建一个MyReactActivity的页面。代码从网络拷贝:

public class MyReactActivity extends Activity 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)
                //.setUseOldBridge(true) // uncomment this line if your app crashes
                .build();
        mReactRootView.startReactApplication(mReactInstanceManager, "HelloWorld", null);

        setContentView(mReactRootView);
    

    @Override
    public void invokeDefaultOnBackPressed() 
        super.onBackPressed();
    

       我们这里就不在处理其他比如onResume等回调了。手动将所有的import导入,最后对该Activity设置theme。设置的主题为:android:theme=”@style/Theme.AppCompat.Light.NoActionBar”,因为有些组件是依赖这个主题的。

运行

       到这一步我们按照官方的内容就算已经集成完成了,是不是可以开始运行了,我们来试着运行一下,首先启动server,输入如下命令:

npm start

 rndeme1@1.0.0 start /Users/doc/ReactNative/RNDemo1
> node node_modules/react-native/local-cli/cli.js start

Scanning 582 folders for symlinks in /Users/doc/ReactNative/RNDemo1/node_modules (15ms)
 ┌────────────────────────────────────────────────────────────────────────────┐ 
 │  Running packager on port 8081.                                            │ 
 │                                                                            │ 
 │  Keep this packager running while developing on any JS projects. Feel      │ 
 │  free to close this tab and run your own packager instance if you          │ 
 │  prefer.                                                                   │ 
 │                                                                            │ 
 │  https://github.com/facebook/react-native                                  │ 
 │                                                                            │ 
 └────────────────────────────────────────────────────────────────────────────┘ 
Looking for JS files in
   /Users/doc/ReactNative/RNDemo1 

[2016-10-20 15:50:07] <START> Building Dependency Graph
[2016-10-20 15:50:08] <START> Crawling File System
[Hot Module Replacement] Server listening on /hot

React packager ready.

[2016-10-20 15:50:08] <END>   Crawling File System (402ms)
[2016-10-20 15:50:08] <START> Building in-memory fs for javascript
[2016-10-20 15:50:08] <END>   Building in-memory fs for JavaScript (175ms)
[2016-10-20 15:50:08] <START> Building in-memory fs for Assets
[2016-10-20 15:50:08] <END>   Building in-memory fs for Assets (120ms)
[2016-10-20 15:50:08] <START> Building Haste Map
[2016-10-20 15:50:08] <START> Building (deprecated) Asset Map
[2016-10-20 15:50:08] <END>   Building (deprecated) Asset Map (67ms)
[2016-10-20 15:50:09] <END>   Building Haste Map (355ms)
[2016-10-20 15:50:09] <END>   Building Dependency Graph (1061ms)

       输出如下内容,表示启动成功了。我们是不是就可以运行了,首先我们用react-native run android来运行,这里需要重新打一个命令窗口。运行命令后直接输出了:

Android project not found. Maybe run react-native android first?

       这里是因为默认创建是有三年级目录的,外层目录,之后Android目录,之后才是代码,那我们采用Andriod Studio来运行。不过我们还需要改一个地方,就是之前我们的点击事件还是弹出一个Toast,我们需要改成打开新的Activity,打开代码如下:

Intent intent = new Intent();
intent.setClass(MainActivity.this, MyReactActivity.class);
startActivity(intent);

       我们再一次运行,成功了? no,成的失败了!~

填坑

       上面我们已经运行了该工程,不过不出所料,成功的失败了,之后还连续出现了一系列的失败,这里就一个一个的解决:

java.lang.UnsatisfiedLinkError

       运行后出现了如下的异常:

FATAL EXCEPTION: AsyncTask #1
Process: im.yixin.rndemo2, PID: 18294
java.lang.RuntimeException: An error occured while executing doInBackground()
  at android.os.AsyncTask$3.done(AsyncTask.java:304)
  at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
  at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
  at java.util.concurrent.FutureTask.run(FutureTask.java:242)
  at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
  at java.lang.Thread.run(Thread.java:818)
Caused by: java.lang.UnsatisfiedLinkError: could find DSO to load: libreactnativejni.so
  at com.facebook.soloader.SoLoader.loadLibraryBySoName(SoLoader.java:213)
  at com.facebook.soloader.SoLoader.loadLibrary(SoLoader.java:178)
  at com.facebook.react.bridge.JSCJavaScriptExecutor.<clinit>(JSCJavaScriptExecutor.java:25)
  at com.facebook.react.bridge.JSCJavaScriptExecutor$Factory.create(JSCJavaScriptExecutor.java:20)
  at com.facebook.react.ReactInstanceManagerImpl$ReactContextInitAsyncTask.doInBackground(ReactInstanceManagerImpl.java:183)
  at com.facebook.react.ReactInstanceManagerImpl$ReactContextInitAsyncTask.doInBackground(ReactInstanceManagerImpl.java:169)
  at android.os.AsyncTask$2.call(AsyncTask.java:292)
  at java.util.concurrent.FutureTask.run(FutureTask.java:237)
  at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231) 
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112) 
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587) 
  at java.lang.Thread.run(Thread.java:818) 

       从日志可以看出是一个so链接错误,这种错误网络是对应的abi不正确。

       解决方案:修改app里的build.gradle,defaultConfig节点下添加ndk节点:

ndk 
    abiFilters "armeabi-v7a", "x86"

       来我们再一次运行:

NDK integration is deprecated in the current plugin.

       运行后发现NDK重复集成了,错误日志如下:

Error:(13, 0) NDK integration is deprecated in the current plugin.
<a href="http://tools.android.com/tech-docs/new-build-system/gradle-experimental">Consider trying the new experimental plugin</a><br><a href="useDeprecatedNdk">Set "android.useDeprecatedNdk=true" in gradle.properties to continue using the current NDK integration</a>

       解决方案:方案1,可以在gradle.properties下添加android.useDeprecatedNdk=true,方案2,降低gradle的版本,我们任意选择一种方式都可以,之后在一次运行:

java.lang.IllegalAccessError,Method ‘void android.support.v4.net.ConnectivityManagerCompat

       我们继续挣扎在没有成功的道路上,错误如下:

FATAL EXCEPTION: AsyncTask #1
Process: im.yixin.rndemo2, PID: 25424
java.lang.RuntimeException: An error occured while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:304)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
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/data/im.yixin.rndemo2/files/instant-run/dex/slice-com.facebook.react-react-native-0.20.1_76f14c344d869afc092625e7670a68a34348b199-classes.dex)
at com.facebook.react.modules.netinfo.NetInfoModule.<init>(NetInfoModule.java:55)
at com.facebook.react.shell.MainReactPackage.createNativeModules(MainReactPackage.java:67)
at com.facebook.react.ReactInstanceManagerImpl.processPackage(ReactInstanceManagerImpl.java:793)
at com.facebook.react.ReactInstanceManagerImpl.createReactContext(ReactInstanceManagerImpl.java:730)
at com.facebook.react.ReactInstanceManagerImpl.access$600(ReactInstanceManagerImpl.java:91)
at com.facebook.react.ReactInstanceManagerImpl$ReactContextInitAsyncTask.doInBackground(ReactInstanceManagerImpl.java:184)
at com.facebook.react.ReactInstanceManagerImpl$ReactContextInitAsyncTask.doInBackground(ReactInstanceManagerImpl.java:169)
at android.os.AsyncTask$2.call(AsyncTask.java:292)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231) 
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112) 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587) 
at java.lang.Thread.run(Thread.java:818)            

       从日志可以看出是ConnectivityManagerCompat的实现获取错误。

       解决方案:这里我们主要修改如下的代码,只代码查找的实现错误,之前我们添加了maven,这里我将:

 "$rootDir/.../node_modules/react-native/android" 改成如下
 "$rootDir/node_modules/react-native/android"

       改了之后,需要重新import某些路径,因为加载的路径变化了。在一起运行:

Application HelloWorld has not been registered

       运行后,没有错误log,不过展示页面出现了错误,页面如下:

       其实这个不算错误,是因为刚才我们写Activity是没有是拷贝的,没有改动,这里需要改动如下:

 mReactRootView.startReactApplication(mReactInstanceManager, "HelloWorld", null);//将HelloWorld改成rndeme1

注意:package.json, index.android.js, activity这三处名称需要一致

       我们修改后再一次运行,到此我们就成功了运行了界面。

       这样我们就成功的运行了。这里我们将Hello World改变一下,改成其他的内容,比如Native Hello World,之后摇动手机点reload,可以发现界面已经变化了。

附录

       这里还有一些其他情况,我们分别来说一下:

弹窗不显示

       有的人摇动手机后,不能弹窗,可能是系统禁止了弹窗,需要手动开启

网络连接失败

       可能是情况是手机与电脑用的网络不是同一个,需要设置为统一个网络

Dev Setting

       点击dev Setting崩溃,这里需要在AndroidManifest中配置如下节点:

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

总结

       到这里,我们已经成功的将React Native集成进已有的项目,中途会碰到这样那样的坑,这里遇到的坑,已经很全了,希望大家工具探讨,学习。

以上是关于React Native 集成到已有项目的主要内容,如果未能解决你的问题,请参考以下文章

原生Android集成React Native

用于 React Native 的 Intune SDK 集成

React Native集成到现有项目(非cocoa pods)

与本机应用程序集成时,React-native 应用程序在每次导航时都会重新启动

如何将 React-Native 项目集成到 Android-Studio?

React-native集成到原生项目