React Native CodePush Android Release Build not loading JS files

2.3k views Asked by At

I've done my best to follow the instructions here and here. The Android debug build (react-native run-android) runs on Android emulator and on my device as expected i.e., it reloads the JS files after each new installation or update. However, when I install the release build (react-native run-android --variant=release), it displays the first screen of the app and none of the app functionality works. I've not tried installing on iOS. It seems like it's not loading the JS files from codepush. When I check the log files (while installing the release build on the emulator), it hangs on this line:

[CodePush] Loading JS bundle from "assets://index.android.bundle"

I find it surprising that the release build is trying to load the JS bundle locally instead of checking CodePush remote server. My React-native and react-native-code-push versions are 0.45.1 and 3.0.1-beta. I've deployed my code to the staging and production codepush servers and have verified it's there by running

code-push deployment ls onetext-Android -k

I've also configured my keys appropriately. After doing this more than 10 times, there was one moment where the log file for the release installation actually showed:

[CodePush] Loading JS bundle from "/data/user/0/com.onetext/files/CodePush/f93a24d467d53.../CodePush/index.android.bundle"

and the app prompted me to install latest updates. However it crashed as soon as the installation finished. Since that one instant, I've not been able to get it to load the files from codepush servers. Any tips on where to look or how to debug this would be much appreciated. My app was built with native-starter-kit as the starting point. Here's my settings.gradle file:

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

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

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

include ':app'

Relevant parts of my build.gradle file:

apply from: "../../node_modules/react-native/react.gradle"
apply from: "../../node_modules/react-native-code-push/android/codepush.gradle"

def enableSeparateBuildPerCPUArchitecture = false

def enableProguardInReleaseBuilds = false

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.2"

    defaultConfig {
        applicationId "com.onetext"
        minSdkVersion 16
        targetSdkVersion 22
        versionCode 4
        versionName "1.0.1"
        ndk {
            abiFilters "armeabi-v7a", "x86"
        }
         manifestPlaceholders = [onesignal_app_id: "xxx",
                                         onesignal_google_project_number: "xxx"]
    }
    signingConfigs {
        release {
            if (project.hasProperty('MYAPP_RELEASE_STORE_FILE')) {
                storeFile file(MYAPP_RELEASE_STORE_FILE)
                storePassword MYAPP_RELEASE_STORE_PASSWORD
                keyAlias MYAPP_RELEASE_KEY_ALIAS
                keyPassword MYAPP_RELEASE_KEY_PASSWORD
            }
        }
    }
    splits {
        abi {
            reset()
            enable enableSeparateBuildPerCPUArchitecture
            universalApk false  // If true, also generate a universal APK
            include "armeabi-v7a", "x86"
        }
    }
    buildTypes {
        debug {
            buildConfigField "String", "CODEPUSH_KEY", '""'
        }
        releaseStaging {
            buildConfigField "String", "CODEPUSH_KEY", '"H3ZFJ..."'
        }
        release {
            buildConfigField "String", "CODEPUSH_KEY", '"r0Sx..."'
            minifyEnabled enableProguardInReleaseBuilds
            proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
            signingConfig signingConfigs.release
        }
    }
    // 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:
            // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits
            def versionCodes = ["armeabi-v7a":1, "x86":2]
            def abi = output.getFilter(OutputFile.ABI)
            if (abi != null) {  // null for the universal-debug, universal-release variants
                output.versionCodeOverride =
                        versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
            }
        }
    }
}

dependencies {
    compile project(':react-native-onesignal')
    compile project(':react-native-image-picker')
    compile project(':react-native-code-push')
    compile fileTree(dir: "libs", include: ["*.jar"])
    compile "com.android.support:appcompat-v7:23.0.1"
    compile "com.facebook.react:react-native:+"  // From node_modules
}

// 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.compile
    into 'libs'
}

MainApplication.java:

package com.onetext;

import android.app.Application;

import com.facebook.react.ReactApplication;
import com.geektime.rnonesignalandroid.ReactNativeOneSignalPackage;
import com.microsoft.codepush.react.CodePush;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
import com.imagepicker.ImagePickerPackage;

import java.util.Arrays;
import java.util.List;

public class MainApplication extends Application implements ReactApplication {

  private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {

    @Override
    protected String getJSBundleFile() {
      return CodePush.getJSBundleFile();
    }

    @Override
    public boolean getUseDeveloperSupport() {
      return BuildConfig.DEBUG;
    }

    @Override
    protected List<ReactPackage> getPackages() {
      return Arrays.<ReactPackage>asList(
          new MainReactPackage(),
            new ReactNativeOneSignalPackage(),
            new CodePush(BuildConfig.CODEPUSH_KEY, MainApplication.this, BuildConfig.DEBUG), // Add/change this line.
            new ImagePickerPackage()
      );
    }
  };

  @Override
  public ReactNativeHost getReactNativeHost() {
    return mReactNativeHost;
  }

  @Override
  public void onCreate() {
    super.onCreate();
    SoLoader.init(this, /* native exopackage */ false);
  }
}
1

There are 1 answers

0
huntharo On

A non-debug app always needs to have a JS Bundle included, even when using CodePush. CodePush's sync / update / verify install functions are all called from JS not from Java or ObjC/Swift (the app is either using the codePush higher-order component to wrap the component registered with AppRegistry or the app is calling codePush.sync() function or lower level functions to perform the update check and install the update).

My assumption here is that you had not, at the time of writing this question, rebuilt your jsbundle in quite some time so the version being shown was not up-to-date and quite possibly did not include integration with CodePush at all and thus would not check for an update that was published to CodePush either.

In summary - Build your android jsbundle, reinstall the release build, and everything should work.