react native打包(android)应用多环境,多包共存实现方案探索

3,929 阅读6分钟

#背景

本人之前一直使用vue技术栈,进入现在的公司之后,接到总部通知需要技术栈需接轨国际化,即转换为react。于是开始了这一路的心酸之旅。

后来随着项目的深入,接到领导通知,需要做移动端app,由于此前已经有心理准备,所以没太多的意外。于是在疫情肆虐的间隙,我开始了react-native的探索之旅。期间发生了很多令人头疼的问题。比如,公司内网环境,react-native所需要的android原生依赖不能正常下载经常卡死,react-navigation导航器不熟悉,完全零基础,没有好用的UI组件,开发时间紧张,不熟悉原生模块配置等等。

好在经过短暂的系统学习脚手架顺利搭建完毕,业务也在匆忙中完成了。考虑到做pc后台管理系统的时候可以利用node环境变量来实现后端BASE_URL的动态切换来实现多环境打包。

于是,引入以下思考

1、react-native中是否也可以做到动态根据BASE_URL来打不同的包

2、打包的apk应用是否可以多包共存

3、app内如何实现切换BASE_URL

带着这些疑惑,开始了采坑之旅。 经过查资料得知有以下几点基础概念需要了解

defaultConfig :默认配置

buildTypes : 编译类型,默认的就是debug,release

productFlavors:渠道

buildConfigField : 配置宏,可以在gradle里配置一个值让java 代码中访问到,其中 defaultConfig buildTypes productFlavors 都可以配置buildConfigField。

那么看完这些,你一定能猜到,根据productFlavors是可以对打包的内容进行控制的。 没错,安卓应用是通过productFlavors里的applicationId来区分应用的,也就是每个applicationId必须是唯一的。 那么如果你想打多个包,比如本地开发环境dev,测试环境test,用户验收环境uat,正式环境prod。如何来实现呢?

这里假设你

1、已经成功搭建rn脚手架,但是需要区分环境打包

2、需要在一个手机上安装多个应用。且之前成功打包过安卓应用。

3、了解基本的rn编译成原生应用(android)的原理。

4、能看得懂简单的错误,并及时解决,不怕折腾和失败。

如果你不怕失败,那么让我们一起来时探索之旅吧!

步骤如下: 打开android/app/build.gradle,新增如下代码

android { 
    ...other code
    flavorDimensions "default"
    productFlavors {
         dev {
            applicationId "com.workshopapp.debug"
             manifestPlaceholders = [
                    app_name: "@string/app_name_debug",
                    app_icon: "@drawable/ic_launcher"
            ]
        }
        beta {
            applicationId "com.workshopapp.test"
             manifestPlaceholders = [
                    app_name: "@string/app_name_test",
                    app_icon: "@drawable/ic_launcher"
            ]
        }
        uat {
            applicationId "com.workshopapp.uat"
             manifestPlaceholders = [
                    app_name: "@string/app_name_uat",
                    app_icon: "@drawable/ic_launcher"
            ]
        }
        production {
            applicationId "com.workshopapp.prod"
             manifestPlaceholders = [
                    app_name: "@string/app_name",
                    app_icon: "@drawable/ic_launcher"
            ]
        }
    } 
    ...other code
}

manifestPlaceholders占位符的使用,可以用来配置app相关的内容, 这里只介绍最基础的桌面应用名称,即app_name。app_icon,即桌面图标。其他的如需详细配置可参照AndroidManifest.xml里内容自行配置。 这里需要注意的是productFlavors里的key不能和buildTypes里的key同名,也就是说不能设置,debug和release,除此之外,test也不可以作为有效的key,具体原因自行谷歌。

接着,我们需要设置 /android/app/src/main/AndroidManifest.xml里面的内容

在manifest里加入
xmlns:tools="http://schemas.android.com/tools"
替换application里的
android:icon="${app_icon}"
android:label="${app_name}"
tools:replace="android:label"
如果activity里有引用appname也需要替换
android:label="${app_name}"

然后就是配置android/app/src/main/res/values/strings.xml

这里app的名字是panda,可以替换成你自己的应用名称。
<resources>
    <string name="app_name">panda</string>
    <string name="app_name_debug">panda_dev</string>
    <string name="app_name_test">panda_test</string>
    <string name="app_name_uat">panda_uat</string>
</resources>

执行打包命令

cd android
./gradlew assembleRelease

如果嫌命令过长可以自己配置成容易记住的命令

package.json里的script中加入如下内容
bundle-android:cd android && ./gradlew assembleRelease
然后打包的时候执行yarn bundle-android即可。

运行下命令,此时发现已经打包出来四个应用,之前操作忘记截图,这里贴上打包出三个不同的包的图。

至此,我们已经实现了多包共存,安装到手机上如下图所示。

但是,至此别高兴地太早。我们只是实现了,多包共存,后端的公共api地址还没有根据不同的包来动态配置。那么如何实现呢? 首先需要引入一个包react-native-config 地址:github.com/luggit/reac…

yarn add react-native-config
或者
npm i react-native-config

在android/app/build.gradle中加入

// 2nd line, add a new apply:
apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle"

同样的在defaultConfig里加入

defaultConfig {
    ...
    resValue "string", "build_config_package", "YOUR_PACKAGE_NAME_IN_ANDROIDMANIFEST.XML"
}

然后在根目录下创建

加入你所需要的后台BASE_URL,比如

用于开发环境的
.env
BASE_URL = 'http://www.baseurl-dev.com'
.env.test
用于测试环境的
BASE_URL = 'http://www.baseurl-test.com'
...

在android/app/build.gradle中加入

project.ext.envConfigFiles=[
    debug:".env",
    prod:".env.prod",
    uat:".env.uat",
    beta:'.env.test',
]

在defaultConfig里加入

resValue "string", "build_config_package", "com.workshopapp"

此时已经配置完毕, 在统一封装的请求库中,这里使用axios示例如下。 由于对xcode比较陌生,加之公司尚未来得及申请ios企业开发者账号,这部分内容先空出来,利用rn内置的Platform判断,是android环境则利用上述配置文件中的环境变量。多环境打包否则使用自已写死的BASE_URL。。。(需要手动更改,后续研究再补上。)

...
import Config from 'react-native-config';
let instance = axios.create({
  baseURL: Platform.OS === 'android' ? Config.BASE_URL : BASE_URL,
  ...
});

接下来配置打包命令。package.json里的script中加入如下内容

"bundle-android-prod": "cd ./android && ENVFILE=.env.prod ./gradlew assembleProductionRelease",
"bundle-android-test": "cd ./android && ENVFILE=.env.test ./gradlew assembleBetaRelease",
"bundle-android-uat": "cd ./android && ENVFILE=.env.uat ./gradlew assembleUatRelease",

需要注意的是,这里不再介绍官方打包所需要的配置文件,如密钥文件,签名配置,gradle配置。如果你没有提前配置打包成功,建议你先打包官方demo跑起来,再参照本篇文章来继续实践。 至此,多环境打包已经配置完成。 比如,我们想打正式环境的包

执行 yarn bundle-android-prod

可以看到下图效果,读取的是.env.prod的环境变量。同样的,其他几个环境也以此类推。这里不再赘述。

由于百度和谷歌到的资料少之又少,期间基本是自己一个人摸索学习状态。好在android语法可以看懂一些,配置起来问题不是很多。最重要的是不怕失败,经得起折腾。 在此,非常感谢,以下文章的帮助。

react native之android多包共存解决方案:juejin.cn/post/684490…

Android打包之多版本、多环境、多渠道:www.jianshu.com/p/872dc6f89…

React Native 多环境配置:juejin.cn/post/684490…

第一次写文章,难免有不足之处。后续如果时间充裕,将继续进行rn项目的系列教程。 此外,至于如何在app里切换测试环境,开发环境。想必不用细说了,在之前的config里已经获取到环境变量,可以利用这个来实现。如果你也在使用react-native,并且热衷于探索,欢迎一起进步学习。