# React Native Android Directory Information and Project Structure. ## Basic Structure ![](https://hackmd.io/_uploads/HJ4p0NVvh.png) As you can see in the picture Android directory comes with some prebuilt directories and files ### Directories ***.gradle Directory:*** This directory is created/extracted from the gradle zip which is downloaded at run time for dependencies. ***.idea Directory:*** This directory is created by Android Studio when we open a project in android studio. In this directory, Android Studio adds indexing and other data which is required by the IDE. ***app Directory:*** This directory contains all the resources and code which was to app design and flow. We can say, it is our main working directory where our app code is written and resources added like fonts images app icons. ***build Directory:*** This is a project-level build directory which is created at a buil time to store project-level build caches ***gradle Directory:*** This directory contains gradle/dependencies download required jar files like gradle-wrapper.jar. It will be used for downloading gradle dependencies files ### Files ***build.gradle File:*** This file contains required information regarding build creation like Android target SDK minimum required SDK. Where to find external dependencies like Google, maven, or Maven central ***gradle.properties File:*** This file contains some variables which are used at the build time we can say it is a config file or env file ***gradlew File:*** This file contains a script for Android build generation. This file is used in macOS and Linux distributed systems for build generation. This file is written in a shell script. ***gradlew.bat File:*** This file contains a script for Android build generation. This file is used in Windows systems for build generation. This file is written in the batch script. ***local.properties File:*** This file contains local environment-required files like the SDK directory path. ***settings.gradle File:*** This file contains scripts that should be executed before build generating script execute. ## android/app Directory ![](https://hackmd.io/_uploads/BkCVbSVvn.png) ***build Directory:*** This directory contains the output of build commands like APK or aab file and its required metadata ***src Directory:*** This directory contains the resources and source code of the app. ## android/app Files ***build.gradle File:*** This is an app-level build configuration file. This file has build-required data stored like build type, Signing Config, required dependencies, etc. ***debug.keystore File:*** This is the keystore file used to sign the debug build. ***[proguard-rules.pro](https://developer.android.com/build/shrink-code) File:*** This file contains proguard configuration. Which helps make the app lightweight and smoother. ## android/app/src Directory ![](https://hackmd.io/_uploads/SkrdU8Nvn.png) ***debug Directory:*** This directory is autogenerated while you run an app in debug mode. This directory contains the compiled Java file which is used by the debug application. ***main Directory:*** This directory contains the app source code and the various resources required app like Images, Fonts, Colors, App Icons, etc. ***release Directory:*** This directory is autogenerated while you generate the app in release mode. This directory contains the compiled Java file which is used by the release application. ## android/app/src/main Directory ![](https://hackmd.io/_uploads/Syhb_LNP2.png) ***assets Directory:*** This directory contains the assets required by the app like fonts and bundle file which is created/required in released builds. This file is named index.android.bundle. This file is the replacement of the metro servers in the released mode APK/AAB. fonts should be added to the fonts directory. ***java Directory:*** This directory contains the main package of the app code. In this directory, nested directories separated with a dot are created as per the project package name. Inside the list package name directory, the main Java code of the application is present. The newarchitecture directory was previously used for enabling newarchitecture in Android now these directory files are removed and by default is comes with newarchitecture no longer needed these files. ***jni Directory:*** This directory was previously used to contain C++ files required for the new architecture of react native. which is now no longer required and so it comes with a blank directory in new projects. ***res Directory:*** This directory contains the app-required resources like images, mipmaps, values, layouts, etc. ## android/app/src/main Files ***AndroidManifest.xml File:*** This file contains the data behavior of the application which is required to communicate with OS. Like which permission is app using? How Many activities (screens) are present in the app? Which resource should be used as an App Icon? which App display name in the home/app screen? and Which activity should be used as an entry point of the application? The entry point of the application defined via the Intent-Filter tag. Inside Activity tag which is present under Application Tag. ## android/app/src/main/res/(drawable/drawable-\*dpi) Directory/Directories These directories contain the Application required Image resources. Here at *dpi can be replaced with the following ones. ***mdpi:*** This folder will contain resources for those devices which has lower device specification and lower display resolution. generally 160dpi displays ***hdpi:*** This folder will contain resources for those devices which has lower device specification and lower display resolution. generally 240dpi displays ***xhdpi:*** This folder will contain resources for those devices which has medium device specification and medium display resolution. generally 320dpi displays ***xxhdpi:*** This folder will contain resources for those devices which has lower Higher specification and Higher display resolution. generally 480dpi displays ***xxxhdpi:*** This folder will contain resources for those devices which has Higher device specification and Higher display resolution. generally 640dpi displays ***anydpi:*** This folder will contain resources that can be used on any configuration device. So it must be an ideal resolution resource. You can also use only the drawable named folder instead of this folder. ## android/app/src/main/res/(mipmap/mipmap-\*dpi) Directory/Directories These directories contain the application-required Image/SVG resources. Here at *dpi can be replaced with above mentioned in drawable Directories. These directories are generally used for storing app icons ## android/app/src/main/res/values Directory These directories contain the application required color, string, theme related resources. The above-mentioned data is in color.xml, stings.xml, and styles.xml files. Android Studio gives you a warning when you use hardcoded colors, and strings in the app and encourages you to put it in the above-mentioned file and use the reference of it. --- # Deep-Dive ## build.gradle File at project level ``` // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { ext { buildToolsVersion = "33.0.0" minSdkVersion = 21 compileSdkVersion = 33 targetSdkVersion = 33 // We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP. ndkVersion = "23.1.7779620" } repositories { google() mavenCentral() } dependencies { classpath("com.android.tools.build:gradle:7.3.1") classpath("com.facebook.react:react-native-gradle-plugin") classpath("com.google.gms:google-services:4.3.15") } } ``` This file contains app common configurations. In this file, all configurations are written in the buildscript block. The ext block contains some common versions of tools required for running/building apps like buildToolsVersion and ndkVersion. minSdkVersion is used to define the minimum android required version for running the app. compileSdkVersion is used to tell buildscript should be used compile time using SDK version which should take as the latest version. targetSdkVersion is used to specify the build script which android SDK we are targetting to build should work smoothly. The repositories block contains places where should gradle search the dependencies. The dependencies block contains project-required dependencies and their versions. ## gradle.properties file ``` # Project-wide Gradle settings. # IDE (e.g. Android Studio) users: # Gradle settings configured through the IDE *will override* # any settings specified in this file. # For more details on how to configure your build environment visit # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true # AndroidX package structure to make it clearer which packages are bundled with the # Android operating system, and which are packaged with your app's APK # https://developer.android.com/topic/libraries/support-library/androidx-rn android.useAndroidX=true # Automatically convert third-party libraries to use AndroidX android.enableJetifier=true # Version of flipper SDK to use with React Native FLIPPER_VERSION=0.125.0 # Use this property to specify which architecture you want to build. # You can also override it from the CLI using # ./gradlew <task> -PreactNativeArchitectures=x86_64 reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 # Use this property to enable support to the new architecture. # This will allow you to use TurboModules and the Fabric render in # your application. You should enable this flag either if you want # to write custom TurboModules/Fabric components OR use libraries that # are providing them. newArchEnabled=false # Use this property to enable or disable the Hermes JS engine. # If set to false, you will be using JSC instead. hermesEnabled=true ``` Initially, the gradle.properties file contains the above-like content which is used to store some variable values. It acts like an env file. All the variable usage and description can be found in comments written above the declaration. Still, you can see refer below to get an understanding of it. ``` org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m ``` The above variable is used to specify how much memory should be used by the Gradle daemon during the build process ``` android.useAndroidX=true ``` The above variable forces Android to use the AndroidX package structure. ``` android.enableJetifier=true ``` The above variable automatically converts the old package structure to the AndroidX project structure. ``` FLIPPER_VERSION=0.125.0 ``` The above variable specifies which flipper sdk should be used. ``` reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 ``` The above variable specifies which architecture should be supported in the build. ``` newArchEnabled=false ``` The above variable specifies is newArchitecture is enabled or not. Enable it if you want to integrate turbo modules/ fabric components as a renderer. ``` hermesEnabled=true ``` The above variable specifies that is Hermes engine is enabled or not. which is used for js to native code communication. If is false then the app will use javascript bridge to communicate which is slower than herms. ``` MYAPP_UPLOAD_STORE_FILE=*****.keystore/.jks MYAPP_UPLOAD_KEY_ALIAS=***** MYAPP_UPLOAD_STORE_PASSWORD=***** MYAPP_UPLOAD_KEY_PASSWORD=***** ``` You will add the above variables while you want to generate a release build. this variable is used to sign the app. Generally, we .keystore files for signing the app. ## AndroidManifest.xml file ``` <manifest xmlns:android="http://schemas.android.com/apk/res/android" // application package name package="com.example.demo"> <uses-permission android:name="android.permission.INTERNET" /> <!-- Permissions of the app is goes here --> <application android:name=".MainApplication" // application class name should be intitalized when application run android:label="@string/app_name" // specify the app name taken from the strings.xml file found in android/app/src/main/res/values folder android:icon="@mipmap/ic_launcher" // specifies normal app icon resource where @mipmap refers android/app/src/main/res/mipmap directories android:roundIcon="@mipmap/ic_launcher_round" // specifies round app icon resource name where @mipmap refers android/app/src/main/res/mipmap directries android:allowBackup="false" // specify app data should be part of the backup of the device. // specifies which style should be applied to the application where @style refers to android/app/src/main/res/values/styles.xml file android:theme="@style/AppTheme"> <activity android:name=".MainActivity" // activity class name should be used when activity initalize android:label="@string/app_name" // specify the screen name taken from the strings.xml file found in android/app/src/main/res/values folder android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode" // to specify when to not restart the application. android:launchMode="singleTask" // forces other activities to be removed and making it run as a single activity android:screenOrientation="portrait" // specifies screen orientation android:exported="true" // to allow other application to be able to open this application via deeplinks // to handle input mode in the activity/screen android:windowSoftInputMode="adjustPan"> <!-- The below intent filter is used for the specify starting screen of app. --> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest> ``` The AndroidManifest.xml file contains the above-like code. The file content starts with the manifest tag in the manifest tag we provide the application's package name In the manifest tag, there is a uses-permission tag. In this tag android:name property we can specify permission which is used by the app. You can add as many tags as you add permissions. Inside the manifest tag, you can find the application tag which accepts app required resources data like application class name, app name, App icon, App round icon, and theme and should be part of the backup or not Inside the application tag, you can see an activity tag which refers to a screen of the app which accepts class name, activity name, launch mode, orientation, etc. Inside MainActivity class named activity, you will find an intent-filter tag with action and a category child tag with its property. It is used to define the start activity when the app is loaded. ## build.gradle file at app level ``` apply plugin: "com.android.application" apply plugin: "com.facebook.react" apply plugin: "com.google.gms.google-services" import com.android.build.OutputFile /** * This is the configuration block to customize your React Native Android app. * By default you don't need to apply any configuration, just uncomment the lines you need. */ react { /* Folders */ // The root of your project, i.e. where "package.json" lives. Default is '..' // root = file("../") // The folder where the react-native NPM package is. Default is ../node_modules/react-native // reactNativeDir = file("../node_modules/react-native") // The folder where the react-native Codegen package is. Default is ../node_modules/react-native-codegen // codegenDir = file("../node_modules/react-native-codegen") // The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js // cliFile = file("../node_modules/react-native/cli.js") /* Variants */ // The list of variants to that are debuggable. For those we're going to // skip the bundling of the JS bundle and the assets. By default is just 'debug'. // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants. // debuggableVariants = ["liteDebug", "prodDebug"] /* Bundling */ // A list containing the node command and its flags. Default is just 'node'. // nodeExecutableAndArgs = ["node"] // // The command to run when bundling. By default is 'bundle' // bundleCommand = "ram-bundle" // // The path to the CLI configuration file. Default is empty. // bundleConfig = file(../rn-cli.config.js) // // The name of the generated asset file containing your JS bundle // bundleAssetName = "MyApplication.android.bundle" // // The entry file for bundle generation. Default is 'index.android.js' or 'index.js' // entryFile = file("../js/MyApplication.android.js") // // A list of extra flags to pass to the 'bundle' commands. // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle // extraPackagerArgs = [] /* Hermes Commands */ // The hermes compiler command to run. By default it is 'hermesc' // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc" // // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map" // hermesFlags = ["-O", "-output-source-map"] } /** * Set this to true to create four separate APKs instead of one, * one for each native architecture. This is useful if you don't * use App Bundles (https://developer.android.com/guide/app-bundle/) * and want to have separate APKs to upload to the Play Store. */ def enableSeparateBuildPerCPUArchitecture = false /** * Set this to true to Run Proguard on Release builds to minify the Java bytecode. */ def enableProguardInReleaseBuilds = false /** * The preferred build flavor of JavaScriptCore (JSC) * * For example, to use the international variant, you can use: * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` * * The international variant includes ICU i18n library and necessary data * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that * give correct results when using with locales other than en-US. Note that * this variant is about 6MiB larger per architecture than default. */ def jscFlavor = 'org.webkit:android-jsc:+' /** * Private function to get the list of Native Architectures you want to build. * This reads the value from reactNativeArchitectures in your gradle.properties * file and works together with the --active-arch-only flag of react-native run-android. */ def reactNativeArchitectures() { def value = project.getProperties().get("reactNativeArchitectures") return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"] } apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle" apply from: "../../node_modules/react-native-code-push/android/codepush.gradle" apply from: "../../node_modules/@sentry/react-native/sentry.gradle" android { ndkVersion rootProject.ext.ndkVersion compileSdkVersion rootProject.ext.compileSdkVersion namespace "com.example.demo" defaultConfig { applicationId "com.example.demo" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 69 versionName "2.0.2" } splits { abi { reset() enable enableSeparateBuildPerCPUArchitecture universalApk false // If true, also generate a universal APK include (*reactNativeArchitectures()) } } signingConfigs { debug { storeFile file('debug.keystore') storePassword 'android' keyAlias 'androiddebugkey' keyPassword 'android' } release { if (project.hasProperty('MYAPP_UPLOAD_STORE_FILE')) { storeFile file(MYAPP_UPLOAD_STORE_FILE) storePassword MYAPP_UPLOAD_STORE_PASSWORD keyAlias MYAPP_UPLOAD_KEY_ALIAS keyPassword MYAPP_UPLOAD_KEY_PASSWORD } } } buildTypes { debug { signingConfig signingConfigs.debug } release { // Caution! In production, you need to generate your own keystore file. // see https://reactnative.dev/docs/signed-apk-android. signingConfig signingConfigs.release minifyEnabled enableProguardInReleaseBuilds proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" } } // applicationVariants are e.g. debug, release applicationVariants.all { variant -> variant.outputs.each { output -> // For each separate APK per architecture, set a unique version code as described here: // https://developer.android.com/studio/build/configure-apk-splits.html // Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc. def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4] def abi = output.getFilter(OutputFile.ABI) if (abi != null) { // null for the universal-debug, universal-release variants output.versionCodeOverride = defaultConfig.versionCode * 1000 + versionCodes.get(abi) } } } } dependencies { // The version of react-native is set by the React Native Gradle Plugin implementation("com.facebook.react:react-android") implementation platform('com.google.firebase:firebase-bom:28.2.0') implementation 'com.google.firebase:firebase-analytics' implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.0.0") debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") { exclude group:'com.squareup.okhttp3', module:'okhttp' } debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") if (hermesEnabled.toBoolean()) { implementation("com.facebook.react:hermes-android") } else { implementation jscFlavor } } apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) ``` You will find the apply plugin statement which is used to add the package required by Gradle. After that, you will find react block which is used to customize the react native application default configuration After that, you will find the Android block which specifies build script configurations. Which build tools version should be used like SDKs? There you will find the default config block inside the android block which contains the application id, application version, application version code, and min and target sdk versions. After that, you will find a splits block inside the Android block. Which is used for specifying how the different architecture output files should be split. After that, you will find the signingConfigs block inside an Android block. Which is used to define various signing configs like keystore files, passwords, and key alias. After that, you will find the buildTypes block inside the Android block. This is used to define various build types which add commands in gradle to generate different types of builds like debug and release. After that, you will find applicationVariants.all block which will get the list of all architectures and iterate through all architecture and generates output for all architectures. At the root level, you will find the dependencies block. Which is used for defined app-required dependencies, Third party packages, etc. apply from is used for applying gradle config from other Gradle files. ## MainActivity.java file ``` package com.example.demo; import com.facebook.react.ReactActivity; import com.facebook.react.ReactActivityDelegate; import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint; import com.facebook.react.defaults.DefaultReactActivityDelegate; public class MainActivity extends ReactActivity { /** * Returns the name of the main component registered from JavaScript. This is * used to schedule * rendering of the component. */ @Override protected String getMainComponentName() { return "example"; } /** * Returns the instance of the {@link ReactActivityDelegate}. Here we use a util * class {@link * DefaultReactActivityDelegate} which allows you to easily enable Fabric and * Concurrent React * (aka React 18) with two boolean flags. */ @Override protected ReactActivityDelegate createReactActivityDelegate() { return new DefaultReactActivityDelegate( this, getMainComponentName(), // If you opted-in for the New Architecture, we enable the Fabric Renderer. DefaultNewArchitectureEntryPoint.getFabricEnabled(), // fabricEnabled // If you opted-in for the New Architecture, we enable Concurrent React (i.e. // React 18). DefaultNewArchitectureEntryPoint.getConcurrentReactEnabled() // concurrentRootEnabled ); } } ``` This file contains MainActivity public class which is Extending ReactActivity class. Inside class code block you will find getMainComponentName method which is used for overriding return value of ReactActivity class methods returning value. Here you will also find another overriding method called createReactActivityDelegate method which returns ReactActivityDelegate class instance for rendering purpose of the app with some configuration changes. ## MainApplication.java file ``` package com.example.demo; import android.app.Application; import com.facebook.react.PackageList; import com.facebook.react.ReactApplication; import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactPackage; import com.microsoft.codepush.react.CodePush; import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint; import com.facebook.react.defaults.DefaultReactNativeHost; import com.facebook.soloader.SoLoader; import java.util.List; public class MainApplication extends Application implements ReactApplication { private final ReactNativeHost mReactNativeHost = new DefaultReactNativeHost(this) { @Override public boolean getUseDeveloperSupport() { return BuildConfig.DEBUG; } @Override protected List<ReactPackage> getPackages() { @SuppressWarnings("UnnecessaryLocalVariable") List<ReactPackage> packages = new PackageList(this).getPackages(); // Packages that cannot be autolinked yet can be added manually here, for // example: // packages.add(new MyReactNativePackage()); return packages; } @Override protected String getJSMainModuleName() { return "index"; } @Override protected boolean isNewArchEnabled() { return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; } @Override protected Boolean isHermesEnabled() { return BuildConfig.IS_HERMES_ENABLED; } @Override protected String getJSBundleFile() { return CodePush.getJSBundleFile(); } }; @Override public ReactNativeHost getReactNativeHost() { return mReactNativeHost; } @Override public void onCreate() { super.onCreate(); SoLoader.init(this, /* native exopackage */ false); if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { // If you opted-in for the New Architecture, we load the native entry point for // this app. DefaultNewArchitectureEntryPoint.load(); } ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); } } ``` This file is creating a public class called MainApplication class which is extending the Application class which also implements the ReactApplication interface. At the start of the class code block, we define the DefaultReactNativeHost class instance with overrides of getUseDeveloperSupport, getPackages, getJSMainModuleName, isNewArchEnabled, isHermesEnabled, getJSBundleFile methods. Where getUseDeveloperSupport returns that the app is running in debug mode. getPackages returns the list of packages which is added by React Native third-party packages. Here all packages get found automatically all thanks to auto-linking. If any package does not get linked then it will be added here. getJSMainModuleName returns the string value of the JS main module name like the entry point of react native application. isNewArchEnabled returns whether that new architecture is enabled in-app or not. isHermesEnabled returns if the herms engine is enabled or not. getJSBundleFile returns a path to jsBundle which is in debug mode and a metro server in release mode index.android.bundle file for this above file example is returning code push js bundle file which helps to push over-the-air js updates to the app. The if statement checks if the IS_NEW_ARCHITECTURE_ENABLED flag is set to true in the BuildConfig class. If the flag is set to true, the DefaultNewArchitectureEntryPoint.load() method is called to load the native entry point for the new architecture. The ReactNativeFlipper.initializeFlipper() method is called to initialize the ReactNativeFlipper library, which provides additional debugging tools for ReactNative applications. The method takes two parameters: the Context of the application and the ReactInstanceManager of the ReactNativeHost. Overall, this code is used to initialize various components of the application, including the SoLoader library, the new architecture native entry point (if enabled), and the ReactNativeFlipper library. Within the class block also you will find the getReactNativeHost overriding method which returns the above created DefaultReactNativeHost class instance. After that, you will find the onCreate overriding method of the android lifecycle. Inside this method super.onCreate method to ensure the class is properly initialized. The SoLoader.init() method is called to initialize the SoLoader library, which is used to load native libraries in the application. The second parameter is set to false to indicate that the native libraries are not packaged as an exopackage. # Build Flavors ``` apply plugin: "com.android.application" apply plugin: 'com.google.gms.google-services' import com.android.build.OutputFile import org.apache.tools.ant.taskdefs.condition.Os /** * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets * and bundleReleaseJsAndAssets). * These basically call `react-native bundle` with the correct arguments during the Android build * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the * bundle directly from the development server. Below you can see all the possible configurations * and their defaults. If you decide to add a configuration block, make sure to add it before the * `apply from: "../../node_modules/react-native/react.gradle"` line. * * project.ext.react = [ * // the name of the generated asset file containing your JS bundle * bundleAssetName: "index.android.bundle", * * // the entry file for bundle generation. If none specified and * // "index.android.js" exists, it will be used. Otherwise "index.js" is * // default. Can be overridden with ENTRY_FILE environment variable. * entryFile: "index.android.js", * * // https://reactnative.dev/docs/performance#enable-the-ram-format * bundleCommand: "ram-bundle", * * // whether to bundle JS and assets in debug mode * bundleInDebug: false, * * // whether to bundle JS and assets in release mode * bundleInRelease: true, * * // whether to bundle JS and assets in another build variant (if configured). * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants * // The configuration property can be in the following formats * // 'bundleIn${productFlavor}${buildType}' * // 'bundleIn${buildType}' * // bundleInFreeDebug: true, * // bundleInPaidRelease: true, * // bundleInBeta: true, * * // whether to disable dev mode in custom build variants (by default only disabled in release) * // for example: to disable dev mode in the staging build type (if configured) * devDisabledInStaging: true, * // The configuration property can be in the following formats * // 'devDisabledIn${productFlavor}${buildType}' * // 'devDisabledIn${buildType}' * * // the root of your project, i.e. where "package.json" lives * root: "../../", * * // where to put the JS bundle asset in debug mode * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", * * // where to put the JS bundle asset in release mode * jsBundleDirRelease: "$buildDir/intermediates/assets/release", * * // where to put drawable resources / React Native assets, e.g. the ones you use via * // require('./image.png')), in debug mode * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", * * // where to put drawable resources / React Native assets, e.g. the ones you use via * // require('./image.png')), in release mode * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", * * // by default the gradle tasks are skipped if none of the JS files or assets change; this means * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to * // date; if you have any other folders that you want to ignore for performance reasons (gradle * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ * // for example, you might want to remove it from here. * inputExcludes: ["android/**", "ios/**"], * * // override which node gets called and with what additional arguments * nodeExecutableAndArgs: ["node"], * * // supply additional arguments to the packager * extraPackagerArgs: [] * ] */ project.ext.react = [ enableHermes: false, // clean and rebuild if changing ] apply from: "../../node_modules/react-native/react.gradle" /** * Set this to true to create two separate APKs instead of one: * - An APK that only works on ARM devices * - An APK that only works on x86 devices * The advantage is the size of the APK is reduced by about 4MB. * Upload all the APKs to the Play Store and people will download * the correct one based on the CPU architecture of their device. */ def enableSeparateBuildPerCPUArchitecture = false /** * Run Proguard to shrink the Java bytecode in release builds. */ def enableProguardInReleaseBuilds = false /** * The preferred build flavor of JavaScriptCore. * * For example, to use the international variant, you can use: * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` * * The international variant includes ICU i18n library and necessary data * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that * give correct results when using with locales other than en-US. Note that * this variant is about 6MiB larger per architecture than default. */ def jscFlavor = 'org.webkit:android-jsc:+' /** * Whether to enable the Hermes VM. * * This should be set on project.ext.react and that value will be read here. If it is not set * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode * and the benefits of using Hermes will therefore be sharply reduced. */ def enableHermes = project.ext.react.get("enableHermes", false); /** * Architectures to build native code for. */ def reactNativeArchitectures() { def value = project.getProperties().get("reactNativeArchitectures") return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"] } android { ndkVersion rootProject.ext.ndkVersion compileSdkVersion rootProject.ext.compileSdkVersion defaultConfig { applicationId "com.example.demo" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 versionName "1.0" buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() if (isNewArchitectureEnabled()) { // We configure the NDK build only if you decide to opt-in for the New Architecture. externalNativeBuild { ndkBuild { arguments "APP_PLATFORM=android-21", "APP_STL=c++_shared", "NDK_TOOLCHAIN_VERSION=clang", "GENERATED_SRC_DIR=$buildDir/generated/source", "PROJECT_BUILD_DIR=$buildDir", "REACT_ANDROID_DIR=$rootDir/../node_modules/react-native/ReactAndroid", "REACT_ANDROID_BUILD_DIR=$rootDir/../node_modules/react-native/ReactAndroid/build" cFlags "-Wall", "-Werror", "-fexceptions", "-frtti", "-DWITH_INSPECTOR=1" cppFlags "-std=c++17" // Make sure this target name is the same you specify inside the // src/main/jni/Android.mk file for the `LOCAL_MODULE` variable. targets "member_life_appmodules" // Fix for windows limit on number of character in file paths and in command lines if (Os.isFamily(Os.FAMILY_WINDOWS)) { arguments "NDK_APP_SHORT_COMMANDS=true" } } } if (!enableSeparateBuildPerCPUArchitecture) { ndk { abiFilters (*reactNativeArchitectures()) } } } } if (isNewArchitectureEnabled()) { // We configure the NDK build only if you decide to opt-in for the New Architecture. externalNativeBuild { ndkBuild { path "$projectDir/src/main/jni/Android.mk" } } def reactAndroidProjectDir = project(':ReactAndroid').projectDir def packageReactNdkDebugLibs = tasks.register("packageReactNdkDebugLibs", Copy) { dependsOn(":ReactAndroid:packageReactNdkDebugLibsForBuck") from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib") into("$buildDir/react-ndk/exported") } def packageReactNdkReleaseLibs = tasks.register("packageReactNdkReleaseLibs", Copy) { dependsOn(":ReactAndroid:packageReactNdkReleaseLibsForBuck") from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib") into("$buildDir/react-ndk/exported") } afterEvaluate { // If you wish to add a custom TurboModule or component locally, // you should uncomment this line. // preBuild.dependsOn("generateCodegenArtifactsFromSchema") preDebugBuild.dependsOn(packageReactNdkDebugLibs) preReleaseBuild.dependsOn(packageReactNdkReleaseLibs) // Due to a bug inside AGP, we have to explicitly set a dependency // between configureNdkBuild* tasks and the preBuild tasks. // This can be removed once this is solved: https://issuetracker.google.com/issues/207403732 configureNdkBuildRelease.dependsOn(preReleaseBuild) configureNdkBuildDebug.dependsOn(preDebugBuild) reactNativeArchitectures().each { architecture -> tasks.findByName("configureNdkBuildDebug[${architecture}]")?.configure { dependsOn("preDebugBuild") } tasks.findByName("configureNdkBuildRelease[${architecture}]")?.configure { dependsOn("preReleaseBuild") } } } } splits { abi { reset() enable enableSeparateBuildPerCPUArchitecture universalApk false // If true, also generate a universal APK include (*reactNativeArchitectures()) } } signingConfigs { debug { storeFile file('debug.keystore') storePassword 'android' keyAlias 'androiddebugkey' keyPassword 'android' } release { if (project.hasProperty('MYAPP_UPLOAD_STORE_FILE')) { storeFile file(MYAPP_UPLOAD_STORE_FILE) storePassword MYAPP_UPLOAD_STORE_PASSWORD keyAlias MYAPP_UPLOAD_KEY_ALIAS keyPassword MYAPP_UPLOAD_KEY_PASSWORD } } } buildTypes { debug { signingConfig signingConfigs.debug } release { // Caution! In production, you need to generate your own keystore file. // see https://reactnative.dev/docs/signed-apk-android. signingConfig signingConfigs.release minifyEnabled enableProguardInReleaseBuilds proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" } } flavorDimensions "defaultDimension" productFlavors { stage { applicationId "com.example.stage" versionName "0.7.3" } live { applicationId "com.example.live" versionName "0.7.4" } test { applicationId "com.example.test" versionName "0.9" } } // applicationVariants are e.g. debug, release applicationVariants.all { variant -> variant.outputs.each { output -> // For each separate APK per architecture, set a unique version code as described here: // https://developer.android.com/studio/build/configure-apk-splits.html // Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc. def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4] def abi = output.getFilter(OutputFile.ABI) if (abi != null) { // null for the universal-debug, universal-release variants output.versionCodeOverride = defaultConfig.versionCode * 1000 + versionCodes.get(abi) } } } } dependencies { implementation fileTree(dir: "libs", include: ["*.jar"]) //noinspection GradleDynamicVersion implementation "com.facebook.react:react-native:+" // From node_modules implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" implementation project(':react-native-splash-screen') debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") { exclude group:'com.facebook.fbjni' } debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") { exclude group:'com.facebook.flipper' exclude group:'com.squareup.okhttp3', module:'okhttp' } debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") { exclude group:'com.facebook.flipper' } if (enableHermes) { def hermesPath = "../../node_modules/hermes-engine/android/"; debugImplementation files(hermesPath + "hermes-debug.aar") releaseImplementation files(hermesPath + "hermes-release.aar") } else { implementation jscFlavor } } if (isNewArchitectureEnabled()) { // If new architecture is enabled, we let you build RN from source // Otherwise we fallback to a prebuilt .aar bundled in the NPM package. // This will be applied to all the imported transtitive dependency. configurations.all { resolutionStrategy.dependencySubstitution { substitute(module("com.facebook.react:react-native")) .using(project(":ReactAndroid")).because("On New Architecture we're building React Native from source") } } } // Run this once to be able to run the application with BUCK // puts all compile dependencies into folder libs for BUCK to use task copyDownloadableDepsToLibs(type: Copy) { from configurations.implementation into 'libs' } apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) def isNewArchitectureEnabled() { // To opt-in for the New Architecture, you can either: // - Set `newArchEnabled` to true inside the `gradle.properties` file // - Invoke gradle with `-newArchEnabled=true` // - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true` return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true" } ``` Here the above file is an app-level build.gradle file with build flavor configuration. For basic build configuration add the following lines in app-level build.gradle file android block. ``` flavorDimensions "defaultDimension" productFlavors { stage { applicationId "com.example.stage" versionName "0.7.3" } live { applicationId "com.example.live" versionName "0.7.4" } test { applicationId "com.example.test" versionName "0.9" } amayadance { applicationId "com.example.amayadance" versionName "0.9" } } ``` By adding the above code you will add three product flavors stage, live, and test. you can give any application id and versionName of your preference. You can also add config for that particular flavor like the SDK version. For providing different resources you need to create directories with the name of flavor name at the path android/app/src. Inside this, you can add a res directory same as present in the main directory to provide different resources for different flavors of build. Here, I have attached an example picture of the directory structure. Where amayadance is a flavor name like live, stage, and test. ![](https://hackmd.io/_uploads/rk_n5prDn.png)