项目连接:HelloRN
ReactNative使用手册
把React Native组件植入到Android应用中官方步骤:
首先,我有一句MMP,不知当讲不当讲,这是玩RN以来遇到的巨坑,当你看到这篇文章的时候,从开始完成这个项目到现在已经过去2天半了,我已经按照官方文档集成在5次以上,已经浏览文章30篇以上,包括在# facebook/react-native的issues
看了很多相关的问题,最终走出来了
1. 首先当然要了解你要植入的React Native组件。
2. 在Android项目根目录中使用npm来安装`react-native` ,这样同时会创建一个`node_modules/`的目录。
3. 创建js文件,编写React Native组件的js代码。
4. 在`build.gradle`文件中添加`com.facebook.react:react-native:+`,以及一个指向`node_nodules/`目录中的`react-native`预编译库的`maven`路径。
5. 创建一个React Native专属的`Activity`,在其中再创建`ReactRootView`。
6. 启动React Native的Packager服务,运行应用。
7. 根据需要添加更多React Native的组件。
8. 在真机上[运行](https://reactnative.cn/docs/0.40/running-on-device-android.html)、[调试](https://reactnative.cn/docs/0.40/debugging.html)。
9. [打包](https://reactnative.cn/docs/0.40/signed-apk-android.html)。
10. 发布应用,升职加薪,走向人生巅峰!
算然官网给出了方法步骤,但是文档过于简单,而且还有很多巨坑,所以本文还是有一定价值的,当然在上面的步骤中有一些我们没必要关注,但是主要步骤还是有的,接下来就按照这个步骤去完成把RN嵌入到android原生项目中
把React Native组件植入到Android应用
第一步:引入react-native
在androidstudio的Terminal窗口中输入 npm init
,接着会提示你输入一些东西(除了项目名字其他都可直接回车使用默认值),如下图
当我们输完的时候将工程切换到project模式下,可以看到工程多了一个package.json的文件
{
"name": "hellorn",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "赖天兵",
"license": "ISC"
}
看到这个文件有一种很熟悉但又陌生的感觉,但是它的作用都应该猜到了,这个和我们build.gradle中的配置是一样的作用,其实就是配置工程的一些属性
第二步:添加react和react_native模块
在Terminal窗口中输入:npm install --save react react-native
并执行,然后就是静静的等待,如果有报错,自己手动敲"--"这个符号,因为在不同的系统下“--”可能是不一样的,完成后就可以看到工程多了一个node_modules模块
第三步:在命令行中运行curl -o .flowconfig https://raw.githubusercontent.com/facebook/react-native/master/.flowconfig
(这步对Windows来说真的坑)
直接运行会提示你:'curl' 不是内部或外部命令,也不是可运行的程序
所以看来我的首先处理Windows运行curl命令的问题了
curl是利用URL语法在命令行方式下工作的开源文件传输工具。它被广泛应用在Unix、多种Linux发行版中,并且有DOS和Win32、Win64下的移植版本。
所以首先要解决Windows下支持curl
命令的问题Windows下安装使用curl命令
提示有可能你找不到| curl-7.33.0-win64-ssl-sspi.zip
再提示一下当你按照上面Windows下安装使用curl命令走到了下图步骤时
这时候应该在命令窗口中输入curl -v -X OPTIONS https://www.baidu.com/
,这是文档没有说的,而是直接输入到命令行里了,第一次看如果不知道这个,那会很懵逼的
不过遗憾的是我按照上门的连接文档一步步走下来还是无法在任何地方使用curl命令(我的电脑win10 64位)
所以我用了最简单的方法,直接复制我们下载的curl.exe
到工程根目录,这样在本工程的根目录就能运行curl命令了
但是:真想爆粗口,还是生成不了.flowconfig
文件,
所以真正最直接的解决方式来了:自己在项目的根目录下创建.flowconfig
文本文件,然后打开.flowconfig连接复制文本到刚刚创建的文件中即可
走到这里突然想感叹一句,我饶了一大圈到底是为了啥
第三步:在package.json文件中的scripts
里面配置启动脚本"start": "node node_modules/react-native/local-cli/cli.js start",
{
"name": "hellorn",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node node_modules/react-native/local-cli/cli.js start",
"bundle-android": "react-native bundle --platform android --entry-file index.android.js --bundle-output app/src/main/assets/index.android.bundle --dev false"
},
"author": "赖天兵",
"license": "ISC",
"dependencies": {
"react": "^16.2.0",
"react-native": "^0.53.3"
}
}
其实在官方文档中第三步已经完了,但是毕竟我是踏过了很多巨坑的,看了很多篇博客的我告诉你这步还没完,照常理这里还应该配置打包用index.android.js生成index.andriod.bundle的配置(你也可以尝试不做,绝对会出现一些经典的bug,这里就不提了,我会有一篇专门的文章记录这些bug)
配置生成发布、打包时所需要bundle文件的配置
在很多博客中会这样解决这个问题,像上面完整package.json中的scripts
代码一样添加
, "bundle-android": "react-native bundle --platform android --entry-file index.android.js --bundle-output app/src/main/assets/index.android.bundle --dev false"
这一句在官方文档中没有说明,但是最好加上(虽然我加上后,至少我这里没有卵用,据说是新版本的RN不支持自动生成打包所需的bundle文件了),但是这里需要修改一下,很多博客都直接是
"bundle-android": "react-native bundle –platform android –dev false –entry-file index.android.js –bundle-output android/app/src/main/assets/index.android.bundle –sourcemap-output android/app/src/main/assets/index.android.map –assets-dest android/app/src/main/res/"
但是这是错的,打包的时候我们都要根据自己项目目录的结果做一些调整,不过在上面package.json中我已经做过调整了,但是还是无法自动完成打包bundle
解决:我们先在main下面创建assets文件夹,然后进入工程根目录打开cmd输入
react-native bundle --platform android --entry-file index.android.js --bundle-output app/src/main/assets/index.android.bundle --dev false
(注意-output后面的参数根据自己的项目目录结构来写)
成功后的项目
你或许看过很多在安卓原始项目中嵌套RN,并按照他们步骤一步步完成了,但是最后就是看不到效果,这里就是其中一个重要的原因
如果这步完成了,那么恭喜你,你基本上能看到效果了,后面可能还要出bug,但是都是些容易解决的固定的了
第四步:在项目根目录中创建index.android.js
文件
在index.android.js中编写我们的代码
import React from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
class HelloWorldApp extends React.Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.hello}>Hello world! I am from ReactNattive!!</Text>
</View>
)
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
hello: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
//这里的第一个参熟名字要和我们创建的这个工程项目名一样
AppRegistry.registerComponent('HelloRN', () => HelloWorldApp);
提示:在本系列RN博客的第二篇创建HelloWorld的时候说过,registerComponent()注册时名字必须和项目名字保持一致
第五步:添加ReactNative依赖
首先在APP的build.gradle
中添加
dependencies { ... compile "com.facebook.react:react-native:+" // From node_modules. }
其次在工程的build.gradle
中添加进入本地ReactNative仓库的路径,但是官方源文档写的路径是
$rootDir/../node_modules/react-native/android
如果你直接复制使用,恭喜你你又入坑了
因为我们通过之前第二步添加的添加react和react_native模块,默认是项目的根目录下,而官方文档给出的路径其实是多了“../”所以这就是一个坑(说到这里提一句,网上很多文章都是直接用的官方路径,但是项目结构又和我的一样,我真怀疑他们写那个文章的时候只是copy别人文章,自己根本没有成功,这也是很多人按照别人文章一步步到最后还是报错的又一个原因),这里路径应该对应自己工程中module的路径
我的工程目录
所以我的路径应该是
allprojects {
repositories {
...
maven {
// All of React Native (JS, Android binaries) is installed from npm
url "$rootDir/node_modules/react-native/android"
}
}
...
}
添加完成后记得同步以下哦,不过等待的时间过于漫长(最好开VPN,我是开了VPN才同步完成的),可以先往后继续看文章
第六步:在清单文件中添加网络请求权限,必须添加的
官方原话Next, make sure you have the Internet permission in your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
以及调试需要用到的权限
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW"/>
第七步:在清单文件中注册DevSettingsActivity
,此步骤可以省略,功能就是重载JavaScript
如果您需要访问DevSettingsActivity添加到您的AndroidManifest.xml:
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
这只在开发服务器重新加载JavaScript时才真正用于开发模式,因此,如果需要,可以在发布版本中将其剥离。
第八步:把之前项目自动创建的MainActivity中代码改成如下代码,但是注意当APP需要支持5.0以下的机型那么需要使用com.android.support:appcompat
包中的AppCompatActivity
类,而不能直接使用Activity
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactRootView;
import com.facebook.react.common.LifecycleState;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.react.shell.MainReactPackage;
public class MainActivity extends AppCompatActivity 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在新 版本中找不到,替换为setJSMainModulePath
// .setJSMainModuleName("index.android")
//这里的路径是相对于根目录的,填入index.android即可
.setJSMainModulePath("index.android")
.addPackage(new MainReactPackage())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
//注意这里的`moduleName`参数必须和工程名字,也是就是在`index.android.js`中AppRegistry.registerComponent()注册的名字一样
mReactRootView.startReactApplication(mReactInstanceManager, "HelloRN", null);
setContentView(mReactRootView);
}
@Override
public void invokeDefaultOnBackPressed() {
super.onBackPressed();
}
}
接下来(这个步骤是可以跳过的),我们需要将一些活动生命周期回调传递给ReactInstanceManager:
@Override
protected void onPause() {
super.onPause();
if (mReactInstanceManager != null) {
mReactInstanceManager.onPause();
}
}
@Override
protected void onResume() {
super.onResume();
if (mReactInstanceManager != null) {
mReactInstanceManager.onResume(this, this);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mReactInstanceManager != null) {
mReactInstanceManager.onDestroy();
}
}
注意:如果你直接copy官网中代码的话,是会出错的,因为ReactNative版本更新了,方法名字更改和安卓更加同步了
(也是可跳过的)我们还需要将按钮事件传递给React Native:
@Override
public void onBackPressed() {
if (mReactInstanceManager != null) {
mReactInstanceManager.onBackPressed();
} else {
super.onBackPressed();
}
}
作用:这允许JavaScript控制用户按下硬件后退按钮时发生的情况(例如,实现导航)。当JavaScript不处理背按时,您的invokeDefaultOnBackPressed方法将被调用。默认情况下,这只是完成你的Activity。
最后(这个步骤也是可以跳过的),我们需要连接开发菜单。默认情况下,这是通过(愤怒)激发设备来激活的,但这在模拟器中并不是很有用。所以我们在按下硬件菜单按钮时显示它(Ctrl + M如果您使用的是Android Studio模拟器,请使用它):
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) {
mReactInstanceManager.showDevOptionsDialog();
return true;
}
return super.onKeyUp(keyCode, event);
}
第九步(此步骤可以跳过):配置权限以便开发中的红屏错误能正确显示
如果您的应用定位到Android API level 23
或更高版本,请确保您已overlay
为开发版本启用权限。你可以检查它Settings.canDrawOverlays(this);
。这在开发版本中是必需的,因为必须在所有其他窗口之上显示原始开发错误。由于在API级别23中引入了新的权限系统,用户需要批准它。这可以通过将以下代码添加到onCreate()方法中的Activity文件中来实现。OVERLAY_PERMISSION_REQ_CODE是将负责将结果传递回活动的类的字段。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.canDrawOverlays(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE);
}
}
最后,onActivityResult()
必须重写该方法(如下面的代码所示)以处理一致UX的权限Accepted或Denied。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == OVERLAY_PERMISSION_REQ_CODE) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.canDrawOverlays(this)) {
// SYSTEM_ALERT_WINDOW permission not granted...
}
}
}
}
最后在明确下index.android.js和mReactRootView.startReactApplication(mReactInstanceManager, "HelloRN", null)
和package.json及项目名字,到底那几个保持一致(我也是集成了5次以上,总结出来的,如果有不对请留言指出)
必须一样的:工程名字(HelloRN)和AppRegistry.registerComponent('HelloRN', () => HelloWorldApp);
以及mReactRootView.startReactApplication(mReactInstanceManager, "HelloRN", null);
三者必须一样。
当你出现以下错误,基本就是这个原因了
而package.json中的"name": "hellorn",
算然给我的感觉应该也和其他一样才对,但是确实可以不一样,而且当你在第一步的时候输入项目名字,如果输了大写,还提示你必须小写。而且我们在初始化的时候"main": "index.js",
这一步默认是index.js
,从字段来看我们后面创建的index.android.js名字也应该叫“index.js”才对,毕竟package.json类似于我们的配置文件,但是我们没这样做,发现也没有任何问题。
运行应用
- 首先运行服务
在项目的根文件夹下,命令行运行如下命令,启动测试服务器(可以直接在Androidstudio的Termin中输入,但是注意当前所在位置)。
npm start
或者:
react-native start
- 接下来像开发安卓项目一样直接运行项目
效果
当出现这个界面时别提多嗨皮了,哭去了
如果报错
或是
解决:在app的build.gradle 中添加
|
|
ndk {
abiFilters "armeabi-v7a", "x86"
}
}
喜欢请点赞,或是关注,后续将完善发布更多的文章,你的鼓励就是我的动力(程序员最大的动力莫过于同行的鼓励)