Android项目接入React Native方案

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

        本篇文章主要介绍在现有的Android项目中接入React Native的接入过程分析接入过程中的一些问题和解决方案接入RN的平台为Android开发环境为Mac开发工具为Android Studio。

一、环境配置

1、Android配置

        因为是现有的Android项目集成RN所以关于Android方面的SDKJDK环境变量配置不做过多的介绍。需要注意的是环境变量的配置

终端输入$ 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/emulator

export 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

2、React Native配置

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 某第三方库名。

二、目录配置

1、项目结构

        首先需了解RN项目的目录结构不能在现在的Android项目中创建RN相关文件和模块否则会导致react-native link失败出现各种奇怪的问题报错如下

React Native CLI failed to determine Android project configuration.

2、项目初始化配置

        首先创建一个空目录用于存放 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用于安装项目到手机并且启动服务。

3、安装React和React Native模块

        打开一个终端/命令提示行进入到项目目录中即包含有 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就会发现该模块已经添加依赖了如下图

三、Android项目配置

        把新建的RN项目的目录中的Android项目使用打开Android Studio打开在编辑器上面完成以下操作

1、gradle配置

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】,如何能够正常编译则说明路径配置正常否则路径有误。

2、AndroidManifest.xml配置

(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访问限制。

四、代码集成

1、index.js创建

        index.js是 React Native应用在Android上的入口文件。而且它是不可或缺的它可以是个很简单的文件简单到可以只包含一行require/import导入语句。在RN项目的根据目录创建index.js和package.json同级目录。

2、添加测试的React Native代码

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
);

3、配置Activity

        配置一个基类的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();
    }
}

4、编写React Native页面

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因为里面有许多组件都使用了这一主题。

5、在MainActivity启动该RN页面


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));
            }
        });
    }
}

        代码就集成完毕结构图如下

五、项目运行 

1、命令行运行

        使用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重新安装。

2、Android Studio + 命令行运行

        除了上面的运行方式外还可以在Android Studio上面运行在Android Studio上面Run app运行之后在RN项目的根目录终端中输入

$ react-native start

        即可开启服务操作和上面第一种方式类似。

3、Android Studio中运行

        打开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年新年快乐😋😋😋

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: android