forked from facebook/react-native
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
- Loading branch information
1 parent
8f85abd
commit dd036c2
Showing
7 changed files
with
222 additions
and
103 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
224 changes: 150 additions & 74 deletions
224
ReactAndroid/src/main/java/com/facebook/react/devsupport/BundleDeltaClient.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
|
||
import android.util.JsonReader; | ||
import android.util.JsonToken; | ||
import java.io.File; | ||
import java.io.FileOutputStream; | ||
import java.io.IOException; | ||
import java.io.InputStreamReader; | ||
import java.util.LinkedHashMap; | ||
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; | ||
|
||
public class BundleDeltaClient { | ||
public abstract class BundleDeltaClient { | ||
|
||
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[]>(); | ||
@Nullable String mDeltaId; | ||
private static final String METRO_DELTA_ID_HEADER = "X-Metro-Delta-ID"; | ||
@Nullable private String mDeltaId; | ||
|
||
static boolean isDeltaUrl(String bundleUrl) { | ||
return bundleUrl.indexOf(".delta?") != -1; | ||
public enum ClientType { | ||
NONE, | ||
DEV_SUPPORT, | ||
NATIVE | ||
} | ||
|
||
public void reset() { | ||
mDeltaId = null; | ||
mDeltaModules.clear(); | ||
mPreModules.clear(); | ||
mPostModules.clear(); | ||
static boolean isDeltaUrl(String bundleUrl) { | ||
return bundleUrl.indexOf(".delta?") != -1; | ||
} | ||
|
||
public String toDeltaUrl(String bundleURL) { | ||
if (isDeltaUrl(bundleURL) && mDeltaId != null) { | ||
return bundleURL + "&deltaBundleId=" + mDeltaId; | ||
@Nullable | ||
static BundleDeltaClient create(ClientType type) { | ||
switch (type) { | ||
case DEV_SUPPORT: | ||
return new BundleDeltaJavaClient(); | ||
case NATIVE: | ||
return new BundleDeltaNativeClient(); | ||
} | ||
return bundleURL; | ||
return null; | ||
} | ||
|
||
public synchronized boolean storeDeltaInFile(BufferedSource body, File outputFile) | ||
throws IOException { | ||
abstract public boolean canHandle(ClientType type); | ||
|
||
JsonReader jsonReader = new JsonReader(new InputStreamReader(body.inputStream())); | ||
abstract protected Pair<Boolean, NativeDeltaClient> processDelta( | ||
BufferedSource body, | ||
File outputFile) throws IOException; | ||
|
||
jsonReader.beginObject(); | ||
final public String extendUrlForDelta(String bundleURL) { | ||
return mDeltaId != null ? bundleURL + "&deltaBundleId=" + mDeltaId : bundleURL; | ||
} | ||
|
||
int numChangedModules = 0; | ||
public void reset() { | ||
mDeltaId = null; | ||
} | ||
|
||
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(); | ||
} | ||
} | ||
public Pair<Boolean, NativeDeltaClient> processDelta( | ||
Headers headers, | ||
BufferedSource body, | ||
File outputFile) throws IOException { | ||
|
||
mDeltaId = headers.get(METRO_DELTA_ID_HEADER); | ||
return processDelta(body, outputFile); | ||
} | ||
|
||
jsonReader.endObject(); | ||
jsonReader.close(); | ||
private static class BundleDeltaJavaClient extends BundleDeltaClient { | ||
|
||
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 false; | ||
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[]>(); | ||
|
||
@Override | ||
public boolean canHandle(ClientType type) { | ||
return type == ClientType.DEV_SUPPORT; | ||
} | ||
|
||
FileOutputStream fileOutputStream = new FileOutputStream(outputFile); | ||
public void reset() { | ||
super.reset(); | ||
mDeltaModules.clear(); | ||
mPreModules.clear(); | ||
mPostModules.clear(); | ||
} | ||
|
||
try { | ||
for (byte[] code : mPreModules.values()) { | ||
fileOutputStream.write(code); | ||
fileOutputStream.write('\n'); | ||
@Override | ||
public synchronized Pair<Boolean, NativeDeltaClient> processDelta( | ||
BufferedSource body, | ||
File outputFile) throws IOException { | ||
|
||
JsonReader jsonReader = new JsonReader(new InputStreamReader(body.inputStream())); | ||
jsonReader.beginObject(); | ||
int numChangedModules = 0; | ||
|
||
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()) { | ||
fileOutputStream.write(code); | ||
fileOutputStream.write('\n'); | ||
jsonReader.endObject(); | ||
jsonReader.close(); | ||
|
||
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.write(code); | ||
fileOutputStream.write('\n'); | ||
FileOutputStream fileOutputStream = new FileOutputStream(outputFile); | ||
|
||
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(); | ||
fileOutputStream.close(); | ||
|
||
return Pair.create(Boolean.TRUE, null); | ||
} | ||
|
||
return true; | ||
} | ||
private static int patchDelta(JsonReader jsonReader, LinkedHashMap<Number, byte[]> map) | ||
throws IOException { | ||
jsonReader.beginArray(); | ||
|
||
private static int patchDelta(JsonReader jsonReader, LinkedHashMap<Number, byte[]> map) | ||
throws IOException { | ||
jsonReader.beginArray(); | ||
int numModules = 0; | ||
while (jsonReader.hasNext()) { | ||
jsonReader.beginArray(); | ||
|
||
int numModules = 0; | ||
while (jsonReader.hasNext()) { | ||
jsonReader.beginArray(); | ||
int moduleId = jsonReader.nextInt(); | ||
|
||
int moduleId = jsonReader.nextInt(); | ||
if (jsonReader.peek() == JsonToken.NULL) { | ||
jsonReader.skipValue(); | ||
map.remove(moduleId); | ||
} else { | ||
map.put(moduleId, jsonReader.nextString().getBytes()); | ||
} | ||
|
||
if (jsonReader.peek() == JsonToken.NULL) { | ||
jsonReader.skipValue(); | ||
map.remove(moduleId); | ||
} else { | ||
map.put(moduleId, jsonReader.nextString().getBytes()); | ||
jsonReader.endArray(); | ||
numModules++; | ||
} | ||
|
||
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(); | ||
} | ||
} | ||
} |
Oops, something went wrong.