Allow developers to load JavaScript bundle from any source.

Summary: This patch adds two pieces of functionality:

- Exposes `JSBundleLoader` to allow a developer to load JavaScript bundles as they choose.
- Adds `ReactBridge.loadScripFromFile` method which loads a JavaScript bundle from an arbitrary file path.

Example usage:

```
JSBundleLoader jsBundleLoader = new JSBundleLoader() {
    Override
    public void loadScript(ReactBridge reactBridge) {
        reactBridge.loadScriptFromFile("/sdcard/Download/index.android.bundle");
    }
};

mReactInstanceManager = ReactInstanceManager.builder()
        .setApplication(getApplication())
        .setJSBundleLoader(jsBundleLoader)
        .setJSMainModuleName("") /* necessary due to TODO(6803830) */
        .addPackage(new MainReactPackage())
        .setInitialLifecycleState(LifecycleState.RESUMED)
        .build();
```

cc ide
Closes https://github.com/facebook/react-native/pull/3189

Reviewed By: svcscm

Differential Revision: D2535819

Pulled By: mkonicek

fb-gh-sync-id: f319299dbe29bab3b7e91f94249c14b270d9fec3
This commit is contained in:
Matthew Arbesfeld
2015-10-28 04:56:26 -07:00
committed by facebook-github-bot-7
parent b59f250214
commit 3a743ef228
5 changed files with 65 additions and 45 deletions

View File

@@ -77,7 +77,7 @@ public class ReactInstanceManager {
private @Nullable ReactContextInitParams mPendingReactContextInitParams;
/* accessed from any thread */
private final @Nullable String mBundleAssetName; /* name of JS bundle file in assets folder */
private @Nullable String mJSBundleFile; /* path to JS bundle on file system */
private final @Nullable String mJSMainModuleName; /* path to JS bundle root on packager server */
private final List<ReactPackage> mPackages;
private final DevSupportManager mDevSupportManager;
@@ -176,7 +176,7 @@ public class ReactInstanceManager {
private ReactInstanceManager(
Context applicationContext,
@Nullable String bundleAssetName,
@Nullable String jsBundleFile,
@Nullable String jsMainModuleName,
List<ReactPackage> packages,
boolean useDeveloperSupport,
@@ -185,7 +185,7 @@ public class ReactInstanceManager {
initializeSoLoaderIfNecessary(applicationContext);
mApplicationContext = applicationContext;
mBundleAssetName = bundleAssetName;
mJSBundleFile = jsBundleFile;
mJSMainModuleName = jsMainModuleName;
mPackages = packages;
mUseDeveloperSupport = useDeveloperSupport;
@@ -224,30 +224,29 @@ public class ReactInstanceManager {
SoLoader.init(applicationContext, /* native exopackage */ false);
}
public void setJSBundleFile(String jsBundleFile) {
mJSBundleFile = jsBundleFile;
}
/**
* Trigger react context initialization asynchronously in a background async task. This enables
* applications to pre-load the application JS, and execute global code before
* {@link ReactRootView} is available and measured.
*/
public void createReactContextInBackground() {
if (mUseDeveloperSupport) {
if (mUseDeveloperSupport && mJSMainModuleName != null) {
if (mDevSupportManager.hasUpToDateJSBundleInCache()) {
// If there is a up-to-date bundle downloaded from server, always use that
onJSBundleLoadedFromServer();
return;
} else if (mBundleAssetName == null ||
!mDevSupportManager.hasBundleInAssets(mBundleAssetName)) {
// Bundle not available in assets, fetch from the server
} else {
mDevSupportManager.handleReloadJS();
return;
}
return;
}
// Use JS file from assets
recreateReactContextInBackground(
new JSCJavaScriptExecutor(),
JSBundleLoader.createAssetLoader(
mApplicationContext.getAssets(),
mBundleAssetName));
JSBundleLoader.createFileLoader(mApplicationContext, mJSBundleFile));
}
/**
@@ -564,7 +563,7 @@ public class ReactInstanceManager {
private final List<ReactPackage> mPackages = new ArrayList<>();
private @Nullable String mBundleAssetName;
private @Nullable String mJSBundleFile;
private @Nullable String mJSMainModuleName;
private @Nullable NotThreadSafeBridgeIdleDebugListener mBridgeIdleDebugListener;
private @Nullable Application mApplication;
@@ -575,11 +574,21 @@ public class ReactInstanceManager {
}
/**
* Name of the JS budle file to be loaded from application's raw assets.
* Name of the JS bundle file to be loaded from application's raw assets.
*
* Example: {@code "index.android.js"}
*/
public Builder setBundleAssetName(String bundleAssetName) {
mBundleAssetName = bundleAssetName;
return this.setJSBundleFile("assets://" + bundleAssetName);
}
/**
* Path to the JS bundle file to be loaded from the file system.
*
* Example: {@code "assets://index.android.js" or "/sdcard/main.jsbundle}
*/
public Builder setJSBundleFile(String jsBundleFile) {
mJSBundleFile = jsBundleFile;
return this;
}
@@ -639,21 +648,23 @@ public class ReactInstanceManager {
* Before calling {@code build}, the following must be called:
* <ul>
* <li> {@link #setApplication}
* <li> {@link #setBundleAssetName} or {@link #setJSMainModuleName}
* <li> {@link #setJSBundleFile} or {@link #setJSMainModuleName}
* </ul>
*/
public ReactInstanceManager build() {
Assertions.assertCondition(
mUseDeveloperSupport || mBundleAssetName != null,
"JS Bundle has to be provided in app assets when dev support is disabled");
mUseDeveloperSupport || mJSBundleFile != null,
"JS Bundle File has to be provided when dev support is disabled");
Assertions.assertCondition(
mBundleAssetName != null || mJSMainModuleName != null,
"Either BundleAssetName or MainModuleName needs to be provided");
mJSMainModuleName != null || mJSBundleFile != null,
"Either MainModuleName or JS Bundle File needs to be provided");
return new ReactInstanceManager(
Assertions.assertNotNull(
mApplication,
"Application property has not been set with this builder"),
mBundleAssetName,
mJSBundleFile,
mJSMainModuleName,
mPackages,
mUseDeveloperSupport,

View File

@@ -9,7 +9,7 @@
package com.facebook.react.bridge;
import android.content.res.AssetManager;
import android.content.Context;
/**
* A class that stores JS bundle information and allows {@link CatalystInstance} to load a correct
@@ -22,18 +22,22 @@ public abstract class JSBundleLoader {
* should be used. JS bundle will be read from assets directory in native code to save on passing
* large strings from java to native memory.
*/
public static JSBundleLoader createAssetLoader(
final AssetManager assetManager,
final String assetFileName) {
public static JSBundleLoader createFileLoader(
final Context context,
final String fileName) {
return new JSBundleLoader() {
@Override
public void loadScript(ReactBridge bridge) {
bridge.loadScriptFromAssets(assetManager, assetFileName);
if (fileName.startsWith("assets://")) {
bridge.loadScriptFromAssets(context.getAssets(), fileName.replaceFirst("assets://", ""));
} else {
bridge.loadScriptFromFile(fileName, fileName);
}
}
@Override
public String getSourceUrl() {
return "file:///android_asset/" + assetFileName;
return fileName;
}
};
}
@@ -51,7 +55,7 @@ public abstract class JSBundleLoader {
return new JSBundleLoader() {
@Override
public void loadScript(ReactBridge bridge) {
bridge.loadScriptFromNetworkCached(sourceURL, cachedFileLocation);
bridge.loadScriptFromFile(cachedFileLocation, sourceURL);
}
@Override
@@ -70,7 +74,7 @@ public abstract class JSBundleLoader {
return new JSBundleLoader() {
@Override
public void loadScript(ReactBridge bridge) {
bridge.loadScriptFromNetworkCached(sourceURL, null);
bridge.loadScriptFromFile(null, sourceURL);
}
@Override

View File

@@ -65,7 +65,7 @@ public class ReactBridge extends Countable {
* All native functions are not thread safe and appropriate queues should be used
*/
public native void loadScriptFromAssets(AssetManager assetManager, String assetName);
public native void loadScriptFromNetworkCached(String sourceURL, @Nullable String tempFileName);
public native void loadScriptFromFile(@Nullable String fileName, @Nullable String sourceURL);
public native void callFunction(int moduleId, int methodId, NativeArray arguments);
public native void invokeCallback(int callbackID, NativeArray arguments);
public native void setGlobalVariable(String propertyName, String jsonEncodedArgument);

View File

@@ -367,10 +367,18 @@ public class DevSupportManager implements NativeModuleCallExceptionHandler {
}
public String getSourceMapUrl() {
if (mJSAppBundleName == null) {
return "";
}
return mDevServerHelper.getSourceMapUrl(Assertions.assertNotNull(mJSAppBundleName));
}
public String getSourceUrl() {
if (mJSAppBundleName == null) {
return "";
}
return mDevServerHelper.getSourceUrl(Assertions.assertNotNull(mJSAppBundleName));
}