一、前言
搭建RN 的环境过程中可能会遇到比较多的坑,要有一个心理准备。我昨天搭建环境的时候,遇到很多的问题,一个问题一个问题的google,到昨天晚上遇到的最后一个问题,始终解决不了,今天又折腾了一天,才弄好。
二、什么是 React Native?
官方解释:
React Native使你能够在Javascript和React的基础上获得完全一致的开发体验,构建世界一流的原生APP。
React Native着力于提高多平台开发的开发效率 —— 仅需学习一次,编写任何平台。(Learn once, write anywhere)
Facebook已经在多项产品中使用了React Native,并且将持续地投入建设React Native。
大白话:
就是,老板招聘一个会RN 的人,就可以抵得上分别招Android、IOS、前端各一个人,然后就没有然后呢。
作为一个技术人才,技术的知识广度和深度始终是不懈的追求。技术总是超前发展的,你不学习,你不进步,就会慢慢被社会淘汰。
三、开始搭建
1、新建一个 Android 项目
项目名称:RnTest
2、引入React-Native
在你的app目录下的build.gradle加入react-native依赖,我加入的是最新版本的:
compile "com.facebook.react:react-native:+"
然后在AndroidManifest.xml加入访问网络权限,当然一般已经项目都有这个权限,如果有这一步可以忽略:
<uses-permission android:name="android.permission.INTERNET" />
为了让项目支持调试RN,需要在AndroidManifest.xml里面加入RN的DevSettingsActivity,如下:
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
这样真机晃动手机或者点击Menu菜单就会打开相关的调试页面,如图下所示:
这里部署完后,还有一个坑,就是React-Native对编译版本和最小编译版本都有要求,它需要app的build.gradle文件的compileSdkVersion为23,minSdkVersion为16,因为我们项目要求最低版本为15甚至更低,这里需要在app的AndroidManifest.xml加入:
<uses-sdk tools:overrideLibrary="com.facebook.react" />
3、实现方式1:引用 ReactRootView
新建一个RnActivity.java:
import com.facebook.react.common.LifecycleState;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactRootView;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.react.shell.MainReactPackage;
public class RnActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler{
private ReactRootView mReactRootView;
private ReactInstanceManager mReactInstanceManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_rn);
mReactRootView = (ReactRootView)findViewById(R.id.react_rootview);
findViewById(R.id.back_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
RnActivity.this.finish();
}
});
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, "RnTest", null);
}
@Override
public void invokeDefaultOnBackPressed() {
super.onBackPressed();
}
@Override
protected void onPause() {
super.onPause();
if(mReactInstanceManager != null){
//mReactInstanceManager.onPause();
mReactInstanceManager.onHostPause(this);
}
}
@Override
protected void onResume() {
super.onResume();
if(mReactInstanceManager != null){
//mReactInstanceManager.onResume(this, this);
mReactInstanceManager.onHostResume(this, this);
}
}
@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);
}
}
因为ReactRootView本身就是一个FrameLayout,我没有按官网来直接new一个,而是直接把它放到布局activity_my_react.xml里面了,布局文件代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context="com.troy.rntest.RnActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:text="我是本地控件TextView"/>
<Button
android:id="@+id/back_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="返回"
android:textColor="@android:color/white"
android:background="@color/colorPrimaryDark"/>
<com.facebook.react.ReactRootView
android:id="@+id/react_rootview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="10dp">
</com.facebook.react.ReactRootView>
</LinearLayout>
可以看到ReactRootView 实际是FrameLayout 的子类:
public class ReactRootView extends SizeMonitoringFrameLayout implements RootView {}
public class SizeMonitoringFrameLayout extends FrameLayout {}
4、JS代码如下
在项目目录下新建一个 index.android.js 文件:
import React from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
NativeModules,
ToastAndroid
} from 'react-native';
class RnTest extends React.Component {
render() {
return (
<View style={styles.container}>
<Text> 测试ReactNative </Text>
</View>
)
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
hello: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
AppRegistry.registerComponent('RnTest', () => RnTest);
注意细节,部分版本在调用方法时需要加React.,比如
注册时:
React.AppRegistry.registerComponent('RnTest', () => RnTest);
引用样式时:
React.StyleSheet.create
5、添加 package.json
代码如下:
{
"name": "RnTest",
"version": "1.0.0",
"description": "demo",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node node_modules/react-native/local-cli/cli.js start"
},
"author": "troy",
"license": "ISC",
"dependencies": {
"react": "^15.4.2",
"react-native": "^0.42.3"
}
}
6、实现方式2:App 实现 ReactApplication接口
项目Application 实现ReactApplication 接口:
public class MyApp extends Application implements ReactApplication{
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
}
@Override
public void onTerminate() {
super.onTerminate();
}
@Override
public ReactNativeHost getReactNativeHost() {
return new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return true;//返回true
}
@Override
protected List<ReactPackage> getPackages() {
return Arrays.asList((ReactPackage)new MainReactPackage());//返回MainReactPackage;
}
};
}
}
新建一个Activity:
public class SecondActivity extends ReactActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//注释原来的布局文件
// setContentView(R.layout.activity_second);
}
@Override
protected String getMainComponentName() {
//直接加载 RN 组件RnTest:
return "RnTest";
}
}
四、项目配置运行调试
4.1、在RnTest 项目目录下执行如下命令
$ npm init
$ npm install --save react-native
$ curl -o .flowconfig https://raw.githubusercontent.com/facebook/react-native/master/.flowconfig
npm init命令可以不用执行,它如要用来生成package.json文件,前面我们已经创建过了,npm install –save react-native执行后会在目录下面生成node_modules文件夹并添加react-native的npm依赖。
首先启动RN的npm本地服务:
$ npm start
启动后如下:
在浏览器中打开:
http://localhost:8081/index.android.bundle?platform=android
出现前端代码:
(function(global) {
global.__DEV__ = true;
global.__BUNDLE_START_TIME__ = Date.now();
})(typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : this);
(function(global) {
'use strict';
global.require = _require;
global.__d = define;
表示启动成功。
4.2、真机测试
为了真机测试,需要设置adb 反转,执行命令如下:
/d/android-sdk-windows/platform-tools/adb.exe reverse tcp:8081 tcp:8081
结果,在真机上运行ok。Hello World 的截图就不放了。
4.3、设置maven 本地依赖
在项目的gradle 文件中加入:
allprojects {
repositories {
maven {
// All of React Native (JS, Android binaries) is installed from npm
url "$rootDir/node_modules/react-native/android"
}
jcenter()
}
}
五、编译/运行遇到的各种问题及解决办法
5.1、appcompat-v7:23 版本问题
问题描述:
Caused by: java.lang.IllegalAccessError: tried to access method android.support.v4.net.ConnectivityManagerCompat.:(Lcom/facebook/react/bridge/ReactApplicationContext;)V from class com.facebook.react.modules.netinfo.NetInfoModule
我的项目的appcompat的版本是23.2.1,改成23.0.1就好了。
compile 'com.android.support:appcompat-v7:23.0.1'
5.2、添加NDK 支持
问题描述:
Caused by: java.lang.UnsatisfiedLinkError: could find DSO to load: libreactnativejni.so
解决办法:
在build.gradle增加对ndk 的支持:
defaultConfig {
applicationId "react.plus.com.android_react"
minSdkVersion 22
targetSdkVersion 23
versionCode 1
versionName "1.0"
ndk {
abiFilters "armeabi-v7a", "x86"
}
}
同时在项目的 gradle.properties 文件中申明:
android.useDeprecatedNdk=true
5.3、sdk 版本问题
问题描述:
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.wuage.steel-1/base.apk:classes20.dex)
将sdk 版本改为24 之后便ok。
android {
compileSdkVersion 24
buildToolsVersion "24.0.2"
defaultConfig {
applicationId "com.troy.rntest"
minSdkVersion 14
targetSdkVersion 24
versionCode 1
versionName "1.0"
ndk {
abiFilters "armeabi","armeabi-v7a"
六、RN 集成用到的常见命令
- npm init // 编辑一个package.json 文件
- npm install –save react-native // 初始化一个 node_modules/
- curl -o .flowconfig https://raw.githubusercontent.com/facebook/react-native/master/.flowconfig // 拉取配置文件
- npm ls // 查看安装的模块
- npm start // 启动模块
- npm start –reset-cache //重启
- npm stop // 停止模块