Improve AppRTCDemo connection speed by sending all
http POST requests asynchronously.

R=jiayl@webrtc.org, tkchin@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/33499004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@7820 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/talk/examples/android/src/org/appspot/apprtc/WebSocketRTCClient.java b/talk/examples/android/src/org/appspot/apprtc/WebSocketRTCClient.java
index 4bfb418..58e3932 100644
--- a/talk/examples/android/src/org/appspot/apprtc/WebSocketRTCClient.java
+++ b/talk/examples/android/src/org/appspot/apprtc/WebSocketRTCClient.java
@@ -26,7 +26,6 @@
  */
 package org.appspot.apprtc;
 
-import android.os.AsyncTask;
 import android.os.Handler;
 import android.os.Looper;
 import android.util.Log;
@@ -36,7 +35,6 @@
 import java.io.OutputStream;
 import java.net.HttpURLConnection;
 import java.net.URL;
-import java.util.LinkedList;
 import java.util.Scanner;
 
 import org.appspot.apprtc.RoomParametersFetcher.RoomParametersFetcherEvents;
@@ -64,6 +62,9 @@
   private enum ConnectionState {
     NEW, CONNECTED, CLOSED, ERROR
   };
+  private enum MessageType {
+    MESSAGE, BYE
+  };
   private final Handler uiHandler;
   private boolean loopback;
   private boolean initiator;
@@ -71,14 +72,12 @@
   private WebSocketChannelClient wsClient;
   private RoomParametersFetcher fetcher;
   private ConnectionState roomState;
-  private LinkedList<PostMessage> postQueue;
   private String postMessageUrl;
   private String byeMessageUrl;
 
   public WebSocketRTCClient(SignalingEvents events) {
     this.events = events;
     uiHandler = new Handler(Looper.getMainLooper());
-    postQueue = new LinkedList<PostMessage>();
   }
 
   // --------------------------------------------------------------------
@@ -222,8 +221,9 @@
     Log.d(TAG, "Disconnect. Room state: " + roomState);
     if (roomState == ConnectionState.CONNECTED) {
       Log.d(TAG, "Closing room.");
-      sendGAEMessage(byeMessageUrl, "");
+      sendPostMessage(MessageType.BYE, byeMessageUrl, "");
     }
+    roomState = ConnectionState.CLOSED;
     if (wsClient != null) {
       wsClient.disconnect();
     }
@@ -236,10 +236,14 @@
   // we might want to filter elsewhere.
   @Override
   public void sendOfferSdp(final SessionDescription sdp) {
+    if (roomState != ConnectionState.CONNECTED) {
+      reportError("Sending offer SDP in non connected state.");
+      return;
+    }
     JSONObject json = new JSONObject();
     jsonPut(json, "sdp", sdp.description);
     jsonPut(json, "type", "offer");
-    sendGAEMessage(postMessageUrl, json.toString());
+    sendPostMessage(MessageType.MESSAGE, postMessageUrl, json.toString());
     if (loopback) {
       // In loopback mode rename this offer to answer and route it back.
       SessionDescription sdpAnswer = new SessionDescription(
@@ -279,7 +283,7 @@
         reportError("Sending ICE candidate in non connected state.");
         return;
       }
-      sendGAEMessage(postMessageUrl, json.toString());
+      sendPostMessage(MessageType.MESSAGE, postMessageUrl, json.toString());
       if (loopback) {
         events.onRemoteIceCandidate(candidate);
       }
@@ -317,88 +321,73 @@
   }
 
   private class PostMessage {
-    PostMessage(String postUrl, String message) {
+    PostMessage(MessageType type, String postUrl, String message) {
+      this.messageType = type;
       this.postUrl = postUrl;
       this.message = message;
     }
+    public final MessageType messageType;
     public final String postUrl;
     public final String message;
   }
 
   // Queue a message for sending to the room  and send it if already connected.
-  private synchronized void sendGAEMessage(String url, String message) {
-    synchronized (postQueue) {
-      postQueue.add(new PostMessage(url, message));
-    }
-    (new AsyncTask<Void, Void, Void>() {
-      public Void doInBackground(Void... unused) {
-        maybeDrainGAEPostQueue();
-        return null;
+  private synchronized void sendPostMessage(
+      MessageType messageType, String url, String message) {
+    final PostMessage postMessage = new PostMessage(messageType, url, message);
+    Runnable runDrain = new Runnable() {
+      public void run() {
+        sendPostMessageAsync(postMessage);
       }
-    }).execute();
+    };
+    new Thread(runDrain).start();
   }
 
-  // Send all queued messages if connected to the room.
-  private void maybeDrainGAEPostQueue() {
-    if (roomState != ConnectionState.CONNECTED) {
-      return;
+  // Send all queued POST messages to app engine server.
+  private void sendPostMessageAsync(PostMessage postMessage) {
+    if (postMessage.messageType == MessageType.BYE) {
+      Log.d(TAG, "C->GAE: " + postMessage.postUrl);
+    } else {
+      Log.d(TAG, "C->GAE: " + postMessage.message);
     }
-    PostMessage postMessage = null;
-    while (true) {
-      synchronized (postQueue) {
-        postMessage = postQueue.poll();
+    try {
+      // Get connection.
+      HttpURLConnection connection =
+        (HttpURLConnection) new URL(postMessage.postUrl).openConnection();
+      byte[] postData = postMessage.message.getBytes("UTF-8");
+      connection.setUseCaches(false);
+      connection.setDoOutput(true);
+      connection.setDoInput(true);
+      connection.setRequestMethod("POST");
+      connection.setFixedLengthStreamingMode(postData.length);
+      connection.setRequestProperty(
+          "content-type", "text/plain; charset=utf-8");
+
+      // Send POST request.
+      OutputStream outStream = connection.getOutputStream();
+      outStream.write(postData);
+      outStream.close();
+
+      // Get response.
+      int responseCode = connection.getResponseCode();
+      if (responseCode != 200) {
+        reportError("Non-200 response to POST: " +
+            connection.getHeaderField(null));
       }
-      if (postMessage == null) {
-        break;
+      InputStream responseStream = connection.getInputStream();
+      String response = drainStream(responseStream);
+      responseStream.close();
+      if (postMessage.messageType == MessageType.MESSAGE) {
+        JSONObject roomJson = new JSONObject(response);
+        String result = roomJson.getString("result");
+        if (!result.equals("SUCCESS")) {
+          reportError("Room POST error: " + result);
+        }
       }
-      try {
-
-        // Check if this is 'bye' message and update room connection state.
-        if (postMessage.postUrl.contains("bye")) {
-          roomState = ConnectionState.CLOSED;
-          Log.d(TAG, "C->GAE: " + postMessage.postUrl);
-        } else {
-          Log.d(TAG, "C->GAE: " + postMessage.message);
-        }
-
-        // Get connection.
-        HttpURLConnection connection =
-          (HttpURLConnection) new URL(postMessage.postUrl).openConnection();
-        byte[] postData = postMessage.message.getBytes("UTF-8");
-        connection.setUseCaches(false);
-        connection.setDoOutput(true);
-        connection.setDoInput(true);
-        connection.setRequestMethod("POST");
-        connection.setFixedLengthStreamingMode(postData.length);
-        connection.setRequestProperty(
-            "content-type", "text/plain; charset=utf-8");
-
-        // Send POST request.
-        OutputStream outStream = connection.getOutputStream();
-        outStream.write(postData);
-        outStream.close();
-
-        // Get response.
-        int responseCode = connection.getResponseCode();
-        if (responseCode != 200) {
-          reportError("Non-200 response to POST: " +
-              connection.getHeaderField(null));
-        }
-        InputStream responseStream = connection.getInputStream();
-        String response = drainStream(responseStream);
-        responseStream.close();
-        if (roomState != ConnectionState.CLOSED) {
-          JSONObject roomJson = new JSONObject(response);
-          String result = roomJson.getString("result");
-          if (!result.equals("SUCCESS")) {
-            reportError("Room POST error: " + result);
-          }
-        }
-      } catch (IOException e) {
-        reportError("GAE POST error: " + e.getMessage());
-      } catch (JSONException e) {
-        reportError("GAE POST JSON error: " + e.getMessage());
-      }
+    } catch (IOException e) {
+      reportError("GAE POST error: " + e.getMessage());
+    } catch (JSONException e) {
+      reportError("GAE POST JSON error: " + e.getMessage());
     }
   }