赞
踩
本篇文章主要介绍在现有的Android项目中接入React Native的接入过程,分析接入过程中的一些问题和解决方案,接入RN的平台为Android,开发环境为Mac,开发工具为Android Studio。
因为是现有的Android项目集成RN,所以关于Android方面的SDK,JDK,环境变量配置不做过多的介绍。需要注意的是环境变量的配置:
终端输入:$ open -e ~/.zshrc
export ANDROID_HOME=/Users/username/Library/Android/sdk
export PATH=$PATH:$ANDROID_HOME/tools
export PATH=$PATH:$ANDROID_HOME/tools/bin
export PATH=$PATH:$ANDROID_HOME/platform-tools
export PATH=$PATH:$ANDROID_HOME/emulatorexport JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home
export PATH=$JAVA_HOME/bin:$PATH:.
export CLASSPATH=$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar:.
使环境变量生效:$ source ~/.zshrc
(1)安装 Node 和 Watchman
我们推荐使用Homebrew来安装 Node 和 Watchman。在命令行中执行下列命令安装(如安装较慢可以尝试阿里云的镜像源 https://developer.aliyun.com/mirror/homebrew):
$ brew install node
$ brew install watchman# 使用nrm工具切换淘宝源
$ npx nrm use taobao如果之后需要切换回官方源可使用
$ npx nrm use npm
(2)安装Yarn
Yarn是 Facebook 提供的替代 npm 的工具,可以加速 node 模块的下载。
$ npm install -g yarn
安装完 yarn 之后就可以用 yarn 代替 npm 了,例如用yarn代替npm install命令,用yarn add 某第三方库名代替npm install 某第三方库名。
首先需了解RN项目的目录结构,不能在现在的Android项目中创建RN相关文件和模块,否则会导致react-native link失败,出现各种奇怪的问题,报错如下:
React Native CLI failed to determine Android project configuration.
首先创建一个空目录用于存放 React Native 项目,然后在其中创建一个/android子目录,把你现有的 Android 项目拷贝到/android子目录中。在项目根目录下创建一个名为package.json的空文本文件,然后填入以下内容:
- {
- "name": "MyReactNativeApp",
- "version": "0.0.1",
- "private": true,
- "scripts": {
- "start": "react-native run-android"
- }
- }
name和version对于现有的Android项目没有实际意义,除非把他发布到仓库中。
scripts用于安装项目到手机并且启动服务。
打开一个终端/命令提示行,进入到项目目录中(即包含有 package.json 文件的目录),注意一定要切换到该目录中,然后运行下列命令来安装:
$ yarn add react-native
安装完成之后终端会出现一条警告:
warning " > react-native@0.63.3" has unmet peer dependency "react@16.13.1".
这是正常现象,意味着我们还需要安装指定版本的 React:
$ yarn add react@16.13.1
这样安装就完成,安装完成后再次打开package.json,就会发现该模块已经添加依赖了,如下图:
把新建的RN项目的目录中的Android项目使用打开Android Studio打开,在编辑器上面完成以下操作:
(1)在你的 app 中 build.gradle 文件中添加 React Native 和 JSC 引擎依赖:
- dependencies {
-
- implementation fileTree(dir: "libs", include: ["*.jar"])
- implementation 'androidx.appcompat:appcompat:1.2.0'
- ...
- implementation "com.facebook.react:react-native:+" // From node_modules
- implementation "org.webkit:android-jsc:+"
- }
注意,以上两个都必须添加,缺一不可。
(2)在项目的build.gradle文件中为React Native和JSC 引擎添加maven
- allprojects {
- repositories {
- google()
- jcenter()
- maven {
- // All of React Native (JS, Android binaries) is installed from npm
- url "$rootDir/../node_modules/react-native/android"
- }
- maven {
- // Android JSC is installed from npm
- url("$rootDir/../node_modules/jsc-android/dist")
- }
- }
- }
运行Android Studio的gradle右上角上面的【Sync Now】,如何能够正常编译,则说明路径配置正常,否则路径有误。
(1)manifest节点下:
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
设置网络请求权限和RN屏幕显示日志权限。
(2)application节点下:
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
如果需要访问开发者菜单,则需要声明,开发者菜单一般仅用于在开发时从Packager服务器刷新JavaScript代码,所以在正式发布时可以去掉这一权限。
(3)application节点内:
- android:networkSecurityConfig="@xml/network_security_config"
-
- app/src/res/xml/network_security_config.xml:
-
- <?xml version="1.0" encoding="utf-8"?>
- <network-security-config>
- <!-- allow cleartext traffic for React Native packager ips in debug -->
- <domain-config cleartextTrafficPermitted="true">
- <domain includeSubdomains="false">localhost</domain>
- <domain includeSubdomains="false">10.0.2.2</domain>
- <domain includeSubdomains="false">10.0.3.2</domain>
- </domain-config>
- </network-security-config>
适配Android9.0明文http访问限制。
index.js是 React Native应用在Android上的入口文件。而且它是不可或缺的!它可以是个很简单的文件,简单到可以只包含一行require/import导入语句。在RN项目的根据目录创建index.js(和package.json)同级目录。
- 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>
- );
- }
- }
- const styles = StyleSheet.create({
- container: {
- flex: 1,
- justifyContent: 'center'
- },
- hello: {
- fontSize: 20,
- textAlign: 'center',
- margin: 10
- }
- });
-
- AppRegistry.registerComponent(
- 'MyReactNativeApp',
- () => HelloWorld
- );
配置一个基类的Activity,初始化一些参数,让需要用React Native开发的页面继承基类,参数含义看备注。
-
- public class BaseActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler {
-
- private final int OVERLAY_PERMISSION_REQ_CODE = 1; // 任写一个值
- protected ReactInstanceManager mReactInstanceManager;
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mReactInstanceManager = ReactInstanceManager.builder()
- .setApplication(getApplication())
- .setCurrentActivity(this)
- .setBundleAssetName("index.android.bundle")
- .setJSMainModulePath("index")
- .addPackage(new MainReactPackage())
- //注意BuildConfig应该是在你自己的包中自动生成,无需额外引入。千万不要从com.facebook...的包中引入!
- .setUseDeveloperSupport(BuildConfig.DEBUG)
- .setInitialLifecycleState(LifecycleState.RESUMED)
- .build();
- // 配置权限以便开发中的红屏错误能正确显示,用于程序调试中
- 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) {
- super.onActivityResult(requestCode, resultCode, 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
- }
- }
- }
- mReactInstanceManager.onActivityResult(this, requestCode, resultCode, data);
- }
-
- /**
- * 把生命周期回传给 React Native
- */
- @Override
- protected void onPause() {
- super.onPause();
-
- if (mReactInstanceManager != null) {
- mReactInstanceManager.onHostPause(this);
- }
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- if (mReactInstanceManager != null) {
- mReactInstanceManager.onHostResume(this, this);
- }
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- if (mReactInstanceManager != null) {
- mReactInstanceManager.onHostDestroy(this);
- }
- }
-
- /**
- * 把后退按钮事件传递给 React Native
- */
- @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);
- }
-
- @Override
- public void invokeDefaultOnBackPressed() {
- super.onBackPressed();
- }
- }
- public class MyReactActivity extends BaseActivity {
-
- private ReactRootView mReactRootView;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mReactRootView = new ReactRootView(this);
- // 注意这里的MyReactNativeApp必须对应“index.js”中的
- // “AppRegistry.registerComponent()”的第一个参数
- mReactRootView.startReactApplication(mReactInstanceManager, "MyReactNativeApp", null);
- setContentView(mReactRootView);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- if (mReactRootView != null) {
- mReactRootView.unmountReactApplication();
- }
- }
- }
在AndroidManifest.xml注册MyReactActivity:
- <activity
- android:name=".MyReactActivity"
- android:label="@string/app_name"
- android:theme="@style/Theme.AppCompat.Light.NoActionBar">
- </activity>
需要把MyReactActivity的主题设定为Theme.AppCompat.Light.NoActionBar,因为里面有许多组件都使用了这一主题。
-
- public class MainActivity extends AppCompatActivity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- MainActivity.this.startActivity(new Intent(MainActivity.this, MyReactActivity.class));
- }
- });
- }
- }
代码就集成完毕,结构图如下:
使用USB连接电脑,通过adb devices命令查看设备如果有设备列表,则已经连接到设备,打开终端,切换到项目根目录执行命令:
$ yarn start
或者
$ react-native run-android
出现如图所示内容,Android project not found. Are you sure this is a React Native project?,这块的问题还是项目结构的问题,对比下刚刚开始的项目结构和上一步咱们自己项目结构:
发现缺少android目录,这块的android目录就使咱们上面的Demo目录,修改Demo为android目录,重新执行yarn start,这时候会出现两个终端界面,如下:
根据右边界面提示,点击键盘R,会看到屏幕的RN页面会刷新,点击键盘D会出现如下:
点击Reload也会重新加载,效果类似与终端上输入R一样。如果出现以下状态,则是Server未连接成功:
解决方案:
(1)通过adb devices命令查看设备是否连接成功,如果成功;
(2)重新执行yarn start,重新安装。
除了上面的运行方式外,还可以在Android Studio上面运行,在Android Studio上面Run app运行之后,在RN项目的根目录终端中输入:
$ react-native start
即可开启服务,操作和上面第一种方式类似。
打开android目录中的Android项目,面板最底部找到Terminal控制台,直接输入:
$ yarn start
效果和第一种方式一样。
在BaseActivity中添加了.addPackage(new MainReactPackage()),除此之外系统还提供了了个.addPackages(packages)方法,参数为:
List<ReactPackage> packages = new PackageList(getApplication()).getPackages();
直接使用会找不到PackageList类,是因为没有link,在React Native 0.60 加入了一个autolinking特性,不需要额外调用react-native link命令。修改android的setting.gradle,bulid.gradle就可以自动完成link。
setting.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)
这样重新clear项目,ReBuild Progect项目,就可以正常编译通过。关于autolink的详细原理,请参考:https://www.jianshu.com/p/9641b3beae84
其他参考资料:https://reactnative.cn/
这些东西整理了很久了今天才发现,顺便记录下。今天是腊月27日,明天回老家了,最后祝大家2023年新年快乐!!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。