Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,11 @@ private void recreateReactContextInBackgroundInner() {
// If remote JS debugging is enabled, load from dev server.
if (mDevSupportManager.hasUpToDateJSBundleInCache() &&
!devSettings.isRemoteJSDebugEnabled()) {
// Read dev bundle container from file
if (mDevBundlesContainer == null) {
mDevBundlesContainer = mDevSupportManager.getBundlesContainerFromCache();
mSourceURL = mDevBundlesContainer.getInitialSourceURL();
}
// If there is a up-to-date bundle downloaded from server,
// with remote JS debugging disabled, always use that.
onJSBundleLoadedFromServer(mSourceURL, mDevBundlesContainer, null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nullable;

/**
Expand Down Expand Up @@ -105,9 +107,14 @@ public ReactInstanceManagerBuilder setJSBundleFile(String jsBundleFile) {
mJSBundleLoader = null;
return this;
}
DevBundlesContainer bundlesContainer = new DevBundlesContainer();
// TODO use regex to get bundleName
bundlesContainer.pushBundle("index", jsBundleFile, jsBundleFile);
DevBundlesContainer bundlesContainer = new DevBundlesContainer(jsBundleFile);
Pattern bundleNamePattern = Pattern.compile("/([^./]+)[^/]+$");
Matcher bundleNameMatcher = bundleNamePattern.matcher(jsBundleFile);
String bundleName = "index"; // fallback
if (bundleNameMatcher.find()) {
bundleName = bundleNameMatcher.group(1);
}
bundlesContainer.pushBundle(bundleName, jsBundleFile, jsBundleFile);
return setJSBundleLoader(JSBundleLoader.createFileLoader(jsBundleFile, bundlesContainer));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import okio.BufferedSource;
import okio.Okio;
import okio.Sink;
import okio.Source;
import org.json.JSONException;
import org.json.JSONObject;

Expand Down Expand Up @@ -108,22 +109,24 @@ public BundleDownloader(OkHttpClient client) {
public void downloadBundleFromURL(
final DevBundleDownloadListener callback,
final File outputFile,
final File bundlesContainerFile,
final String bundleURL,
final @Nullable BundleInfo bundleInfo,
final BundleDeltaClient.ClientType clientType) {
downloadBundleFromURL(
callback, outputFile, bundleURL, bundleInfo, clientType, new Request.Builder());
callback, outputFile, bundlesContainerFile, bundleURL, bundleInfo, clientType, new Request.Builder());
}

public void downloadBundleFromURL(
final DevBundleDownloadListener callback,
final File outputFile,
final File bundlesContainerFile,
final String bundleURL,
final @Nullable BundleInfo bundleInfo,
final BundleDeltaClient.ClientType clientType,
Request.Builder requestBuilder) {

mDevBundlesContainer = new DevBundlesContainer();
mDevBundlesContainer = new DevBundlesContainer(bundleURL);
final Request request =
requestBuilder
.url(formatBundleUrl(bundleURL, clientType))
Expand Down Expand Up @@ -170,7 +173,7 @@ public void onResponse(Call call, final Response response) throws IOException {
try (Response r = response) {
if (match.find()) {
processMultipartResponse(
url, r, match.group(1), outputFile, bundleInfo, clientType, callback);
url, r, match.group(1), outputFile, bundlesContainerFile, bundleInfo, clientType, callback);
} else {
// In case the server doesn't support multipart/mixed responses, fallback to normal
// download.
Expand All @@ -180,6 +183,7 @@ public void onResponse(Call call, final Response response) throws IOException {
r.headers(),
Okio.buffer(r.body().source()),
outputFile,
bundlesContainerFile,
bundleInfo,
clientType,
callback);
Expand All @@ -200,6 +204,7 @@ private void processMultipartResponse(
final Response response,
String boundary,
final File outputFile,
final File bundlesContainerFile,
@Nullable final BundleInfo bundleInfo,
final BundleDeltaClient.ClientType clientType,
final DevBundleDownloadListener callback)
Expand All @@ -225,7 +230,7 @@ public void onChunkComplete(
status = Integer.parseInt(headers.get("X-Http-Status"));
}
processBundleResult(
url, status, Headers.of(headers), body, outputFile, bundleInfo, clientType, callback);
url, status, Headers.of(headers), body, outputFile, bundlesContainerFile, bundleInfo, clientType, callback);
} else {
if (!headers.containsKey("Content-Type")
|| !headers.get("Content-Type").equals("application/json")) {
Expand Down Expand Up @@ -280,6 +285,7 @@ private void processBundleResult(
Headers headers,
BufferedSource body,
File outputFile,
File bundlesContainerFile,
BundleInfo bundleInfo,
BundleDeltaClient.ClientType clientType,
DevBundleDownloadListener callback)
Expand Down Expand Up @@ -323,8 +329,20 @@ private void processBundleResult(
}

if (bundleWritten) {
// TODO use regexp to get name
mDevBundlesContainer.pushBundle("index", url, outputFile.getPath());
Pattern bundleNamePattern = Pattern.compile("^https?://[^/]+/([^.]+)");
Matcher bundleNameMatcher = bundleNamePattern.matcher(url);
String bundleName = "index"; // fallback
if (bundleNameMatcher.find()) {
bundleName = bundleNameMatcher.group(1);
}
mDevBundlesContainer.pushBundle(bundleName, url, outputFile.getPath());
// We need to save the mDevBundlesContainer in file because it is null
// after killing app. App doesn't request for new bundles if has up to date bundle in cache.
// In that case we need to read bundles container from file.

// TODO this code should be moved to place where all bundles are downloaded (multibundle support).
JSONObject bundlesContainerJSON = mDevBundlesContainer.toJSON();
storeBundlesContainerInFile(bundlesContainerJSON, bundlesContainerFile);
// If we have received a new bundle from the server, move it to its final destination.
if (!tmpFile.renameTo(outputFile)) {
throw new IOException("Couldn't rename " + tmpFile + " to " + outputFile);
Expand Down Expand Up @@ -356,6 +374,27 @@ private static boolean storePlainJSInFile(BufferedSource body, File outputFile)
return true;
}

private boolean storeBundlesContainerInFile(JSONObject body, File outputFile)
throws IOException {
Sink fileSink = null;
try {
String jsonString = body.toString();
fileSink = Okio.sink(outputFile);
Buffer buffer = new Buffer();
buffer.writeUtf8(jsonString);
BufferedSource source = Okio.buffer((Source) buffer);
source.readAll(fileSink);
} catch(Throwable e) {
throw new IOException("Couldn't save to " + outputFile);
} finally {
if (fileSink != null) {
fileSink.close();
}
}

return true;
}

private static void populateBundleInfo(String url, Headers headers, BundleDeltaClient.ClientType clientType, BundleInfo bundleInfo) {
bundleInfo.mDeltaClientName = clientType == BundleDeltaClient.ClientType.NONE ? null : clientType.name();
bundleInfo.mUrl = url;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,63 @@

package com.facebook.react.devsupport;

import com.facebook.common.logging.FLog;
import com.facebook.react.common.ReactConstants;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import org.json.JSONException;
import org.json.JSONObject;

public class DevBundlesContainer {

private String SOURCE_URL_KEY = "sourceURL";
private String FILE_URL_KEY = "fileURL";
private String INITIAL_SOURCE_URL_KEY = "initialSourceURL";
private String BUNDLES_NAME_MAPPING_KEY = "bundleNameMapping";

private String initialSourceURL;
private Map<String, BundleURLs> bundleNameMapping = new HashMap<>();

private class BundleURLs {
public BundleURLs(String sourceURL, String fileURL) {
BundleURLs(String sourceURL, String fileURL) {
this.fileURL = fileURL;
this.sourceURL = sourceURL;
}
public String sourceURL;
public String fileURL;

BundleURLs(JSONObject json) {
try {
this.sourceURL = json.getString(SOURCE_URL_KEY);
this.fileURL = json.getString(FILE_URL_KEY);
} catch (Throwable e) {
FLog.e(ReactConstants.TAG, "BundleURLs is unable to create from JSON");
}
}

String sourceURL;
String fileURL;

JSONObject toJSON() {
JSONObject json = new JSONObject();
try {
json.put(FILE_URL_KEY, fileURL);
json.put(SOURCE_URL_KEY, sourceURL);
} catch (JSONException e) {
FLog.e(ReactConstants.TAG, "BundleURLs is unable to be parsed to JSON");
}
return json;
}
}

private Map<String, BundleURLs> bundleNameMapping = new HashMap<>();
public DevBundlesContainer(String initialSourceURL) {
this.initialSourceURL = initialSourceURL;
}

public String getInitialSourceURL() {
return this.initialSourceURL;
}

public void pushBundle(String name, String sourceURL, String fileURL) {
bundleNameMapping.put(name, new BundleURLs(sourceURL, fileURL));
Expand Down Expand Up @@ -65,4 +108,32 @@ public String getFileURLBySourceURL(String sourceURL) {
}
return "";
}

public JSONObject toJSON() {
JSONObject jsonContainer = new JSONObject();
JSONObject jsonNameMapping = new JSONObject();
try {
jsonContainer.put(INITIAL_SOURCE_URL_KEY, initialSourceURL);
for (String key : bundleNameMapping.keySet()) {
jsonNameMapping.put(key, Objects.requireNonNull(bundleNameMapping.get(key)).toJSON());
}
jsonContainer.put(BUNDLES_NAME_MAPPING_KEY, jsonNameMapping);
} catch (JSONException e) {
FLog.e(ReactConstants.TAG, "DevBundlesContainer is unable to be parsed to JSON");
}
return jsonContainer;
}

public DevBundlesContainer(JSONObject json) {
try {
this.initialSourceURL = json.getString(INITIAL_SOURCE_URL_KEY);
JSONObject bundles = json.getJSONObject(BUNDLES_NAME_MAPPING_KEY);
for(int i = 0; i<bundles.names().length(); i++){
String key = bundles.names().getString(i);
bundleNameMapping.put(key, new BundleURLs(bundles.getJSONObject(key)));
}
} catch (Throwable e) {
FLog.e(ReactConstants.TAG, "DevBundlesContainer is unable to create from JSON");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -379,18 +379,19 @@ private String getInspectorAttachUrl(String title) {

public void downloadBundleFromURL(
DevBundleDownloadListener callback,
File outputFile, String bundleURL, BundleDownloader.BundleInfo bundleInfo) {
mBundleDownloader.downloadBundleFromURL(callback, outputFile, bundleURL, bundleInfo, getDeltaClientType());
File outputFile, File bundlesContainerFile, String bundleURL, BundleDownloader.BundleInfo bundleInfo) {
mBundleDownloader.downloadBundleFromURL(callback, outputFile, bundlesContainerFile, bundleURL, bundleInfo, getDeltaClientType());
}

public void downloadBundleFromURL(
DevBundleDownloadListener callback,
File outputFile,
File bundlesContainerFile,
String bundleURL,
BundleDownloader.BundleInfo bundleInfo,
Request.Builder requestBuilder) {
mBundleDownloader.downloadBundleFromURL(
callback, outputFile, bundleURL, bundleInfo, getDeltaClientType(), requestBuilder);
callback, outputFile, bundlesContainerFile, bundleURL, bundleInfo, getDeltaClientType(), requestBuilder);
}

private BundleDeltaClient.ClientType getDeltaClientType() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@
import com.facebook.react.modules.debug.interfaces.DeveloperSettings;
import com.facebook.react.packagerconnection.RequestHandler;
import com.facebook.react.packagerconnection.Responder;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
Expand All @@ -67,6 +71,9 @@
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.internal.io.FileSystem;
import okio.BufferedSource;
import okio.Okio;

/**
* Interface for accessing and interacting with development features. Following features
Expand Down Expand Up @@ -102,6 +109,7 @@ public class DevSupportManagerImpl implements
private static final int JAVA_ERROR_COOKIE = -1;
private static final int JSEXCEPTION_ERROR_COOKIE = -1;
private static final String JS_BUNDLE_FILE_NAME = "ReactNativeDevBundle.js";
private static final String JS_BUNDLE_CONTAINER_FILE_NAME = "ReactNativeDevBundlesContainer.json";
private static final String RELOAD_APP_ACTION_SUFFIX = ".RELOAD_APP_ACTION";

private enum ErrorType {
Expand All @@ -126,6 +134,7 @@ private enum ErrorType {
private final ReactInstanceManagerDevHelper mReactInstanceManagerHelper;
private final @Nullable String mJSAppBundleName;
private final File mJSBundleTempFile;
private final File mDevBundleContainerTempFile;
private final DefaultNativeModuleCallExceptionHandler mDefaultNativeModuleCallExceptionHandler;
private final DevLoadingViewController mDevLoadingViewController;

Expand Down Expand Up @@ -259,6 +268,8 @@ public void onReceive(Context context, Intent intent) {
// file. As this should only be the case in dev mode we leave it as it is.
// TODO(6418010): Fix readers-writers problem in debug reload from HTTP server
mJSBundleTempFile = new File(applicationContext.getFilesDir(), JS_BUNDLE_FILE_NAME);
mDevBundleContainerTempFile = new File(applicationContext.getFilesDir(), JS_BUNDLE_CONTAINER_FILE_NAME);


mDefaultNativeModuleCallExceptionHandler = new DefaultNativeModuleCallExceptionHandler();

Expand Down Expand Up @@ -687,6 +698,21 @@ public boolean hasUpToDateJSBundleInCache() {
return false;
}

@Override
public DevBundlesContainer getBundlesContainerFromCache() {
if (mDevBundleContainerTempFile.exists()) {
try {
final BufferedSource source = Okio.buffer(FileSystem.SYSTEM.source(mDevBundleContainerTempFile));
return new DevBundlesContainer(new JSONObject(source.readUtf8()));
} catch (IOException e) {
FLog.e(ReactConstants.TAG, "DevSupport is unable to read bundles container");
} catch (JSONException e) {
FLog.e(ReactConstants.TAG, "DevSupport is unable to parse bundles container");
}
}
return null;
}

/**
* @return {@code true} if JS bundle {@param bundleAssetName} exists, in that case
* {@link com.facebook.react.ReactInstanceManager} should use that file from assets instead of
Expand Down Expand Up @@ -1019,6 +1045,7 @@ public void run() {
}
},
mJSBundleTempFile,
mDevBundleContainerTempFile,
bundleURL,
bundleInfo);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ public boolean hasUpToDateJSBundleInCache() {
return false;
}

@Override
public DevBundlesContainer getBundlesContainerFromCache() {
return null;
}

@Override
public void reloadSettings() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import com.facebook.react.bridge.NativeModuleCallExceptionHandler;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.devsupport.DevBundlesContainer;
import com.facebook.react.modules.debug.interfaces.DeveloperSettings;

/**
Expand All @@ -40,6 +41,7 @@ public interface DevSupportManager extends NativeModuleCallExceptionHandler {
String getSourceUrl();
String getJSBundleURLForRemoteDebugging();
String getDownloadedJSBundleFile();
DevBundlesContainer getBundlesContainerFromCache();
boolean hasUpToDateJSBundleInCache();
void reloadSettings();
void handleReloadJS();
Expand Down