Implement WebSocket module for Android. Fixes #2837

Summary: The JavaScript code for Android is same as the iOS counterpart, I just added few new lines and used arrow functions instead of binding `this`.
Closes https://github.com/facebook/react-native/pull/2839

Reviewed By: @​svcscm, @vjeux

Differential Revision: D2498703

Pulled By: @mkonicek

fb-gh-sync-id: 3fe958dd5af0efba00df07515f8e33b5d87eb05b
This commit is contained in:
Satyajit Sahoo
2015-10-07 08:28:34 -07:00
committed by facebook-github-bot-8
parent 57a7387d85
commit f4857a6d42
10 changed files with 335 additions and 177 deletions

View File

@@ -0,0 +1,170 @@
/**
* 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.modules.websocket;
import java.io.IOException;
import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import com.squareup.okhttp.ws.WebSocket;
import com.squareup.okhttp.ws.WebSocketCall;
import com.squareup.okhttp.ws.WebSocketListener;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import okio.Buffer;
import okio.BufferedSource;
public class WebSocketModule extends ReactContextBaseJavaModule {
private Map<Integer, WebSocket> mWebSocketConnections = new HashMap<>();
private ReactContext mReactContext;
public WebSocketModule(ReactApplicationContext context) {
super(context);
mReactContext = context;
}
private void sendEvent(String eventName, WritableMap params) {
mReactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, params);
}
@Override
public String getName() {
return "WebSocketModule";
}
@ReactMethod
public void connect(final String url, final int id) {
OkHttpClient client = new OkHttpClient();
client.setConnectTimeout(10, TimeUnit.SECONDS);
client.setWriteTimeout(10, TimeUnit.SECONDS);
// Disable timeouts for read
client.setReadTimeout(0, TimeUnit.MINUTES);
Request request = new Request.Builder()
.tag(id)
.url(url)
.build();
WebSocketCall.create(client, request).enqueue(new WebSocketListener() {
@Override
public void onOpen(WebSocket webSocket, Response response) {
mWebSocketConnections.put(id, webSocket);
WritableMap params = Arguments.createMap();
params.putInt("id", id);
sendEvent("websocketOpen", params);
}
@Override
public void onClose(int code, String reason) {
WritableMap params = Arguments.createMap();
params.putInt("id", id);
params.putInt("code", code);
params.putString("reason", reason);
sendEvent("websocketClosed", params);
}
@Override
public void onFailure(IOException e, Response response) {
notifyWebSocketFailed(id, e.getMessage());
}
@Override
public void onPong(Buffer buffer) {
}
@Override
public void onMessage(BufferedSource bufferedSource, WebSocket.PayloadType payloadType) {
String message;
try {
message = bufferedSource.readUtf8();
} catch (IOException e) {
notifyWebSocketFailed(id, e.getMessage());
return;
}
try {
bufferedSource.close();
} catch (IOException e) {
FLog.e(
ReactConstants.TAG,
"Could not close BufferedSource for WebSocket id " + id,
e);
}
WritableMap params = Arguments.createMap();
params.putInt("id", id);
params.putString("data", message);
sendEvent("websocketMessage", params);
}
});
// Trigger shutdown of the dispatcher's executor so this process can exit cleanly
client.getDispatcher().getExecutorService().shutdown();
}
@ReactMethod
public void close(int code, String reason, int id) {
WebSocket client = mWebSocketConnections.get(id);
if (client == null) {
// This is a programmer error
throw new RuntimeException("Cannot close WebSocket. Unknown WebSocket id " + id);
}
try {
client.close(code, reason);
mWebSocketConnections.remove(id);
} catch (Exception e) {
FLog.e(
ReactConstants.TAG,
"Could not close WebSocket connection for id " + id,
e);
}
}
@ReactMethod
public void send(String message, int id) {
WebSocket client = mWebSocketConnections.get(id);
if (client == null) {
// This is a programmer error
throw new RuntimeException("Cannot send a message. Unknown WebSocket id " + id);
}
try {
client.sendMessage(
WebSocket.PayloadType.TEXT,
new Buffer().writeUtf8(message));
} catch (IOException e) {
notifyWebSocketFailed(id, e.getMessage());
}
}
private void notifyWebSocketFailed(int id, String message) {
WritableMap params = Arguments.createMap();
params.putInt("id", id);
params.putString("message", message);
sendEvent("websocketFailed", params);
}
}

View File

@@ -21,6 +21,7 @@ import com.facebook.react.modules.fresco.FrescoModule;
import com.facebook.react.modules.network.NetworkingModule;
import com.facebook.react.modules.storage.AsyncStorageModule;
import com.facebook.react.modules.toast.ToastModule;
import com.facebook.react.modules.websocket.WebSocketModule;
import com.facebook.react.uimanager.ViewManager;
import com.facebook.react.views.drawer.ReactDrawerLayoutManager;
import com.facebook.react.views.image.ReactImageManager;
@@ -47,6 +48,7 @@ public class MainReactPackage implements ReactPackage {
new AsyncStorageModule(reactContext),
new FrescoModule(reactContext),
new NetworkingModule(reactContext),
new WebSocketModule(reactContext),
new ToastModule(reactContext));
}