The statement

This article is suitable for front end engineers who have no experience in Android development

Project background

Due to its fast development speed, higher performance than Hybrid and other advantages, ReactNative is increasingly used in front-end development. However, many problems arise as a result:

  • It is difficult to distinguish the development environment, test environment, and formal environment. When sending test and formal packages, you need to manually modify a lot of environment configurations (such as the interface address of the request), which is tedious and error-prone
  • In order for the new package to overwrite the installation of the old package, the version number needs to be manually maintained and incremented before each package
  • Package generated files named app-debug.apk and app-release.apk are not easy to distinguish and archive
  • performreact-native run-androidWill open a shell window to run JS-server (which will be repeated in new versions), which is annoying

The solution

Environment to distinguish the

As a normal requirement, we need to distinguish between three environments — development environment, test environment, and formal environment (also called production environment). CD Android &&./ Gradlew assembleRelease CD Android &&./ Gradlew assembleRelease CD Android && gradlew assembleRelease It is obvious that the Release environment is used, but we also use this command to provide the test package to the test engineer, so the formal package and the test package use the same environment, and you have to manually modify the code to complete the environment differentiation.

To solve these problems, we need to introduce a new environment — Beta, or test environment. First we need to modify the android/app/build.gradle file of the project:

React = [bundleInBeta: true]; // If jsbundle is not set, the beta version will read jsbundle project.ext.react = [bundleInBeta: true]. android { ... buildTypes { release { ... } minifyEnabled false proguardFiles getDefaultProguardFile(' proGuard-android.txt '), 'proguard-rules.pro' } } }Copy the code

CD Android &&./ Gradlew Assemblblebeta: CD Android &&./ Gradlew assembleRelease: CD Android &&.

At this point, however, we are only changing the packaging command, and the actual configuration information is not automatically switched based on the environment. Then we need to continue to modify gradle files so that different environments read different configurations.

android { ... buildTypes { release { ... // Not a development environment, not a test environment, So the official environment buildConfigField "Boolean ", "IS_DEBUG", "false" buildConfigField" Boolean ", "IS_DEV", "false"} beta {... // Not a development environment, but a test environment, So test environment buildConfigField "Boolean ", "IS_DEBUG", "true" buildConfigField" Boolean "IS_DEV", Debug {// Is the development environment, is the test environment, So development environment buildConfigField "Boolean ", "IS_DEBUG", "true" buildConfigField" Boolean ", "IS_DEV", "true"}}}Copy the code

After setting it up, in the Android code, we can get our current runtime environment by buildconfig.is_debug, buildconfig.is_dev, and we can easily switch the configuration based on the environment. But this is just the level of the Android, we also need to pass this information to RN level, also need to know the running environment in js, at this time only need for Android/app/SRC/main/Java/XXX/MainActivity. Java to make small changes you can:

. public class MainActivity extends ReactActivity { ... class MyReactDelegate extends ReactActivityDelegate { final Context context; public MyReactDelegate(Activity activity, @javax.annotation.Nullable String mainComponentName) { super(activity, mainComponentName); context = activity; } @javax.annotation.Nullable @Override protected Bundle getLaunchOptions() { Bundle bundle = new Bundle(); bundle.putBoolean("isDebugMode", BuildConfig.IS_DEBUG); bundle.putBoolean("isDevMode", BuildConfig.IS_DEV); return bundle; }}}Copy the code

And then we get these two things in the root element of RN:

. class App extends Component { constructor (props) { super(props) console.log('isDebugMode', props.isDebugMode) console.log('isDevMode', props.isDevMode) } ... }...Copy the code

And I don’t have to tell you how to switch the configuration, do I? ~

Modify the package name and other package information

Above although has can distinguish between environmental packaging, but due to the package name, the same three versions of the installation package and cannot coexist on one device, and even can coexist, three showed the name of the package is installed has exactly the same ICONS are indistinguishable, so we need to do some operations that they can be easy to distinguish.

Modify the package name and display name

Android/app/build. Gradle file:

release { ... // Set the manifest placeholder to show manifestplaceholder = [APP_NAME: "@string/ APP_NAME "]} beta {... If you violate the manifestplaceholder, you will find that the manifest name is manifestplaceholder = [APP_NAME: "@string/app_name_beta" ] } debug { ... // Add the. Debug applicationIdSuffix ". Debug "// Set the manifest placeholder to show the manifestplaceholder of a different software name = [APP_NAME: "@string/app_name_debug" ] }Copy the code

Android/app/SRC/main/AndroidManifest. XML file:

<application
    ...
    android:label="${APP_NAME}">
    <activity
        ...
        android:name=".MainActivity"
        android:label="${APP_NAME}">
        ...
    </activity>
    ...
</application>
Copy the code

Android/app/SRC/main/res/values/strings. The XML file:

<resources> <string name="app_name"> Software XXX</string> <string name="app_name_beta"> Software XXX test </string> <string Name ="app_name_debug"> software XXX developer </string> </resources>Copy the code

!!!!!!!!! Note that RN’s default startup command can be packaged normally after the startup package name is changed, but the application will not start automatically. That is, after executing the react-native run-Android, we need to manually start the APP. But the RN team figured this out a long time ago, and it’s possible to solve this problem with parameters in cli. React-native run-android –appIdSuffix=debug

Modify the icon

The icon file is stored in android/app/ SRC /main/res (this is the path corresponding to the Release version, the Beta version will replace main with Beta, the Debug version will replace main with Debug, if the directory does not exist, you can create it by yourself). According to android icon rules put resources in the corresponding directory can be. Here’s a one-click app icon site: icon.wuruihong.com/. Note: no advertising fee.

I made an icon to distinguish the environment

The version number is increased and the APK file name is changed

Android /app/build.gradle:

. Def versionNamePrefix = "1.0" Git rev-list HEAD --count'.execute().gettext ().trim() // use git describe as version description def revisionDescription = 'git describe --always'.execute().getText().trim() def verName = versionNamePrefix + "." + revisionNumber + "." + revisionDescription android { ... applicationVariants.all { variant -> variant.outputs.each { output -> output.outputFile = new File(output.outputFile.parent, "XXXXX_" + "v" + verName + "_" + buildType.name + ".apk"); }}}Copy the code

Apk, XXXXX_v1.0.214.2837e31_release.apk, xxxxX_v1.0.214.2837 e31_beta.apk, and XXXXX_v1.0.214.2837e31_debug.apk are generated.

Avoid launching popovers

There are two advantages to doing this. First, you don’t have to pop up a box every time you start up. Second, when multiple RN projects are switched, the bundle read by the project will not be misnamed because the project forgot to turn off jS-server.

React-native run-android –appIdSuffix=debug –no-packager We can also configure a script for it in package.json.

concurrently -r 'react-native start' 'react-native run-android --appIdSuffix=debug --no-packager'