第一篇blog介绍了配置开发RN的环境,在这个官方文档上我们可以了解到RN开发需要哪些工具,这些熟悉之后我们就可以进入实际项目开发了,不管我们现在懂不懂RN到底是个什么东西,有哪些它自己的语言或者开发要用到的API,我们统统不管,先进入项目开发,在开发中学习,在不断的掉坑中进步(^o^)/
前言
我们不学习怎么用RN开发应用程序,我们学习怎么在现有的android开发中加入RN进行混合开发。
其实这种方式,Facebook
已经在其react-native
项目的文档中进行了介绍,你可以直接通过—>这里<—的链接去访问这个文档;
如果这个链接不好找或者容易忘记的话,你也可以通过github
上的—>react-native
<—项目Getting Started
栏目的Adding React Native to an Existing Application
去查看这个文档
中文相关的文档可以查看这里集成到现有原生应用
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、在真机上运行、调试。
9、打包。
引入相关的依赖
1、在app中bulid.gradle 添加依赖
新建一个项目(原生开发的方式),在app下的build.gradle中添加React Native 依赖。
compile "com.facebook.react:react-native:+"
然后sync一下, 出现一个错误,依赖冲突错误
2、解决依赖冲突
这里就要讲到gradle依赖中的冲突处理问题了,当我们有两个依赖关系依赖到了同一个库的不同版本会出现什么情况呢?
->如果两个依赖都在同一个配置中,都在app 或者 test app中,那么gradle能够自动解决冲突,这个库的这两个依赖中的最高版本会在构建中;
->但是,如果两个依赖在不同的配置中,就如上com.google.code.findbugs:jsr305
在app中是3.0.0,但是在test app 中是2.0.1,gradle就会抛出一个错误,如上。
Android官方的文档的解释是
当运行测试时,主APK和测试APK共享相同的classpath,如果主APK和测试APK使用相同的库,但是在不同的版本时,Gradle将构建失败,如果gradle没有捕捉到,你的app在测试和正常云期间会表现不同的行为。
解决的方法也有两种:
1、你清楚是哪些库产生的冲突,这时你就可以决定排除哪个库上的依赖,如下
//--热更新,防止重复依赖utdid
compile ('com.aliyun.ams:alicloud-android-hotfix:3.1.0') {
exclude(module:'alicloud-android-utdid')
}
上面的代码就表示,项目在引用com.aliyun.ams:alicloud-android-hotfix:3.1.0
库的时候,将排除module:'alicloud-android-utdid'
。
2、第二种解决方法就是—强制库的解析
除了上面的每一个库都去修改外,我们还可以在gradle中加上全局配置的文件,强制所有配置使用同一个库
比如上面的引用com.google.code.findbugs:jsr305
的冲突,我们就可以在gradle中加上如下代码,强制app和test app 都使用3.0.0的版本
configurations.all {
resolutionStrategy.force 'com.google.code.findbugs:jsr305:3.0.0'
}
现在我们再sync一下,发现没有错误了。
3、在项目中bulid.gradle 添加依赖
allprojects {
repositories {
...
maven {
// All of React Native (JS, Android binaries) is installed from npm
url "$rootDir/node_modules/react-native/android"
}
}
...
}
清单文件修改
在AndroidManifest.xml
中加上如下权限
<uses-permission android:name="android.permission.INTERNET" />
在AndroidManifest.xml
中加上DevSettingsActivity
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
添加index.android.js和package.json文件
1、在根目录下创建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('MyReactNativeApp', () => HelloWorld);
2、在根目录下创建package.json文件
可以自己项目右键创建一个file(package.json
),然后自己写入如下内容:
{
"name": "rn1030",
"version": "1.0.0",
"description": "",
"main": "index.android.js",
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start"
},
"author": "zuo",
"license": "ISC"
}
或者通过AS的命令行使用npm init
命令创建
根据提示输入内容,就会生成 如下内容的`package.json`文件
{
"name": "rn1030",
"version": "1.0.0",
"description": "",
"main": "index.android.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "zuo",
"license": "ISC"
}
然后我们把scripts下的内容换成"start": "node node_modules/react-native/local-cli/cli.js start"
变成
{
"name": "rn1030",
"version": "1.0.0",
"description": "",
"main": "index.android.js",
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start"
},
"author": "zuo",
"license": "ISC"
}
这些都是react-native文档中提到的,但是我们这里是根据自己的步骤来进行相关配置,并没有完全依据官方文档的步骤
修改MainActivity
需要注意android6.0之后的权限问题
官方文档说明:
如果您的应用需要在Android API23或更高版本运行,请确保您已为应用启用了覆盖权限, 您可以使用Settings.canDrawOverlays(this)来检查是否开启了覆盖权限。这在应用构建中是必需的,因为必须在所有其他窗口之上显示本机开发错误。 由于在API级别23中引入了新的权限系统,用户需要批准它。 这可以通过在onCreate()方法中的Activity文件中添加以下代码来实现。
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);
}
}
...
@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...
}
}
}
}
修改之后的MainActivity如下
public class MainActivity extends BaseActivity implements DefaultHardwareBackBtnHandler {
private ReactRootView mReactRootView;
private ReactInstanceManager mReactInstanceManager;
private final int ResultCODE = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main);
initView();
}
private void initView() {
/* 下面的版本判断代码,如果不添加,在6.0以上的Android版本中会报错 */
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, ResultCODE);
}
}
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)
.build();
//下面代码中的"MyReactNativeApp"来自index.android.js文件中最后一行代码
mReactRootView.startReactApplication(mReactInstanceManager,
"MyReactNativeApp", null);
setContentView(mReactRootView);
}
@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();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == ResultCODE) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.canDrawOverlays(this)) {
// SYSTEM_ALERT_WINDOW permission not granted...
Toast.makeText(this, "ACTION_MANAGE_OVERLAY_PERMISSION 权限获取失败!!", Toast.LENGTH_LONG).show();
}
}
}
}
@Override
public void invokeDefaultOnBackPressed() {
super.onBackPressed();
}
@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);
}
}
添加node_modules
完成上面的步骤后,我们使用npm(node包管理器,Node package manager)来安装React和React Native模块,
打开一个终端/命令提示行,进入到项目目录中(即包含有package.json文件的目录),然后运行下列命令来安装,或者直接在Android studio 的Terminal中执行命令
npm install
这些模块会被安装到项目根目录下的node_modules/目录中(所有通过npm install命令安装的模块都会放在这个目录中。这个目录我们原则上不复制、不移动、不修改、不上传,随用随装)。
错误修改
使用npm start
命令去运行这个包,再点击Android Studio界面上的运行按钮去运行项目,结果报错了
查看错误信息,应该是“\\”这样的字符识别不了,查看了下github上的解决方案,先在项目–>app–>main下新建一个assert文件夹,然后在根目录的命令行执行如下命令
react-native bundle --platform android --dev false --entry-file index.android.js --bundle-output app/src/main/assets/index.android.bundle --sourcemap-output app/src/main/assets/index.android.map --assets-dest app/src/main/res/
可以查看assert目录下生产了三个文件
接着我们使用build下的clear project,清空一下项目,然后使用npm start
命令去运行这个包,再点击Android Studio界面上的运行按钮去运行项目,结果可以运行也可以安装apk文件到手机上但是应用闪退,查看原因
这个错误的原因是React Native提供的libgnustl_shared.so文件是32位,而我们的项目中用了一些不兼容的64位so文件,二者混在一起产生的。
解决的办法就是禁止使用那些64位的so文件
第一,在项目根目录下的gradle.properties文件最后加上这样一句:
android.useDeprecatedNdk=true
第二、在app module下的build.gradle文件中添加如下内容:
android {
...
defaultConfig {
...
ndk{
abiFilters "armeabi-v7a", "x86"
}
...
}
...
}
见证奇迹的时候到了!!!(^o^)/~
使用npm start
命令去运行这个包,再点击Android Studio界面上的运行按钮去运行项目
运行出来的Hello World!
大功告成,写完收工!