React Native 集成到已有项目

原文链接:http://blog.csdn.net/xueshanhaizi/article/details/52873626
前言
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.

   创建工程很容易就完成了,现在我们加一个跳转按钮不过当前还不能跳转。最终展示界面如下: ![这里写图片描述](http://upload-images.jianshu.io/upload_images/4655344-a1992400c298f151?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
   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) rndeme1version: (1.0.0) 1.0.0description: demeentry point: (index.js) index.android.jstest command: testgit 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 startScanning 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 /hotReact 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 #1Process: im.yixin.rndemo2, PID: 18294java.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>
<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 #1Process: im.yixin.rndemo2, PID: 25424java.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
1

1

注意: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集成进已有的项目,中途会碰到这样那样的坑,这里遇到的坑,已经很全了,希望大家工具探讨,学习。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容