在React native 【0.73.6】【Android】问题避坑

1,465 阅读4分钟

react native APP横屏锁定

在android\app\src\main\AndroidManifest.xml 中设置 android:screenOrientation属性的值来实现横屏(landscape)、竖屏(portrait)锁定,在纯RN项目中设置在默认的一个activity即可

    <application
     ...,
      android:theme="@style/AppTheme">
      <activity
        android:screenOrientation="landscape"
        android:exported="true">
       
      </activity>
    </application>

react native的原生model与antd model无法全屏展示

  • 使用react native 的StatusBar组件,无法有效隐藏掉状态栏
  • react调用原生model还是react native antd的model模块使样式100%都无法全屏展示,【电池。信号】这种系统状态栏依旧存在。也就是内容无法从状态栏处开始布局

最快解决思路,强制设备在当前APP隐藏状态栏,直接设置新的Theme主题,并且AndroidManifest.xml使用这个主题

编辑新增 android/app/src/main/res/values/styles.xml:

NoActionBar.FullScreen这个代表:没有动作栏,并且是全屏的

<style name="AppTheme"        parent="Theme.ReactNative.AppCompat.Light.NoActionBar.FullScreen">
<!-- Customize your theme here. -->
</style>

android\app\src\main\AndroidManifest.xml使用这个主题,@style/AppTheme

 <application
    
      android:theme="@style/AppTheme">
      <activity
       ...,
        android:exported="true">
       
      </activity>
    </application>

在React Native项目中将'src·目录配置为别名`@'

  • 在你的React Native项目中,你可以使用`babe1-plugin-module-resolver'来设置别名。
  • npm install--save-dev babel-plugin-module-resolver
  • .在项目的根目录下babel.config.js添加
const prodPlugins = [];
console.log('process.env.BABEL_ENV::', process.env.BABEL_ENV)
if(process.env.BABEL_ENV !== 'development'){
  // 生产环境移除console
  prodPlugins.push("transform-remove-console");
}

module.exports = {
  presets: ['module:metro-react-native-babel-preset'],
  plugins: [
    ...prodPlugins,
    ["import", { libraryName: "@ant-design/react-native" }], // 与 Web 平台的区别是不需要设置 style
    [
      'babel-plugin-root-import',
      {
        paths: [
          {
            rootPathSuffix: './src',
            rootPathPrefix: '@src', // 使用 ~/  代替 ./src (~指向的就是src目录)
            
          },
          {
            rootPathSuffix: './config',
            rootPathPrefix: '@config', // 使用 ~/  代替 ./src (~指向的就是src目录)
            
          },
         
        ],
      },
    ],
  ]
};

react native api请求区分测试环境还是正式环境

这里引用react-native-config的插件。目前npm已经是1.5.2的版本,现使用的是1.5.1的版本

1.安装react-native-config

yarn add react-native-config

2.创建.env.development 创建 env.development

3.android/settings.gradle

include ':react-native-config'
project(':react-native-config').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-config/android')

image.png

4.build.gradle

apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle";
dependencies {
    // The version of react-native is set by the React Native Gradle Plugin
    implementation("com.facebook.react:react-android")
    implementation("com.facebook.react:flipper-integration")
    implementation project(':react-native-config')
    if (hermesEnabled.toBoolean()) {
        implementation("com.facebook.react:hermes-android")
    } else {
        implementation jscFlavor
    }
}

image.png

5.业务代码引入

import Config from 'react-native-config';
const BASE_URL = Config && Config.API_URL ? Config.API_URL : '/'; a

image.png

react native APK区分正式环境还是测试环境

测试人员需要打包的测试apk与正式apk在android的设备能够共存。且测试apk在android需要显示名称和版本 app区分api请求地址是环

解决思路

这里是使用package.json来定义APP名称与版本,在build.gradle定义,

//引入可以加载解析的json
import groovy.json.JsonSlurper;

// 定义一个函数来加载和解析 package.json
def loadPackageJson() {
    // 使用 projectDir 获取项目根目录,然后 parentFile 来获取上级目录
   
    def packageJsonFile = new File(project.projectDir.parentFile.parentFile, 'package.json')
    if (!packageJsonFile.exists()) {
        throw new FileNotFoundException("Cannot find package.json at ${packageJsonFile.path}")
    }
    def jsonSlurper = new JsonSlurper()
    def packageJson = jsonSlurper.parse(packageJsonFile)
    return packageJson
}



def packageJson = loadPackageJson();//获取packageJson的对象


println "Package version: ${packageJson.version}"
println "Package appName: ${packageJson.appName}"


android {
   applicationVariants.all { variant ->
        variant.outputs.all {
              def versionName = variant.versionName
                def appName = packageJson.appName ?: "DefaultAppName"
            def buildType = variant.buildType.name
            def envName = variant.name.capitalize()

             if (buildType == 'debug') {
                outputFileName = "${appName}测试版 v${versionName}.apk"
                variant.resValue "string", "app_name", "${appName}测试版 v${versionName}"
            } else if (buildType == 'release') {
                outputFileName = "${appName}正式版 v${versionName}.apk"
            }
        
        }
    }
}

如何cli工程化。借助packjson的script命令行去执行一段js

思路

  1. 确定版本。提供版本累加
  2. 确定环境。如果是测试环境则回滚packjson.避免git提交影响.正式则不需要回滚

步聚

1.获取用户选择发布选项 2.先备份packjson 3.写入packjson版本,因为打包是获取packjson的信息去打包 4.CI 流程执行成功!如果是测试环境则回滚

效果

bec3be2f424bf1ba5191c7f2e4244a2.png

0bef0b1c31f59609a01112c9e7fe7a7.png

3e8e74fb4f3876a434cc404440ed110.png

cli部分代码

async function runCI() {

  const questions = [
    {
      type: 'list',
      name: 'selectedOption',
      message: `当前版本是${packjosn.version},请选择发布版本选项:`,
      choices: [
        { value: 1, name: `大版本 ${await getVersionFromOption(1)}` },
        { value: 2, name: `中版本 ${await getVersionFromOption(2)}` },
        { value: 3, name: `小版本 ${await getVersionFromOption(3)}` },
      ],
      default: '',
    },
    // 添加其他需要的选项
    {
      type: 'list',
      name: 'selectedDev',
      message: `请选择发布环境选项:`,
      choices: [
        { value: 'dev', name: '测试环境', short:"测试环境,packjson版本不变化"},
        { value: 'prod', name: '正式环境', short:"正式环境,packjson版本变化" },
      ],
      default: 'dev',
    },
  ];
  try {
  // 用户选择发布选项
    const releaseOptions = await inquirer.prompt(questions);   
  // 查找用户选择的选项的 name
  const selectedOption = questions[0].choices.find(choice => choice.value === releaseOptions.selectedOption);
  const selectedDev = questions[1].choices.find(choice => choice.value === releaseOptions.selectedDev);
  const env = releaseOptions.selectedDev;
  const newVersion = await getVersionFromOption(releaseOptions.selectedOption);
   //先备份packjson
  await removeCopyBuildPackjson()
  await copyBuildPackjson()
   //写入packjson版本,因为打包是获取packjson的信息去打包
   let bool = await writeBuildPackjson(newVersion,env)
    if(!bool) return
    await scriptCommandFn(async()=>{
    console.log(`CI 流程执行成功!打包【${selectedOption.name}】【${selectedDev.name}】`)
    if(env=='dev'){
       //如果测试环境就回滚packjson,不对git造成影响
      await callBackPackjson()
      await removeCopyBuildPackjson()
    }else{
      await removeCopyBuildPackjson()
    } 
  },env);   
  } catch (error) {
    console.error('执行构建步骤时发生异常:', error.message);
    await callBackPackjson()
    await removeCopyBuildPackjson()
    process.exit(1);
  }
}


在打包测试包给其他人使用的情况下红屏 react-navite 找不到 index.android.bundle

image.png

在 React Native 中,index.android.bundle 是一个包含了您应用中所有 JavaScript 代码的打包文件。这个文件是通过 React Native 的打包器(通常是 Metro)生成的,它将所有的 JavaScript 文件和模块合并成一个单一的文件,以便在 Android 设备上运行。

为什么这个步骤是重要的:

  • 独立性:打包后的应用不需要连接到 Metro 服务器就可以运行,这对于测试环境尤其重要,因为您可能需要在没有开发服务器的情况下运行应用。
  • 性能:打包的应用通常性能更好,因为所有的资源都已经被本地化,不需要通过网络加载。
  • 资源整合:通过 --assets-dest 参数,所有的图片和资源文件都会被复制到正确的位置,确保它们能够在应用中正确显示。
  • 模拟生产环境:这个过程模拟了生产环境的构建过程,帮助您发现可能只在打包版本中出现的问题。

因此可以总结,报这个错误是本地没有index.android.bundle,是你在打包测试apk,这个apk是需要连接到本地Metro 服务器才可以运行,这意味着应用无法找到执行的 JavaScript 代码。

当您构建一个 React Native 应用并准备在 Android 设备上运行时,index.android.bundle 会被创建并包含在 APK 或 AAB 文件中。这个 bundle 文件是应用运行时执行的 JavaScript 代码的来源,它使得 React Native 能够在 Android 平台上运行 JavaScript 代码并渲染相应的 UI 组件。

解决方式

react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res

在package.json添加自己的命令行

  1. build:dev_before
  2. build:dev_after
{
scripts: {
    "build:dev_before": "react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res",
    "build:dev_after": "cd android  && cross-env ENVFILE=.env.development && ./gradlew assembleDebug",
    "build:dev": "yarn build:dev_before && yarn build:dev_after",
    "build:prod": "cd android && cross-env ENVFILE=.env.production && ./gradlew assembleRelease",
  },
}