Android WebSocket實踐

既生HTTP何生WebSockt

與服務端交互的協議中,HTTP只能被動接收客戶端請求,而後響應。而WebSocket在客戶端與服務端創建鏈接之後,服務端能夠主動發消息給客戶端。這就是WebSocket的特色。html

應用

由於服務端能夠主動發消息的特性,WebSocket在如下場景使用很是多: 一、推送 二、聊天室 三、開發工具 4...java

大部分功能依賴於產品的業務,投入使用可能比較困難,而開發工具是開發人員能夠肆意發揮的地方!react

ReactNative中WebSocket的使用

ReactNative開發過程當中,pc端能夠控制android端從新加載、打印堆棧等等。這些的實現都依賴於WebSocket。android

com.facebook.react.packagerconnection.ReconnectingWebSocket實現了WebSocket的鏈接,使用了okhttp。git

final public class ReconnectingWebSocket extends WebSocketListener {
      public ReconnectingWebSocket( String url, MessageCallback messageCallback, ConnectionCallback connectionCallback) {
    super();
    mUrl = url;
    mMessageCallback = messageCallback;
    mConnectionCallback = connectionCallback;
    mHandler = new Handler(Looper.getMainLooper());
  }

  public void connect() {
    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();

    Request request = new Request.Builder().url(mUrl).build();
    httpClient.newWebSocket(request, this);
  }
}
複製代碼

JSPackagerClient包裝了ReconnectingWebSocket,在onMessage回調處分發服務端下發的命令:github

final public class JSPackagerClient implements ReconnectingWebSocket.MessageCallback {
  private ReconnectingWebSocket mWebSocket;

  public JSPackagerClient( String clientId, PackagerConnectionSettings settings, Map<String, RequestHandler> requestHandlers, @Nullable ReconnectingWebSocket.ConnectionCallback connectionCallback) {
    super();

    Uri.Builder builder = new Uri.Builder();
    builder.scheme("ws")
      .encodedAuthority(settings.getDebugServerHost())
      .appendPath("message")
      .appendQueryParameter("device", AndroidInfoHelpers.getFriendlyDeviceName())
      .appendQueryParameter("app", settings.getPackageName())
      .appendQueryParameter("clientid", clientId);
    String url = builder.build().toString();

    mWebSocket = new ReconnectingWebSocket(url, this, connectionCallback);
    mRequestHandlers = requestHandlers;
  }

  @Override
  public void onMessage(String text) {
    try {
      JSONObject message = new JSONObject(text);

      int version = message.optInt("version");
      String method = message.optString("method");
      Object id = message.opt("id");
      Object params = message.opt("params");

      if (version != PROTOCOL_VERSION) {
        FLog.e(
          TAG,
          "Message with incompatible or missing version of protocol received: " + version);
        return;
      }

      if (method == null) {
        abortOnMessage(id, "No method provided");
        return;
      }

      RequestHandler handler = mRequestHandlers.get(method);
      if (handler == null) {
        abortOnMessage(id, "No request handler for method: " + method);
        return;
      }

      if (id == null) {
        handler.onNotification(params);
      } else {
        handler.onRequest(params, new ResponderImpl(id));
      }
    } catch (Exception e) {
      FLog.e(TAG, "Handling the message failed", e);
    }
  }

}
複製代碼

命令的具體執行在RequestHandler。注入RequestHandler的時機在DevServerHelper#openPackagerConnectionweb

public void openPackagerConnection( final String clientId, final PackagerCommandListener commandListener) {
    if (mPackagerClient != null) {
      FLog.w(ReactConstants.TAG, "Packager connection already open, nooping.");
      return;
    }
    new AsyncTask<Void, Void, Void>() {
      @Override
      protected Void doInBackground(Void... backgroundParams) {
        Map<String, RequestHandler> handlers = new HashMap<>();
        handlers.put("reload", new NotificationOnlyHandler() {
          @Override
          public void onNotification(@Nullable Object params) {
            commandListener.onPackagerReloadCommand();
          }
        });
        handlers.put("devMenu", new NotificationOnlyHandler() {
          @Override
          public void onNotification(@Nullable Object params) {
            commandListener.onPackagerDevMenuCommand();
          }
        });
        handlers.put("captureHeap", new RequestOnlyHandler() {
          @Override
          public void onRequest(@Nullable Object params, Responder responder) {
            commandListener.onCaptureHeapCommand(responder);
          }
        });
        handlers.putAll(new FileIoHandler().handlers());

        ConnectionCallback onPackagerConnectedCallback =
          new ConnectionCallback() {
              @Override
              public void onConnected() {
                commandListener.onPackagerConnected();
              }

              @Override
              public void onDisconnected() {
                commandListener.onPackagerDisconnected();
              }
            };

        mPackagerClient = new JSPackagerClient(
            clientId,
            mSettings.getPackagerConnectionSettings(),
            handlers,
            onPackagerConnectedCallback);
        mPackagerClient.init();

        return null;
      }
    }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
  }
複製代碼

Android客戶端實現一個WebSocket Server

大部分使用WebSocket的開發工具,服務端都是pc,Android端都是一個client。通過okhttp的封裝,實現client很是簡單。websocket

github上找到了兩個java websocket server的庫,Java-WebSocketAndroidAsync。嘗試寫了一下Demo,最終只跑通了AndroidAsync,github:github.com/PortgasAce/…app

Server的主要代碼:socket

AsyncHttpClient.getDefaultInstance().websocket(get, "my-protocol", new WebSocketConnectCallback() {
    @Override
    public void onCompleted(Exception ex, WebSocket webSocket) {
        if (ex != null) {
            ex.printStackTrace();
            return;
        }
        webSocket.send("a string");
        // webSocket.send(new byte[10]);
        webSocket.setStringCallback(new StringCallback() {
            public void onStringAvailable(String s) {
                System.out.println("I got a string: " + s);
            }
        });
        webSocket.setDataCallback(new DataCallback() {
            public void onDataAvailable(DataEmitter emitter, ByteBufferList byteBufferList) {
                System.out.println("I got some bytes!");
                // note that this data has been read
                byteBufferList.recycle();
            }
        });
    }
});
複製代碼

上面是github的readme,註釋了一行webSocket.send(new byte[10]);,不然websocket鏈接會斷掉。

參考

知乎WebSocket原理:www.zhihu.com/question/20…

阮一峯WebSocket教程:www.ruanyifeng.com/blog/2017/0…

Java-WebSocket:github.com/TooTallNate…

AndroidAsync:github.com/koush/Andro…

相關文章
相關標籤/搜索