mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-17 12:19:12 +08:00
Split JSPackagerWebSocketClient into JSPackagerClient and ReconnectingWebSocket
Reviewed By: cwdick Differential Revision: D4495054 fbshipit-source-id: 49c634dde789317270c711370dfbdc82f70f44cd
This commit is contained in:
committed by
Facebook Github Bot
parent
0ea47424ff
commit
47616d84d8
@@ -13,7 +13,9 @@ import javax.annotation.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import android.content.Context;
|
||||
@@ -28,7 +30,8 @@ import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.common.network.OkHttpCallUtil;
|
||||
import com.facebook.react.devsupport.interfaces.PackagerStatusCallback;
|
||||
import com.facebook.react.modules.systeminfo.AndroidInfoHelpers;
|
||||
import com.facebook.react.packagerconnection.JSPackagerWebSocketClient;
|
||||
import com.facebook.react.packagerconnection.JSPackagerClient;
|
||||
import com.facebook.react.packagerconnection.ReconnectingWebSocket;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
@@ -86,7 +89,7 @@ public class DevServerHelper {
|
||||
public interface PackagerCommandListener {
|
||||
void onPackagerReloadCommand();
|
||||
void onCaptureHeapCommand();
|
||||
void onPokeSamplingProfilerCommand(@Nullable final JSPackagerWebSocketClient.WebSocketSender webSocket);
|
||||
void onPokeSamplingProfilerCommand(@Nullable final ReconnectingWebSocket.WebSocketSender webSocket);
|
||||
}
|
||||
|
||||
private final DevInternalSettings mSettings;
|
||||
@@ -94,7 +97,7 @@ public class DevServerHelper {
|
||||
private final Handler mRestartOnChangePollingHandler;
|
||||
|
||||
private boolean mOnChangePollingEnabled;
|
||||
private @Nullable JSPackagerWebSocketClient mPackagerConnection;
|
||||
private @Nullable JSPackagerClient mPackagerClient;
|
||||
private @Nullable InspectorPackagerConnection mInspectorPackagerConnection;
|
||||
private @Nullable OkHttpClient mOnChangePollingClient;
|
||||
private @Nullable OnServerContentChangeListener mOnServerContentChangeListener;
|
||||
@@ -112,32 +115,40 @@ public class DevServerHelper {
|
||||
}
|
||||
|
||||
public void openPackagerConnection(final PackagerCommandListener commandListener) {
|
||||
if (mPackagerConnection != null) {
|
||||
if (mPackagerClient != null) {
|
||||
FLog.w(ReactConstants.TAG, "Packager connection already open, nooping.");
|
||||
return;
|
||||
}
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
mPackagerConnection = new JSPackagerWebSocketClient(getPackagerConnectionURL(),
|
||||
new JSPackagerWebSocketClient.JSPackagerCallback() {
|
||||
@Override
|
||||
public void onMessage(
|
||||
@Nullable JSPackagerWebSocketClient.WebSocketSender webSocket,
|
||||
String target,
|
||||
String action) {
|
||||
if (commandListener != null && "bridge".equals(target)) {
|
||||
if ("reload".equals(action)) {
|
||||
commandListener.onPackagerReloadCommand();
|
||||
} else if ("captureHeap".equals(action)) {
|
||||
commandListener.onCaptureHeapCommand();
|
||||
} else if ("pokeSamplingProfiler".equals(action)) {
|
||||
commandListener.onPokeSamplingProfilerCommand(webSocket);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
mPackagerConnection.connect();
|
||||
Map<String, JSPackagerClient.RequestHandler> handlers =
|
||||
new HashMap<String, JSPackagerClient.RequestHandler>();
|
||||
handlers.put("reload", new JSPackagerClient.RequestHandler() {
|
||||
@Override
|
||||
public void onNotification(
|
||||
@Nullable ReconnectingWebSocket.WebSocketSender webSocket) {
|
||||
commandListener.onPackagerReloadCommand();
|
||||
}
|
||||
});
|
||||
handlers.put("captureHeap", new JSPackagerClient.RequestHandler() {
|
||||
@Override
|
||||
public void onNotification(
|
||||
@Nullable ReconnectingWebSocket.WebSocketSender webSocket) {
|
||||
commandListener.onCaptureHeapCommand();
|
||||
}
|
||||
});
|
||||
handlers.put("pokeSamplingProfiler", new JSPackagerClient.RequestHandler() {
|
||||
@Override
|
||||
public void onNotification(
|
||||
@Nullable ReconnectingWebSocket.WebSocketSender webSocket) {
|
||||
commandListener.onPokeSamplingProfilerCommand(webSocket);
|
||||
}
|
||||
});
|
||||
|
||||
mPackagerClient = new JSPackagerClient(getPackagerConnectionURL(), handlers);
|
||||
mPackagerClient.init();
|
||||
|
||||
return null;
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
@@ -147,9 +158,9 @@ public class DevServerHelper {
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
if (mPackagerConnection != null) {
|
||||
mPackagerConnection.closeQuietly();
|
||||
mPackagerConnection = null;
|
||||
if (mPackagerClient != null) {
|
||||
mPackagerClient.close();
|
||||
mPackagerClient = null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ import com.facebook.react.devsupport.interfaces.DevSupportManager;
|
||||
import com.facebook.react.devsupport.interfaces.PackagerStatusCallback;
|
||||
import com.facebook.react.devsupport.interfaces.StackFrame;
|
||||
import com.facebook.react.modules.debug.interfaces.DeveloperSettings;
|
||||
import com.facebook.react.packagerconnection.JSPackagerWebSocketClient;
|
||||
import com.facebook.react.packagerconnection.ReconnectingWebSocket;
|
||||
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.OkHttpClient;
|
||||
@@ -694,7 +694,7 @@ public class DevSupportManagerImpl implements
|
||||
|
||||
@Override
|
||||
public void onPokeSamplingProfilerCommand(
|
||||
@Nullable final JSPackagerWebSocketClient.WebSocketSender webSocket) {
|
||||
@Nullable final ReconnectingWebSocket.WebSocketSender webSocket) {
|
||||
UiThreadUtil.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -710,7 +710,7 @@ public class DevSupportManagerImpl implements
|
||||
}
|
||||
|
||||
private void handlePokeSamplingProfiler(
|
||||
@Nullable JSPackagerWebSocketClient.WebSocketSender webSocket) {
|
||||
@Nullable ReconnectingWebSocket.WebSocketSender webSocket) {
|
||||
try {
|
||||
List<String> pokeResults = JSCSamplingProfiler.poke(60000);
|
||||
for (String result : pokeResults) {
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc. All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the LICENSE file in the root
|
||||
* directory of this source tree. An additional grant of patent rights can be found in the PATENTS
|
||||
* file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.react.packagerconnection;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import android.util.JsonReader;
|
||||
import android.util.JsonToken;
|
||||
|
||||
import com.facebook.common.logging.FLog;
|
||||
|
||||
import okhttp3.ResponseBody;
|
||||
import okhttp3.ws.WebSocket;
|
||||
|
||||
/**
|
||||
* A client for packager that uses WebSocket connection.
|
||||
*/
|
||||
final public class JSPackagerClient implements ReconnectingWebSocket.MessageCallback {
|
||||
private static final String TAG = JSPackagerClient.class.getSimpleName();
|
||||
|
||||
public interface RequestHandler {
|
||||
public void onNotification(@Nullable ReconnectingWebSocket.WebSocketSender webSocket);
|
||||
}
|
||||
|
||||
private ReconnectingWebSocket mWebSocket;
|
||||
private Map<String, RequestHandler> mRequestHandlers;
|
||||
|
||||
public JSPackagerClient(String url, Map<String, RequestHandler> requestHandlers) {
|
||||
super();
|
||||
mWebSocket = new ReconnectingWebSocket(url, this);
|
||||
mRequestHandlers = requestHandlers;
|
||||
}
|
||||
|
||||
public void init() {
|
||||
mWebSocket.connect();
|
||||
}
|
||||
|
||||
public void close() {
|
||||
mWebSocket.closeQuietly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(@Nullable ReconnectingWebSocket.WebSocketSender webSocket, ResponseBody response) {
|
||||
if (response.contentType() != WebSocket.TEXT) {
|
||||
FLog.w(TAG, "Websocket received unexpected message with payload of type " + response.contentType());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
JsonReader reader = new JsonReader(response.charStream());
|
||||
|
||||
Integer version = null;
|
||||
String target = null;
|
||||
String action = null;
|
||||
|
||||
reader.beginObject();
|
||||
while (reader.hasNext()) {
|
||||
String field = reader.nextName();
|
||||
|
||||
if (JsonToken.NULL == reader.peek()) {
|
||||
reader.skipValue();
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("version".equals(field)) {
|
||||
version = reader.nextInt();
|
||||
} else if ("target".equals(field)) {
|
||||
target = reader.nextString();
|
||||
} else if ("action".equals(field)) {
|
||||
action = reader.nextString();
|
||||
}
|
||||
}
|
||||
reader.close();
|
||||
|
||||
if (version == null || target == null || action == null || version != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ("bridge".equals(target) && mRequestHandlers.containsKey(action)) {
|
||||
mRequestHandlers.get(action).onNotification(webSocket);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
FLog.e(TAG, "Parsing response message from websocket failed", e);
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,6 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.JsonReader;
|
||||
import android.util.JsonToken;
|
||||
|
||||
import com.facebook.common.logging.FLog;
|
||||
|
||||
@@ -31,19 +29,14 @@ import okhttp3.ws.WebSocketListener;
|
||||
import okio.Buffer;
|
||||
|
||||
/**
|
||||
* A wrapper around WebSocketClient that recognizes packager's message format.
|
||||
* A wrapper around WebSocketClient that reconnects automatically
|
||||
*/
|
||||
public class JSPackagerWebSocketClient implements WebSocketListener {
|
||||
private static final String TAG = "JSPackagerWebSocketClient";
|
||||
final public class ReconnectingWebSocket implements WebSocketListener {
|
||||
private static final String TAG = ReconnectingWebSocket.class.getSimpleName();
|
||||
|
||||
private static final int RECONNECT_DELAY_MS = 2000;
|
||||
|
||||
private final String mUrl;
|
||||
private final Handler mHandler;
|
||||
private boolean mClosed = false;
|
||||
private boolean mSuppressConnectionErrors;
|
||||
|
||||
final public class WebSocketSender {
|
||||
static public class WebSocketSender {
|
||||
private WebSocket mWebSocket;
|
||||
|
||||
public WebSocketSender(WebSocket webSocket) {
|
||||
@@ -55,14 +48,18 @@ public class JSPackagerWebSocketClient implements WebSocketListener {
|
||||
}
|
||||
}
|
||||
|
||||
public interface JSPackagerCallback {
|
||||
void onMessage(@Nullable WebSocketSender webSocket, String target, String action);
|
||||
public interface MessageCallback {
|
||||
void onMessage(@Nullable WebSocketSender webSocket, ResponseBody message);
|
||||
}
|
||||
|
||||
private final String mUrl;
|
||||
private final Handler mHandler;
|
||||
private boolean mClosed = false;
|
||||
private boolean mSuppressConnectionErrors;
|
||||
private @Nullable WebSocket mWebSocket;
|
||||
private @Nullable JSPackagerCallback mCallback;
|
||||
private @Nullable MessageCallback mCallback;
|
||||
|
||||
public JSPackagerWebSocketClient(String url, JSPackagerCallback callback) {
|
||||
public ReconnectingWebSocket(String url, MessageCallback callback) {
|
||||
super();
|
||||
mUrl = url;
|
||||
mCallback = callback;
|
||||
@@ -73,33 +70,40 @@ public class JSPackagerWebSocketClient implements WebSocketListener {
|
||||
if (mClosed) {
|
||||
throw new IllegalStateException("Can't connect closed client");
|
||||
}
|
||||
OkHttpClient httpClient = new OkHttpClient.Builder()
|
||||
.connectTimeout(10, TimeUnit.SECONDS)
|
||||
.writeTimeout(10, TimeUnit.SECONDS)
|
||||
.readTimeout(0, TimeUnit.MINUTES) // Disable timeouts for read
|
||||
.build();
|
||||
|
||||
OkHttpClient httpClient = new OkHttpClient.Builder()
|
||||
.connectTimeout(10, TimeUnit.SECONDS)
|
||||
.writeTimeout(10, TimeUnit.SECONDS)
|
||||
.readTimeout(0, TimeUnit.MINUTES) // Disable timeouts for read
|
||||
.build();
|
||||
|
||||
Request request = new Request.Builder().url(mUrl).build();
|
||||
WebSocketCall call = WebSocketCall.create(httpClient, request);
|
||||
call.enqueue(this);
|
||||
}
|
||||
|
||||
private synchronized void delayedReconnect() {
|
||||
// check that we haven't been closed in the meantime
|
||||
if (!mClosed) {
|
||||
connect();
|
||||
}
|
||||
}
|
||||
|
||||
private void reconnect() {
|
||||
if (mClosed) {
|
||||
throw new IllegalStateException("Can't reconnect closed client");
|
||||
}
|
||||
|
||||
if (!mSuppressConnectionErrors) {
|
||||
FLog.w(TAG, "Couldn't connect to packager, will silently retry");
|
||||
FLog.w(TAG, "Couldn't connect to \"" + mUrl + "\", will silently retry");
|
||||
mSuppressConnectionErrors = true;
|
||||
}
|
||||
|
||||
mHandler.postDelayed(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// check that we haven't been closed in the meantime
|
||||
if (!mClosed) {
|
||||
connect();
|
||||
}
|
||||
delayedReconnect();
|
||||
}
|
||||
},
|
||||
RECONNECT_DELAY_MS);
|
||||
@@ -108,6 +112,7 @@ public class JSPackagerWebSocketClient implements WebSocketListener {
|
||||
public void closeQuietly() {
|
||||
mClosed = true;
|
||||
closeWebSocketQuietly();
|
||||
mCallback = null;
|
||||
}
|
||||
|
||||
private void closeWebSocketQuietly() {
|
||||
@@ -121,65 +126,19 @@ public class JSPackagerWebSocketClient implements WebSocketListener {
|
||||
}
|
||||
}
|
||||
|
||||
private void triggerMessageCallback(String target, String action) {
|
||||
if (mCallback != null) {
|
||||
WebSocketSender webSocketSender = mWebSocket == null
|
||||
? null
|
||||
: new WebSocketSender(mWebSocket);
|
||||
mCallback.onMessage(webSocketSender, target, action);
|
||||
}
|
||||
private void abort(String message, Throwable cause) {
|
||||
FLog.e(TAG, "Error occurred, shutting down websocket connection: " + message, cause);
|
||||
closeWebSocketQuietly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(ResponseBody response) throws IOException {
|
||||
if (response.contentType() != WebSocket.TEXT) {
|
||||
FLog.w(TAG, "Websocket received unexpected message with payload of type " + response.contentType());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
JsonReader reader = new JsonReader(response.charStream());
|
||||
|
||||
Integer version = null;
|
||||
String target = null;
|
||||
String action = null;
|
||||
|
||||
reader.beginObject();
|
||||
while (reader.hasNext()) {
|
||||
String field = reader.nextName();
|
||||
|
||||
if (JsonToken.NULL == reader.peek()) {
|
||||
reader.skipValue();
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("version".equals(field)) {
|
||||
version = reader.nextInt();
|
||||
} else if ("target".equals(field)) {
|
||||
target = reader.nextString();
|
||||
} else if ("action".equals(field)) {
|
||||
action = reader.nextString();
|
||||
}
|
||||
}
|
||||
reader.close();
|
||||
|
||||
if (version == null || version != 1) {
|
||||
return;
|
||||
}
|
||||
if (target == null || action == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
triggerMessageCallback(target, action);
|
||||
} catch (IOException e) {
|
||||
abort("Parsing response message from websocket failed", e);
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
public synchronized void onOpen(WebSocket webSocket, Response response) {
|
||||
mWebSocket = webSocket;
|
||||
mSuppressConnectionErrors = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(IOException e, Response response) {
|
||||
public synchronized void onFailure(IOException e, Response response) {
|
||||
if (mWebSocket != null) {
|
||||
abort("Websocket exception", e);
|
||||
}
|
||||
@@ -189,26 +148,23 @@ public class JSPackagerWebSocketClient implements WebSocketListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(WebSocket webSocket, Response response) {
|
||||
mWebSocket = webSocket;
|
||||
mSuppressConnectionErrors = false;
|
||||
public synchronized void onMessage(ResponseBody message) {
|
||||
if (mCallback != null) {
|
||||
WebSocketSender webSocketSender = mWebSocket == null
|
||||
? null
|
||||
: new WebSocketSender(mWebSocket);
|
||||
mCallback.onMessage(webSocketSender, message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(int code, String reason) {
|
||||
public synchronized void onPong(Buffer payload) { }
|
||||
|
||||
@Override
|
||||
public synchronized void onClose(int code, String reason) {
|
||||
mWebSocket = null;
|
||||
if (!mClosed) {
|
||||
reconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPong(Buffer payload) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
private void abort(String message, Throwable cause) {
|
||||
FLog.e(TAG, "Error occurred, shutting down websocket connection: " + message, cause);
|
||||
closeWebSocketQuietly();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user