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 集成到已有项目的主要内容,如果未能解决你的问题,请参考以下文章
用于 React Native 的 Intune SDK 集成
React Native集成到现有项目(非cocoa pods)
与本机应用程序集成时,React-native 应用程序在每次导航时都会重新启动