How to use offline bundle on android for react native project?

How to use offline bundle on android?
I didn`t see the document about use offline bundle on android.
I tried to uncomment the code in build.gradle.

project.ext.react = [
        // the name of the generated asset file containing your JS bundle
    bundleAssetName: "index.android.bundle",

    // the entry file for bundle generation
    entryFile: "index.android.js",

    // whether to bundle JS and assets in debug mode
    bundleInDebug: false,

    // whether to bundle JS and assets in release mode
    bundleInRelease: true,

    // 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/**"]
]

but it didn`t work.It still fetch the JS bundle from the server.
Is there something I missed?


Solution 1:

For offline bunduling of JS into android, first start the server in respective project path:

  1. when server start, open next terminal with same path as project path
  2. copy and paste this command: Before you copy and paste command in command propmt, make assets folder in project respective path
    as:
    android/app/src/main/assets

    paste this command in command prompt and run:

react-native bundle --platform android --dev false --entry-file index.android.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res/
  1. Then in assets folder there will appear file as index.android.bundle
  2. Finally, run command: react-native run-android(while building new offline apk you need not to start server, your offline js file will help you to build apk file.)
  3. Final, apk now build is ready for running in different devices(run apk from app/src/build/debug.apk).
  4. Sometimes newly made apk will run without showing images, If application runs without image , then copy and paste the specific image resource folder into android/app/src/main/assets/(image source folder)
  5. Again rerun application and thus build apk is ready for running.

Solution 2:

You can read the source code of react.gradle,then you will know how to do the offline bundle.

In react.gradle, it creates offline bundle gradle task for each build variant, and make it execute before gradle process resources, but it enables the bundle task in some special conditions, the code is:

enabled config."bundleIn${targetName}" ||
        config."bundleIn${buildTypeName.capitalize()}" ?:
        targetName.toLowerCase().contains("release")

while the config object is the react object actually as its definition is def config = project.hasProperty("react") ? project.react : [];

and the targetName definition is def targetName = "${productFlavorName.capitalize()}${buildTypeName.capitalize()}"

So if we define a proper react property in app\build.gradle, we can enable the offline bundle task.

For example, if we want to enable the offline bundle in release build type but not in debug build type ,we can define the react as:

ext.react = [
    bundleInDebug     : false,
    bundleInRelease   : true
]

then execute gradle assembleRelease in command line, gradle will execute the bundle task and the js bundle will be included in final apk.

Now in your question, you have defined the proper react object, but you don't mention which build type you have ran. Only execute gradle assembleRelease can do the bundle job, maybe you executed gradle assembleDebug so it does not make any sense.

And there is still another problem I need to say(Also see in issue7258). If you enabled the bundle task for release build type and run app from android studio, gradle don't execute bundleReleaseJsAndAssets, because android studio enable the feature Configure on demand (it's in File | Settings | Build, Execution, Deployment | Compiler) by default for speeding up build, and in react.gradle the bundle task is add to project when all project are configured(or called evaluated) as the main code is in gradle.projectsEvaluated{} block.

Configuration on demand mode attempts to configure only projects that are relevant for requested tasks, i.e. it only executes the build.gradle file of projects that are participating in the build.

For some unclear reasons, any task define in gradle.projectsEvaluated{} block doesn't executed when enable the Configure on demand feature. To make it executable, disable the Configure on demand feature in android studio settings, or change gradle.projectsEvaluated{} to afterEvaluate{}.

If you run gradle assembleRelease in command line tool, everything is ok because Configure on demand is disabled by default.

Solution 3:

You do not have to uncomment anything in your gradle.build file. It is there for reference and you can overwrite any of the values which are there as defaults if you need to.

The problem is that Android Studio is not running the task that is responsible for generating the bundle.

To fix that in Android Studio go to Preferences > Build, Execution, Deployment > Build Tools > Compiler and uncheck "Configure on demand".

Now, any variant which you mark as requiring bundle, will have the bundle generated automatically.

By default, debug variant does not get the bundle generated: bundleInDebug : false

You can change that like this. Here I also provided example how to change the default location of the entry point and default name of the bundle.

project.ext.react = [
    bundleAssetName: "index.myapp.bundle",
    entryFile: "js/index.myapp.js",
    bundleInDebug: true
]

Once you make these adjustments, you may still tun into issues building because Studio does not recognise paths from your shell profiles, so i may not find the path to node.

I posted a solution to that issue here

This is a stable and robust solution. You do not have to do anything manually once you make these changes in your env. Bundle will be built automatically as you press the buttons in your IDE, granted you select proper build variants (that is also in IDE on the LHS bar of the Studio window towards the bottom).

Solution 4:

When building an unsigned apk from react native project through create-react-native-app which does not have index.android.js and index.ios.js .so first of all you have to check that wheter index.js file is there in your project .if it is not available then create a new file index.js and write this code there

import { AppRegistry } from "react-native";
import App from "./App";
AppRegistry.registerComponent("VOS", () => App);

after this eject the project and then run this command

  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/

and then react-native run-android so it will build an apk for you.