关于react-native和android的开发环境搭建、环境变量配置等可以查看官方文档。
官方文档地址
文章中涉及的node、react等版本:
node:v16.18.1
react:^18.1.0
react-native:^0.70.6
gradle:gradle-7.2
开发工具:VSCode和android studio
关于react-native和安卓的混合开发,官方文档已经提供了一种方式,按照文档一步一步操作还是比较容易实现的,但是官方文档方式,对已经存在的原生APP来说,不太友好,需要改动大,代码混在一起,不利于代码管理,项目耦合性高,所以决定采用aar的方式试一下react-native和安卓的混合开发。
创建项目目录文件夹
选择合适的盘符新建reactnativetest文件夹,然后使用vscode打开该文件夹。
安装JavaScript依赖包
在vscode打开的项目根目录下通过运行npm init创建一个名为package.json的文件夹文件。
安装react和react-native依赖
npm install react@^18.1.0
npm install react-native@^0.70.6
通过执行上面两个命令,在reactnativetest下面就会出现node_modules文件夹。
创建安卓原生项目文件夹android
在reactnativetest文件夹中创建android子文件夹,使用android studio在android文件夹下创建原生项目。
原生安卓项目依赖react-native aar和依赖库
到这一步,官方文档采用的是直接本地依赖,这里采用的aar的方式依赖,需要找到react-native的aar包,
C:\androidp\reactnativetest\node_modules\react-native\android\com\facebook\react\react-native\0.70.6
在node_modules文件夹中找到如下文件:
.aar就是react-native对应版本的aar库,.pom就是react-native对应版本的依赖库
C:\androidp\reactnativetest\node_modules\jsc-android\dist\org\webkit\android-jsc\r250230
在node-modules文件夹下找到如下:
在原生android项目中添加一个android module
将找到的react-native的aar包拷贝到mylibrary中libs下
接着将.pom文件中的依赖转换,找到该文件中的dependencies节点
对每一个dependency节点进行转换
implementation 'androidx.appcompat:appcompat-resources:1.4.1'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'androidx.autofill:autofill:1.1.0'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'
implementation 'com.facebook.fbjni:fbjni-java-only:0.2.2'
implementation 'com.facebook.fresco:fresco:2.5.0'
implementation 'com.facebook.fresco:imagepipeline-okhttp3:2.5.0'
implementation 'com.facebook.fresco:ui-common:2.5.0'
implementation 'com.facebook.infer.annotation:infer-annotation:0.18.0'
implementation 'com.facebook.soloader:soloader:0.10.4'
implementation 'com.facebook.yoga:proguard-annotations:1.19.0'
implementation 'com.google.code.findbugs:jsr305:3.0.2'
implementation 'com.squareup.okhttp3:okhttp-urlconnection:4.9.2'
implementation 'com.squareup.okhttp3:okhttp:4.9.2'
implementation 'com.squareup.okio:okio:2.9.0'
implementation 'javax.inject:javax.inject:1'
将这些依赖和aar一起添加到mylibrary中的build.gradle文件中
在reactnativetest文件夹下创建index.js入口文件
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}>我是react-native页面的</Text>
</View>
);
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center'
},
hello: {
fontSize: 20,
textAlign: 'center',
margin: 10
}
});
AppRegistry.registerComponent(
'MyReactNativeApp',
() => HelloWorld
);
这里的index.js只是测试文件,具体根据实际需求编写。
将react-native中js代码打包成.bundle文件
npx react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/com/your-company-name/app-package-name/src/main/assets/index.android.bundle --assets-dest android/com/your-company-name/app-package-name/src/main/res/
android/com/your-company-name/app-package-name/src/main/assets/index.android.bundle
android/com/your-company-name/app-package-name/src/main/res/
上面两个输出目录和文件名称可以替换成实际需要的。
在android app module中新增assets文件夹,然后执行上面的命令。
接着将生成.bundle文件拷贝到mylibrary module中。
在原生项目中添加程序react-native页面的逻辑
public class MyReactNativeActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler {
private ReactRootView mReactRootView;
private ReactInstanceManager mReactInstanceManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SoLoader.init(this, false);
mReactRootView = new ReactRootView(this);
List<ReactPackage> packages = new PackageList(getApplication()).getPackages();
// 有一些第三方可能不能自动链接,对于这些包我们可以用下面的方式手动添加进来:
// packages.add(new SvgPackage());
// 同时需要手动把他们添加到`settings.gradle`和 `app/build.gradle`配置文件中。
mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(getApplication())
.setCurrentActivity(this)
.setBundleAssetName("index.android.bundle")
.setJSMainModulePath("index")
.addPackages(packages)
// .setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
// 注意这里的MyReactNativeApp 必须对应"index.js"中的
// "AppRegistry.registerComponent()"的第一个参数
mReactRootView.startReactApplication(mReactInstanceManager, "MyReactNativeApp", null);
setContentView(mReactRootView);
}
@Override
protected void onPause() {
super.onPause();
// 把一些 activity 的生命周期回调传递给ReactInstanceManager
if (mReactInstanceManager != null) {
mReactInstanceManager.onHostPause(this);
}
}
@Override
protected void onResume() {
super.onResume();
// 把一些 activity 的生命周期回调传递给ReactInstanceManager
if (mReactInstanceManager != null) {
mReactInstanceManager.onHostResume(this, this);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
// 把一些 activity 的生命周期回调传递给ReactInstanceManager
if (mReactInstanceManager != null) {
mReactInstanceManager.onHostDestroy(this);
}
if (mReactRootView != null) {
mReactRootView.unmountReactApplication();
}
}
@Override
public void onBackPressed() {
// 后退按钮事件传递给 React Native
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);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
// 最后,必须重写 onActivityResult() 方法(如下面的代码所示)以处理权限 Accepted 或 Denied 情况以实现一致的 UX。
// 此外,为了集成使用 startActivityForResult 的 Native Modules,我们需要将结果传递给我们的 ReactInstanceManager
mReactInstanceManager.onActivityResult(this, requestCode, resultCode, data);
super.onActivityResult(requestCode, resultCode, data);
}
@Override
public void invokeDefaultOnBackPressed() {
super.onBackPressed();
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main);
startActivity(new Intent(this, MyReactNativeActivity.class));
finish();
}
}
这样就将一个简单的react-native加载到原生里面来展示了,所有的react-native aar包和依赖库都是放在mylibrary中的,可以将mylibrary打包成aar,拷贝到需要的项目中使用。这个是比较简单的demo集成,没有涉及到react-native第三库的使用,特别是react-native和安卓的混合第三方库。
react-native和安卓的混合第三方库使用
纯js的react-native不需要怎么处理,会被打包到.bundle文件中的,混合库会有些不太一样,这里简单使用一下react-native-svg这个混合库。
npm install react-native-svg//安装依赖库
会发现react-native-svg混合部分就是一个android module,如果是采用官方文档的依赖,启动原生模块的自动链接,会自动在android 原生中生产对应的svg module的。
启用原生模块的自动链接
要使用自动链接的功能,我们必须将其应用于几个地方。首先,将以下内容添加到settings.gradle:
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
接下来,在app/build.gradle的最底部添加以下内容:
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
不过很遗憾的是,采用aar的方式,这些依赖都没有,谈何的启动原生模块的自动链接呢。
既然react-native-svg库的混合部分是一个android module,那将它打包成aar进行依赖是可行的,所以将react-native-svg库的混合部分打包aar。
import进来后,构建发现报错,屏蔽掉repositories,具体报错具体分析。需要将dependencies中的react-native替换成aar方式依赖。这套干脆新增一个module,就专门用于依赖react-native的aar包和库,然后其他module依赖该module。
其他module的依赖变成如下:
开始对react-native-svg打包aar
打包好后,将react-native-svg的aar拷贝到mylibrary中进行依赖。
react-native-svg简易demo的js文件
import React from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
import Svg, { Circle } from 'react-native-svg';
class HelloWorld extends React.Component {
render() {
return (
<View style={styles.container}>
<Svg height="50%" width="50%" viewBox="0 0 100 100" >
<Circle cx="50" cy="50" r="50" stroke="purple" strokeWidth=".5" fill="violet" />
</Svg>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
AppRegistry.registerComponent(
'MyReactNativeApp',
() => HelloWorld
);
重新打包生产.bundle文件。运行app,会看到如下错误:
在加载react-native的代码出添加SvgPackage实例对象
重新运行就看到效果了:
将mylibrary module打包成aar文件输出
可以将react-native和原生混合开发的逻辑都封装在mylibrary,然后根据实际需要将mylibrary打包成aar输出,可以解耦,同时大大提升开发效率等。
执行后看到报错了:
这个错误网上有不少对应的解决方案:
aar打包错误解决方案
大致就是将aar包放到独立的moudle,然后依赖module,再打包输出。那就将react-native-svg的aar包放到arrlib中,然后mylibrary依赖arrlib。
这样aar打包输出成功了。
demo链接:https://pan.baidu.com/s/1nlBDqF__LdpCcbtMUMKYGA
提取码:nps1