Hook up native delta client
Summary: Adds support for native clients to `ReactAndroid`: - `.devsupport.BundleDeltaClient` is now abstract with two implementations: the existing Java client, and a native client - `BundleDeltaClient#processDelta(...)` can now return a native delta client object - if that client object is non-null, the bridge is started up with that client rather than a script written to disk Reviewed By: fromcelticpark Differential Revision: D7845135 fbshipit-source-id: 379a9c6f9319c62eec3c370cda9ffa0969266a29
This commit is contained in:
parent
8f85abdb14
commit
dd036c2328
|
@ -50,6 +50,7 @@ import com.facebook.react.bridge.JSIModulesProvider;
|
||||||
import com.facebook.react.bridge.JavaJSExecutor;
|
import com.facebook.react.bridge.JavaJSExecutor;
|
||||||
import com.facebook.react.bridge.JavaScriptExecutor;
|
import com.facebook.react.bridge.JavaScriptExecutor;
|
||||||
import com.facebook.react.bridge.JavaScriptExecutorFactory;
|
import com.facebook.react.bridge.JavaScriptExecutorFactory;
|
||||||
|
import com.facebook.react.bridge.NativeDeltaClient;
|
||||||
import com.facebook.react.bridge.NativeModuleCallExceptionHandler;
|
import com.facebook.react.bridge.NativeModuleCallExceptionHandler;
|
||||||
import com.facebook.react.bridge.NativeModuleRegistry;
|
import com.facebook.react.bridge.NativeModuleRegistry;
|
||||||
import com.facebook.react.bridge.NotThreadSafeBridgeIdleDebugListener;
|
import com.facebook.react.bridge.NotThreadSafeBridgeIdleDebugListener;
|
||||||
|
@ -84,6 +85,7 @@ import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper;
|
||||||
import com.facebook.soloader.SoLoader;
|
import com.facebook.soloader.SoLoader;
|
||||||
import com.facebook.systrace.Systrace;
|
import com.facebook.systrace.Systrace;
|
||||||
import com.facebook.systrace.SystraceMessage;
|
import com.facebook.systrace.SystraceMessage;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -270,8 +272,8 @@ public class ReactInstanceManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onJSBundleLoadedFromServer() {
|
public void onJSBundleLoadedFromServer(@Nullable NativeDeltaClient nativeDeltaClient) {
|
||||||
ReactInstanceManager.this.onJSBundleLoadedFromServer();
|
ReactInstanceManager.this.onJSBundleLoadedFromServer(nativeDeltaClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -360,7 +362,7 @@ public class ReactInstanceManager {
|
||||||
!devSettings.isRemoteJSDebugEnabled()) {
|
!devSettings.isRemoteJSDebugEnabled()) {
|
||||||
// If there is a up-to-date bundle downloaded from server,
|
// If there is a up-to-date bundle downloaded from server,
|
||||||
// with remote JS debugging disabled, always use that.
|
// with remote JS debugging disabled, always use that.
|
||||||
onJSBundleLoadedFromServer();
|
onJSBundleLoadedFromServer(null);
|
||||||
} else if (mBundleLoader == null) {
|
} else if (mBundleLoader == null) {
|
||||||
mDevSupportManager.handleReloadJS();
|
mDevSupportManager.handleReloadJS();
|
||||||
} else {
|
} else {
|
||||||
|
@ -848,12 +850,17 @@ public class ReactInstanceManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ThreadConfined(UI)
|
@ThreadConfined(UI)
|
||||||
private void onJSBundleLoadedFromServer() {
|
private void onJSBundleLoadedFromServer(@Nullable NativeDeltaClient nativeDeltaClient) {
|
||||||
Log.d(ReactConstants.TAG, "ReactInstanceManager.onJSBundleLoadedFromServer()");
|
Log.d(ReactConstants.TAG, "ReactInstanceManager.onJSBundleLoadedFromServer()");
|
||||||
recreateReactContextInBackground(
|
|
||||||
mJavaScriptExecutorFactory,
|
JSBundleLoader bundleLoader = nativeDeltaClient == null
|
||||||
JSBundleLoader.createCachedBundleFromNetworkLoader(
|
? JSBundleLoader.createCachedBundleFromNetworkLoader(
|
||||||
mDevSupportManager.getSourceUrl(), mDevSupportManager.getDownloadedJSBundleFile()));
|
mDevSupportManager.getSourceUrl(),
|
||||||
|
mDevSupportManager.getDownloadedJSBundleFile())
|
||||||
|
: JSBundleLoader.createDeltaFromNetworkLoader(
|
||||||
|
mDevSupportManager.getSourceUrl(), nativeDeltaClient);
|
||||||
|
|
||||||
|
recreateReactContextInBackground(mJavaScriptExecutorFactory, bundleLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ThreadConfined(UI)
|
@ThreadConfined(UI)
|
||||||
|
|
|
@ -1,121 +1,197 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2018-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
package com.facebook.react.devsupport;
|
package com.facebook.react.devsupport;
|
||||||
|
|
||||||
import android.util.JsonReader;
|
|
||||||
import android.util.JsonToken;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import android.util.JsonReader;
|
||||||
|
import android.util.JsonToken;
|
||||||
|
import android.util.Pair;
|
||||||
|
import com.facebook.react.bridge.NativeDeltaClient;
|
||||||
|
import okhttp3.Headers;
|
||||||
|
import okio.Buffer;
|
||||||
import okio.BufferedSource;
|
import okio.BufferedSource;
|
||||||
|
|
||||||
public class BundleDeltaClient {
|
public abstract class BundleDeltaClient {
|
||||||
|
|
||||||
final LinkedHashMap<Number, byte[]> mPreModules = new LinkedHashMap<Number, byte[]>();
|
private static final String METRO_DELTA_ID_HEADER = "X-Metro-Delta-ID";
|
||||||
final LinkedHashMap<Number, byte[]> mDeltaModules = new LinkedHashMap<Number, byte[]>();
|
@Nullable private String mDeltaId;
|
||||||
final LinkedHashMap<Number, byte[]> mPostModules = new LinkedHashMap<Number, byte[]>();
|
|
||||||
@Nullable String mDeltaId;
|
public enum ClientType {
|
||||||
|
NONE,
|
||||||
|
DEV_SUPPORT,
|
||||||
|
NATIVE
|
||||||
|
}
|
||||||
|
|
||||||
static boolean isDeltaUrl(String bundleUrl) {
|
static boolean isDeltaUrl(String bundleUrl) {
|
||||||
return bundleUrl.indexOf(".delta?") != -1;
|
return bundleUrl.indexOf(".delta?") != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
static BundleDeltaClient create(ClientType type) {
|
||||||
|
switch (type) {
|
||||||
|
case DEV_SUPPORT:
|
||||||
|
return new BundleDeltaJavaClient();
|
||||||
|
case NATIVE:
|
||||||
|
return new BundleDeltaNativeClient();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract public boolean canHandle(ClientType type);
|
||||||
|
|
||||||
|
abstract protected Pair<Boolean, NativeDeltaClient> processDelta(
|
||||||
|
BufferedSource body,
|
||||||
|
File outputFile) throws IOException;
|
||||||
|
|
||||||
|
final public String extendUrlForDelta(String bundleURL) {
|
||||||
|
return mDeltaId != null ? bundleURL + "&deltaBundleId=" + mDeltaId : bundleURL;
|
||||||
|
}
|
||||||
|
|
||||||
public void reset() {
|
public void reset() {
|
||||||
mDeltaId = null;
|
mDeltaId = null;
|
||||||
mDeltaModules.clear();
|
|
||||||
mPreModules.clear();
|
|
||||||
mPostModules.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toDeltaUrl(String bundleURL) {
|
public Pair<Boolean, NativeDeltaClient> processDelta(
|
||||||
if (isDeltaUrl(bundleURL) && mDeltaId != null) {
|
Headers headers,
|
||||||
return bundleURL + "&deltaBundleId=" + mDeltaId;
|
BufferedSource body,
|
||||||
}
|
File outputFile) throws IOException {
|
||||||
return bundleURL;
|
|
||||||
|
mDeltaId = headers.get(METRO_DELTA_ID_HEADER);
|
||||||
|
return processDelta(body, outputFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean storeDeltaInFile(BufferedSource body, File outputFile)
|
private static class BundleDeltaJavaClient extends BundleDeltaClient {
|
||||||
throws IOException {
|
|
||||||
|
|
||||||
JsonReader jsonReader = new JsonReader(new InputStreamReader(body.inputStream()));
|
final LinkedHashMap<Number, byte[]> mPreModules = new LinkedHashMap<Number, byte[]>();
|
||||||
|
final LinkedHashMap<Number, byte[]> mDeltaModules = new LinkedHashMap<Number, byte[]>();
|
||||||
|
final LinkedHashMap<Number, byte[]> mPostModules = new LinkedHashMap<Number, byte[]>();
|
||||||
|
|
||||||
jsonReader.beginObject();
|
@Override
|
||||||
|
public boolean canHandle(ClientType type) {
|
||||||
int numChangedModules = 0;
|
return type == ClientType.DEV_SUPPORT;
|
||||||
|
|
||||||
while (jsonReader.hasNext()) {
|
|
||||||
String name = jsonReader.nextName();
|
|
||||||
if (name.equals("id")) {
|
|
||||||
mDeltaId = jsonReader.nextString();
|
|
||||||
} else if (name.equals("pre")) {
|
|
||||||
numChangedModules += patchDelta(jsonReader, mPreModules);
|
|
||||||
} else if (name.equals("post")) {
|
|
||||||
numChangedModules += patchDelta(jsonReader, mPostModules);
|
|
||||||
} else if (name.equals("delta")) {
|
|
||||||
numChangedModules += patchDelta(jsonReader, mDeltaModules);
|
|
||||||
} else {
|
|
||||||
jsonReader.skipValue();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonReader.endObject();
|
public void reset() {
|
||||||
jsonReader.close();
|
super.reset();
|
||||||
|
mDeltaModules.clear();
|
||||||
if (numChangedModules == 0) {
|
mPreModules.clear();
|
||||||
// If we receive an empty delta, we don't need to save the file again (it'll have the
|
mPostModules.clear();
|
||||||
// same content).
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
|
@Override
|
||||||
|
public synchronized Pair<Boolean, NativeDeltaClient> processDelta(
|
||||||
|
BufferedSource body,
|
||||||
|
File outputFile) throws IOException {
|
||||||
|
|
||||||
try {
|
JsonReader jsonReader = new JsonReader(new InputStreamReader(body.inputStream()));
|
||||||
for (byte[] code : mPreModules.values()) {
|
jsonReader.beginObject();
|
||||||
fileOutputStream.write(code);
|
int numChangedModules = 0;
|
||||||
fileOutputStream.write('\n');
|
|
||||||
|
while (jsonReader.hasNext()) {
|
||||||
|
String name = jsonReader.nextName();
|
||||||
|
if (name.equals("pre")) {
|
||||||
|
numChangedModules += patchDelta(jsonReader, mPreModules);
|
||||||
|
} else if (name.equals("post")) {
|
||||||
|
numChangedModules += patchDelta(jsonReader, mPostModules);
|
||||||
|
} else if (name.equals("delta")) {
|
||||||
|
numChangedModules += patchDelta(jsonReader, mDeltaModules);
|
||||||
|
} else {
|
||||||
|
jsonReader.skipValue();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (byte[] code : mDeltaModules.values()) {
|
jsonReader.endObject();
|
||||||
fileOutputStream.write(code);
|
jsonReader.close();
|
||||||
fileOutputStream.write('\n');
|
|
||||||
|
if (numChangedModules == 0) {
|
||||||
|
// If we receive an empty delta, we don't need to save the file again (it'll have the
|
||||||
|
// same content).
|
||||||
|
return Pair.create(Boolean.FALSE, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (byte[] code : mPostModules.values()) {
|
FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
|
||||||
fileOutputStream.write(code);
|
|
||||||
fileOutputStream.write('\n');
|
try {
|
||||||
|
for (byte[] code : mPreModules.values()) {
|
||||||
|
fileOutputStream.write(code);
|
||||||
|
fileOutputStream.write('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
for (byte[] code : mDeltaModules.values()) {
|
||||||
|
fileOutputStream.write(code);
|
||||||
|
fileOutputStream.write('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
for (byte[] code : mPostModules.values()) {
|
||||||
|
fileOutputStream.write(code);
|
||||||
|
fileOutputStream.write('\n');
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
fileOutputStream.flush();
|
||||||
|
fileOutputStream.close();
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
fileOutputStream.flush();
|
return Pair.create(Boolean.TRUE, null);
|
||||||
fileOutputStream.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
private static int patchDelta(JsonReader jsonReader, LinkedHashMap<Number, byte[]> map)
|
||||||
}
|
throws IOException {
|
||||||
|
|
||||||
private static int patchDelta(JsonReader jsonReader, LinkedHashMap<Number, byte[]> map)
|
|
||||||
throws IOException {
|
|
||||||
jsonReader.beginArray();
|
|
||||||
|
|
||||||
int numModules = 0;
|
|
||||||
while (jsonReader.hasNext()) {
|
|
||||||
jsonReader.beginArray();
|
jsonReader.beginArray();
|
||||||
|
|
||||||
int moduleId = jsonReader.nextInt();
|
int numModules = 0;
|
||||||
|
while (jsonReader.hasNext()) {
|
||||||
|
jsonReader.beginArray();
|
||||||
|
|
||||||
if (jsonReader.peek() == JsonToken.NULL) {
|
int moduleId = jsonReader.nextInt();
|
||||||
jsonReader.skipValue();
|
|
||||||
map.remove(moduleId);
|
if (jsonReader.peek() == JsonToken.NULL) {
|
||||||
} else {
|
jsonReader.skipValue();
|
||||||
map.put(moduleId, jsonReader.nextString().getBytes());
|
map.remove(moduleId);
|
||||||
|
} else {
|
||||||
|
map.put(moduleId, jsonReader.nextString().getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonReader.endArray();
|
||||||
|
numModules++;
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonReader.endArray();
|
jsonReader.endArray();
|
||||||
numModules++;
|
|
||||||
|
return numModules;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class BundleDeltaNativeClient extends BundleDeltaClient {
|
||||||
|
private final NativeDeltaClient nativeClient = new NativeDeltaClient();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canHandle(ClientType type) {
|
||||||
|
return type == ClientType.NATIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonReader.endArray();
|
@Override
|
||||||
|
protected Pair<Boolean, NativeDeltaClient> processDelta(
|
||||||
|
BufferedSource body,
|
||||||
|
File outputFile) throws IOException {
|
||||||
|
nativeClient.processDelta(body);
|
||||||
|
return Pair.create(Boolean.FALSE, nativeClient);
|
||||||
|
}
|
||||||
|
|
||||||
return numModules;
|
@Override
|
||||||
|
public void reset() {
|
||||||
|
super.reset();
|
||||||
|
nativeClient.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,10 @@
|
||||||
package com.facebook.react.devsupport;
|
package com.facebook.react.devsupport;
|
||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.util.Pair;
|
||||||
import com.facebook.common.logging.FLog;
|
import com.facebook.common.logging.FLog;
|
||||||
import com.facebook.infer.annotation.Assertions;
|
import com.facebook.infer.annotation.Assertions;
|
||||||
|
import com.facebook.react.bridge.NativeDeltaClient;
|
||||||
import com.facebook.react.common.DebugServerException;
|
import com.facebook.react.common.DebugServerException;
|
||||||
import com.facebook.react.common.ReactConstants;
|
import com.facebook.react.common.ReactConstants;
|
||||||
import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener;
|
import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener;
|
||||||
|
@ -40,7 +42,7 @@ public class BundleDownloader {
|
||||||
|
|
||||||
private final OkHttpClient mClient;
|
private final OkHttpClient mClient;
|
||||||
|
|
||||||
private final BundleDeltaClient mBundleDeltaClient = new BundleDeltaClient();
|
private BundleDeltaClient mBundleDeltaClient;
|
||||||
|
|
||||||
private @Nullable Call mDownloadBundleFromURLCall;
|
private @Nullable Call mDownloadBundleFromURLCall;
|
||||||
|
|
||||||
|
@ -95,14 +97,15 @@ public class BundleDownloader {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void downloadBundleFromURL(
|
public void downloadBundleFromURL(
|
||||||
final DevBundleDownloadListener callback,
|
final DevBundleDownloadListener callback,
|
||||||
final File outputFile,
|
final File outputFile,
|
||||||
final String bundleURL,
|
final String bundleURL,
|
||||||
final @Nullable BundleInfo bundleInfo) {
|
final @Nullable BundleInfo bundleInfo,
|
||||||
|
final BundleDeltaClient.ClientType clientType) {
|
||||||
|
|
||||||
final Request request =
|
final Request request =
|
||||||
new Request.Builder()
|
new Request.Builder()
|
||||||
.url(mBundleDeltaClient.toDeltaUrl(bundleURL))
|
.url(formatBundleUrl(bundleURL, clientType))
|
||||||
// FIXME: there is a bug that makes MultipartStreamReader to never find the end of the
|
// FIXME: there is a bug that makes MultipartStreamReader to never find the end of the
|
||||||
// multipart message. This temporarily disables the multipart mode to work around it,
|
// multipart message. This temporarily disables the multipart mode to work around it,
|
||||||
// but
|
// but
|
||||||
|
@ -146,7 +149,7 @@ public class BundleDownloader {
|
||||||
try (Response r = response) {
|
try (Response r = response) {
|
||||||
if (match.find()) {
|
if (match.find()) {
|
||||||
processMultipartResponse(
|
processMultipartResponse(
|
||||||
url, r, match.group(1), outputFile, bundleInfo, callback);
|
url, r, match.group(1), outputFile, bundleInfo, clientType, callback);
|
||||||
} else {
|
} else {
|
||||||
// In case the server doesn't support multipart/mixed responses, fallback to normal
|
// In case the server doesn't support multipart/mixed responses, fallback to normal
|
||||||
// download.
|
// download.
|
||||||
|
@ -157,6 +160,7 @@ public class BundleDownloader {
|
||||||
Okio.buffer(r.body().source()),
|
Okio.buffer(r.body().source()),
|
||||||
outputFile,
|
outputFile,
|
||||||
bundleInfo,
|
bundleInfo,
|
||||||
|
clientType,
|
||||||
callback);
|
callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,12 +168,19 @@ public class BundleDownloader {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String formatBundleUrl(String bundleURL, BundleDeltaClient.ClientType clientType) {
|
||||||
|
return BundleDeltaClient.isDeltaUrl(bundleURL) && mBundleDeltaClient != null && mBundleDeltaClient.canHandle(clientType)
|
||||||
|
? mBundleDeltaClient.extendUrlForDelta(bundleURL)
|
||||||
|
: bundleURL;
|
||||||
|
}
|
||||||
|
|
||||||
private void processMultipartResponse(
|
private void processMultipartResponse(
|
||||||
final String url,
|
final String url,
|
||||||
final Response response,
|
final Response response,
|
||||||
String boundary,
|
String boundary,
|
||||||
final File outputFile,
|
final File outputFile,
|
||||||
@Nullable final BundleInfo bundleInfo,
|
@Nullable final BundleInfo bundleInfo,
|
||||||
|
final BundleDeltaClient.ClientType clientType,
|
||||||
final DevBundleDownloadListener callback)
|
final DevBundleDownloadListener callback)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
|
||||||
|
@ -193,7 +204,7 @@ public class BundleDownloader {
|
||||||
status = Integer.parseInt(headers.get("X-Http-Status"));
|
status = Integer.parseInt(headers.get("X-Http-Status"));
|
||||||
}
|
}
|
||||||
processBundleResult(
|
processBundleResult(
|
||||||
url, status, Headers.of(headers), body, outputFile, bundleInfo, callback);
|
url, status, Headers.of(headers), body, outputFile, bundleInfo, clientType, callback);
|
||||||
} else {
|
} else {
|
||||||
if (!headers.containsKey("Content-Type")
|
if (!headers.containsKey("Content-Type")
|
||||||
|| !headers.get("Content-Type").equals("application/json")) {
|
|| !headers.get("Content-Type").equals("application/json")) {
|
||||||
|
@ -249,6 +260,7 @@ public class BundleDownloader {
|
||||||
BufferedSource body,
|
BufferedSource body,
|
||||||
File outputFile,
|
File outputFile,
|
||||||
BundleInfo bundleInfo,
|
BundleInfo bundleInfo,
|
||||||
|
BundleDeltaClient.ClientType clientType,
|
||||||
DevBundleDownloadListener callback)
|
DevBundleDownloadListener callback)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
// Check for server errors. If the server error has the expected form, fail with more info.
|
// Check for server errors. If the server error has the expected form, fail with more info.
|
||||||
|
@ -274,24 +286,36 @@ public class BundleDownloader {
|
||||||
|
|
||||||
File tmpFile = new File(outputFile.getPath() + ".tmp");
|
File tmpFile = new File(outputFile.getPath() + ".tmp");
|
||||||
|
|
||||||
boolean bundleUpdated;
|
boolean bundleWritten;
|
||||||
|
NativeDeltaClient nativeDeltaClient = null;
|
||||||
|
|
||||||
if (BundleDeltaClient.isDeltaUrl(url)) {
|
if (BundleDeltaClient.isDeltaUrl(url)) {
|
||||||
// If the bundle URL has the delta extension, we need to use the delta patching logic.
|
// If the bundle URL has the delta extension, we need to use the delta patching logic.
|
||||||
bundleUpdated = mBundleDeltaClient.storeDeltaInFile(body, tmpFile);
|
BundleDeltaClient deltaClient = getBundleDeltaClient(clientType);
|
||||||
|
Assertions.assertNotNull(deltaClient);
|
||||||
|
Pair<Boolean, NativeDeltaClient> result = deltaClient.processDelta(headers, body, tmpFile);
|
||||||
|
bundleWritten = result.first;
|
||||||
|
nativeDeltaClient = result.second;
|
||||||
} else {
|
} else {
|
||||||
mBundleDeltaClient.reset();
|
mBundleDeltaClient = null;
|
||||||
bundleUpdated = storePlainJSInFile(body, tmpFile);
|
bundleWritten = storePlainJSInFile(body, tmpFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bundleUpdated) {
|
if (bundleWritten) {
|
||||||
// If we have received a new bundle from the server, move it to its final destination.
|
// If we have received a new bundle from the server, move it to its final destination.
|
||||||
if (!tmpFile.renameTo(outputFile)) {
|
if (!tmpFile.renameTo(outputFile)) {
|
||||||
throw new IOException("Couldn't rename " + tmpFile + " to " + outputFile);
|
throw new IOException("Couldn't rename " + tmpFile + " to " + outputFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
callback.onSuccess();
|
callback.onSuccess(nativeDeltaClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BundleDeltaClient getBundleDeltaClient(BundleDeltaClient.ClientType clientType) {
|
||||||
|
if (mBundleDeltaClient == null || !mBundleDeltaClient.canHandle(clientType)) {
|
||||||
|
mBundleDeltaClient = BundleDeltaClient.create(clientType);
|
||||||
|
}
|
||||||
|
return mBundleDeltaClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean storePlainJSInFile(BufferedSource body, File outputFile)
|
private static boolean storePlainJSInFile(BufferedSource body, File outputFile)
|
||||||
|
|
|
@ -375,7 +375,17 @@ public class DevServerHelper {
|
||||||
public void downloadBundleFromURL(
|
public void downloadBundleFromURL(
|
||||||
DevBundleDownloadListener callback,
|
DevBundleDownloadListener callback,
|
||||||
File outputFile, String bundleURL, BundleDownloader.BundleInfo bundleInfo) {
|
File outputFile, String bundleURL, BundleDownloader.BundleInfo bundleInfo) {
|
||||||
mBundleDownloader.downloadBundleFromURL(callback, outputFile, bundleURL, bundleInfo);
|
mBundleDownloader.downloadBundleFromURL(callback, outputFile, bundleURL, bundleInfo, getDeltaClientType());
|
||||||
|
}
|
||||||
|
|
||||||
|
private BundleDeltaClient.ClientType getDeltaClientType() {
|
||||||
|
if (mSettings.isBundleDeltasCppEnabled()) {
|
||||||
|
return BundleDeltaClient.ClientType.NATIVE;
|
||||||
|
} else if (mSettings.isBundleDeltasEnabled()) {
|
||||||
|
return BundleDeltaClient.ClientType.DEV_SUPPORT;
|
||||||
|
} else {
|
||||||
|
return BundleDeltaClient.ClientType.NONE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -32,6 +32,7 @@ import com.facebook.react.bridge.CatalystInstance;
|
||||||
import com.facebook.react.bridge.DefaultNativeModuleCallExceptionHandler;
|
import com.facebook.react.bridge.DefaultNativeModuleCallExceptionHandler;
|
||||||
import com.facebook.react.bridge.JavaJSExecutor;
|
import com.facebook.react.bridge.JavaJSExecutor;
|
||||||
import com.facebook.react.bridge.JavaScriptContextHolder;
|
import com.facebook.react.bridge.JavaScriptContextHolder;
|
||||||
|
import com.facebook.react.bridge.NativeDeltaClient;
|
||||||
import com.facebook.react.bridge.ReactContext;
|
import com.facebook.react.bridge.ReactContext;
|
||||||
import com.facebook.react.bridge.ReactMarker;
|
import com.facebook.react.bridge.ReactMarker;
|
||||||
import com.facebook.react.bridge.ReactMarkerConstants;
|
import com.facebook.react.bridge.ReactMarkerConstants;
|
||||||
|
@ -973,7 +974,7 @@ public class DevSupportManagerImpl implements
|
||||||
mDevServerHelper.downloadBundleFromURL(
|
mDevServerHelper.downloadBundleFromURL(
|
||||||
new DevBundleDownloadListener() {
|
new DevBundleDownloadListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess() {
|
public void onSuccess(final @Nullable NativeDeltaClient nativeDeltaClient) {
|
||||||
mDevLoadingViewController.hide();
|
mDevLoadingViewController.hide();
|
||||||
mDevLoadingViewVisible = false;
|
mDevLoadingViewVisible = false;
|
||||||
synchronized (DevSupportManagerImpl.this) {
|
synchronized (DevSupportManagerImpl.this) {
|
||||||
|
@ -981,14 +982,14 @@ public class DevSupportManagerImpl implements
|
||||||
mBundleStatus.updateTimestamp = System.currentTimeMillis();
|
mBundleStatus.updateTimestamp = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
if (mBundleDownloadListener != null) {
|
if (mBundleDownloadListener != null) {
|
||||||
mBundleDownloadListener.onSuccess();
|
mBundleDownloadListener.onSuccess(nativeDeltaClient);
|
||||||
}
|
}
|
||||||
UiThreadUtil.runOnUiThread(
|
UiThreadUtil.runOnUiThread(
|
||||||
new Runnable() {
|
new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
ReactMarker.logMarker(ReactMarkerConstants.DOWNLOAD_END, bundleInfo.toJSONString());
|
ReactMarker.logMarker(ReactMarkerConstants.DOWNLOAD_END, bundleInfo.toJSONString());
|
||||||
mReactInstanceManagerHelper.onJSBundleLoadedFromServer();
|
mReactInstanceManagerHelper.onJSBundleLoadedFromServer(nativeDeltaClient);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,9 @@
|
||||||
package com.facebook.react.devsupport;
|
package com.facebook.react.devsupport;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
|
||||||
import com.facebook.react.bridge.JavaJSExecutor;
|
import com.facebook.react.bridge.JavaJSExecutor;
|
||||||
|
|
||||||
|
import com.facebook.react.bridge.NativeDeltaClient;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,7 +27,7 @@ public interface ReactInstanceManagerDevHelper {
|
||||||
/**
|
/**
|
||||||
* Notify react instance manager about new JS bundle version downloaded from the server.
|
* Notify react instance manager about new JS bundle version downloaded from the server.
|
||||||
*/
|
*/
|
||||||
void onJSBundleLoadedFromServer();
|
void onJSBundleLoadedFromServer(@Nullable NativeDeltaClient nativeDeltaClient);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request to toggle the react element inspector.
|
* Request to toggle the react element inspector.
|
||||||
|
|
|
@ -7,10 +7,11 @@
|
||||||
|
|
||||||
package com.facebook.react.devsupport.interfaces;
|
package com.facebook.react.devsupport.interfaces;
|
||||||
|
|
||||||
|
import com.facebook.react.bridge.NativeDeltaClient;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
public interface DevBundleDownloadListener {
|
public interface DevBundleDownloadListener {
|
||||||
void onSuccess();
|
void onSuccess(@Nullable NativeDeltaClient nativeDeltaClient);
|
||||||
void onProgress(@Nullable String status, @Nullable Integer done, @Nullable Integer total);
|
void onProgress(@Nullable String status, @Nullable Integer done, @Nullable Integer total);
|
||||||
void onFailure(Exception cause);
|
void onFailure(Exception cause);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue