From 348da5eff25c1efd4adffd09fe1fec0190afec47 Mon Sep 17 00:00:00 2001 From: Dratwas Date: Tue, 11 Jun 2019 12:10:22 +0200 Subject: [PATCH 1/4] Save and read bundles container --- .../facebook/react/ReactInstanceManager.java | 5 ++ .../react/ReactInstanceManagerBuilder.java | 13 +++- .../react/devsupport/BundleDownloader.java | 51 +++++++++++-- .../react/devsupport/DevBundlesContainer.java | 76 ++++++++++++++++++- .../react/devsupport/DevServerHelper.java | 7 +- .../devsupport/DevSupportManagerImpl.java | 27 +++++++ .../devsupport/DisabledDevSupportManager.java | 5 ++ .../interfaces/DevSupportManager.java | 2 + 8 files changed, 170 insertions(+), 16 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java index acdc984758cc..1e36169f3b4e 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java @@ -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); diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java index bcd5d4fe16a8..f636f328a032 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java @@ -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; /** @@ -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)); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/BundleDownloader.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/BundleDownloader.java index 66dac828db4d..451185e043a5 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/BundleDownloader.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/BundleDownloader.java @@ -31,6 +31,7 @@ import okio.BufferedSource; import okio.Okio; import okio.Sink; +import okio.Source; import org.json.JSONException; import org.json.JSONObject; @@ -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)) @@ -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. @@ -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); @@ -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) @@ -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")) { @@ -280,6 +285,7 @@ private void processBundleResult( Headers headers, BufferedSource body, File outputFile, + File bundlesContainerFile, BundleInfo bundleInfo, BundleDeltaClient.ClientType clientType, DevBundleDownloadListener callback) @@ -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); @@ -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 aa) { + 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; diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevBundlesContainer.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevBundlesContainer.java index be01bb364ddc..a3ec9e6a1dea 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevBundlesContainer.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevBundlesContainer.java @@ -9,18 +9,58 @@ 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 = "sourceURL"; + private String INITIAL_SOURCE_URL_KEY = "initialSourceURL"; + private String BUNDLES_NAME_MAPPING_KEY = "bundleNameMapping"; + + private String initialSourceURL; + private Map 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) { + // TODO + } + } + + 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) { + // TODO + } + return json; + } } - private Map 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)); @@ -65,4 +105,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) { + // TODO + } + 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 Date: Tue, 11 Jun 2019 12:43:33 +0200 Subject: [PATCH 2/4] fix key --- .../java/com/facebook/react/devsupport/DevBundlesContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevBundlesContainer.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevBundlesContainer.java index a3ec9e6a1dea..ed9cebeda490 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevBundlesContainer.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevBundlesContainer.java @@ -17,7 +17,7 @@ public class DevBundlesContainer { private String SOURCE_URL_KEY = "sourceURL"; - private String FILE_URL_KEY = "sourceURL"; + private String FILE_URL_KEY = "fileURL"; private String INITIAL_SOURCE_URL_KEY = "initialSourceURL"; private String BUNDLES_NAME_MAPPING_KEY = "bundleNameMapping"; From 2b2bc564b237726efc7caa44a7d59351a1ac6743 Mon Sep 17 00:00:00 2001 From: Dratwas Date: Tue, 11 Jun 2019 13:00:56 +0200 Subject: [PATCH 3/4] Add error messages --- .../react/devsupport/DevBundlesContainer.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevBundlesContainer.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevBundlesContainer.java index ed9cebeda490..2520e72ef5de 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevBundlesContainer.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevBundlesContainer.java @@ -7,6 +7,9 @@ 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; @@ -35,7 +38,7 @@ private class BundleURLs { this.sourceURL = json.getString(SOURCE_URL_KEY); this.fileURL = json.getString(FILE_URL_KEY); } catch (Throwable e) { - // TODO + FLog.e(ReactConstants.TAG, "BundleURLs is unable to create from JSON"); } } @@ -48,7 +51,7 @@ JSONObject toJSON() { json.put(FILE_URL_KEY, fileURL); json.put(SOURCE_URL_KEY, sourceURL); } catch (JSONException e) { - // TODO + FLog.e(ReactConstants.TAG, "BundleURLs is unable to be parsed to JSON"); } return json; } @@ -116,7 +119,7 @@ public JSONObject toJSON() { } jsonContainer.put(BUNDLES_NAME_MAPPING_KEY, jsonNameMapping); } catch (JSONException e) { - // TODO + FLog.e(ReactConstants.TAG, "DevBundlesContainer is unable to be parsed to JSON"); } return jsonContainer; } @@ -130,7 +133,7 @@ public DevBundlesContainer(JSONObject json) { bundleNameMapping.put(key, new BundleURLs(bundles.getJSONObject(key))); } } catch (Throwable e) { - // TODO + FLog.e(ReactConstants.TAG, "DevBundlesContainer is unable to create from JSON"); } } } \ No newline at end of file From 585ee55e66b6704d3b3a28a6c2daa26ec43f439f Mon Sep 17 00:00:00 2001 From: Dratwas Date: Tue, 11 Jun 2019 13:05:07 +0200 Subject: [PATCH 4/4] fix name of error --- .../java/com/facebook/react/devsupport/BundleDownloader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/BundleDownloader.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/BundleDownloader.java index 451185e043a5..3651949a9bfc 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/BundleDownloader.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/BundleDownloader.java @@ -384,7 +384,7 @@ private boolean storeBundlesContainerInFile(JSONObject body, File outputFile) buffer.writeUtf8(jsonString); BufferedSource source = Okio.buffer((Source) buffer); source.readAll(fileSink); - } catch(Throwable aa) { + } catch(Throwable e) { throw new IOException("Couldn't save to " + outputFile); } finally { if (fileSink != null) {