You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ag...@apache.org on 2012/09/18 19:52:50 UTC
[4/14] android commit: Optimize encoding of PluginResults within
NativeToJsMessageQueue.
Optimize encoding of PluginResults within NativeToJsMessageQueue.
Also included in this refactoring:
-Better use of StringBuilder when encoding messages
-Defers actual encoding of messages until they are popped.
-Add a pause/unpause of message queue so that all pending messages will
be sent to JS in order at the end of a PROMPT or JS_OBJECT context.
Before they may not have been sent in order.
-Tweaked LOAD_URL so that it always happens on the UI thread (avoids
log warning).
Project: http://git-wip-us.apache.org/repos/asf/incubator-cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-cordova-android/commit/365edcad
Tree: http://git-wip-us.apache.org/repos/asf/incubator-cordova-android/tree/365edcad
Diff: http://git-wip-us.apache.org/repos/asf/incubator-cordova-android/diff/365edcad
Branch: refs/heads/master
Commit: 365edcad168550a845a8543b2b60cca4c4a05321
Parents: ae9047a
Author: Andrew Grieve <ag...@chromium.org>
Authored: Tue Sep 11 10:21:54 2012 -0400
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Tue Sep 18 13:24:37 2012 -0400
----------------------------------------------------------------------
framework/src/org/apache/cordova/ExposedJsApi.java | 11 +-
.../org/apache/cordova/NativeToJsMessageQueue.java | 227 ++++++++++----
2 files changed, 170 insertions(+), 68 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-cordova-android/blob/365edcad/framework/src/org/apache/cordova/ExposedJsApi.java
----------------------------------------------------------------------
diff --git a/framework/src/org/apache/cordova/ExposedJsApi.java b/framework/src/org/apache/cordova/ExposedJsApi.java
index 461f320..daef571 100755
--- a/framework/src/org/apache/cordova/ExposedJsApi.java
+++ b/framework/src/org/apache/cordova/ExposedJsApi.java
@@ -38,9 +38,14 @@ import org.json.JSONException;
}
public String exec(String service, String action, String callbackId, String arguments) throws JSONException {
- pluginManager.exec(service, action, callbackId, arguments, true /* async */);
- // TODO(agrieve): Should this use popAll() instead?
- return jsMessageQueue.popAndEncode();
+ jsMessageQueue.setPaused(true);
+ try {
+ pluginManager.exec(service, action, callbackId, arguments, true /* async */);
+ // TODO(agrieve): Should this use popAll() instead?
+ return jsMessageQueue.popAndEncode();
+ } finally {
+ jsMessageQueue.setPaused(false);
+ }
}
public void setNativeToJsBridgeMode(int value) {
http://git-wip-us.apache.org/repos/asf/incubator-cordova-android/blob/365edcad/framework/src/org/apache/cordova/NativeToJsMessageQueue.java
----------------------------------------------------------------------
diff --git a/framework/src/org/apache/cordova/NativeToJsMessageQueue.java b/framework/src/org/apache/cordova/NativeToJsMessageQueue.java
index 08cadd3..779926d 100755
--- a/framework/src/org/apache/cordova/NativeToJsMessageQueue.java
+++ b/framework/src/org/apache/cordova/NativeToJsMessageQueue.java
@@ -19,9 +19,7 @@
package org.apache.cordova;
import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
-import java.util.ArrayList;
import java.util.LinkedList;
import org.apache.cordova.api.CordovaInterface;
@@ -55,7 +53,7 @@ public class NativeToJsMessageQueue {
/**
* The list of JavaScript statements to be sent to JavaScript.
*/
- private final LinkedList<String> queue = new LinkedList<String>();
+ private final LinkedList<JsMessage> queue = new LinkedList<JsMessage>();
/**
* The array of listeners that can be used to send messages to JS.
@@ -89,7 +87,7 @@ public class NativeToJsMessageQueue {
synchronized (this) {
activeListenerIndex = value;
BridgeMode activeListener = registeredListeners[value];
- if (!queue.isEmpty() && activeListener != null) {
+ if (!paused && !queue.isEmpty() && activeListener != null) {
activeListener.onNativeToJsMessageAvailable();
}
}
@@ -107,16 +105,26 @@ public class NativeToJsMessageQueue {
}
}
+ private int calculatePackedMessageLength(JsMessage message) {
+ int messageLen = message.calculateEncodedLength();
+ String messageLenStr = String.valueOf(messageLen);
+ return messageLenStr.length() + messageLen + 1;
+ }
+
+ private void packMessage(JsMessage message, StringBuilder sb) {
+ sb.append(message.calculateEncodedLength())
+ .append(' ');
+ message.encodeAsMessage(sb);
+ }
+
public String popAndEncode() {
synchronized (this) {
if (queue.isEmpty()) {
return null;
}
- String message = queue.removeFirst();
- StringBuffer sb = new StringBuffer(message.length() + 8)
- .append(message.length())
- .append(' ')
- .append(message);
+ JsMessage message = queue.removeFirst();
+ StringBuilder sb = new StringBuilder(calculatePackedMessageLength(message));
+ packMessage(message, sb);
return sb.toString();
}
}
@@ -131,16 +139,14 @@ public class NativeToJsMessageQueue {
if (queue.isEmpty()) {
return null;
}
- int totalMessageLen = 0;
- for (String message : queue) {
- totalMessageLen += message.length();
+ int totalPayloadLen = 0;
+ for (JsMessage message : queue) {
+ totalPayloadLen += calculatePackedMessageLength(message);
}
- StringBuffer sb = new StringBuffer(totalMessageLen + 8 * queue.size());
- for (String message : queue) {
- sb.append(message.length())
- .append(' ')
- .append(message);
+ StringBuilder sb = new StringBuilder(totalPayloadLen);
+ for (JsMessage message : queue) {
+ packMessage(message, sb);
}
queue.clear();
return sb.toString();
@@ -148,69 +154,61 @@ public class NativeToJsMessageQueue {
}
private String popAllAndEncodeAsJs() {
- String ret = popAllAndEncode();
- if (ret != null) {
- ret = "cordova.require('cordova/exec').processMessages(\"" + JSONObject.quote(ret) + "\")";
- }
- return ret;
- }
-
- private String encodePluginResult(PluginResult result, String callbackId) {
- int status = result.getStatus();
- boolean noResult = status == PluginResult.Status.NO_RESULT.ordinal();
- boolean resultOk = status == PluginResult.Status.OK.ordinal();
- boolean keepCallback = result.getKeepCallback();
- if (noResult && keepCallback) {
- return null;
- }
-
- StringBuilder sb = new StringBuilder(result.getMessage().length() + 50);
- sb.append((noResult || resultOk) ? 'S' : 'F')
- .append(keepCallback ? '1' : '0')
- .append(status)
- .append(' ')
- .append(callbackId)
- .append(' ');
- switch (result.getMessageType()) {
- case PluginResult.MESSAGE_TYPE_BOOLEAN:
- sb.append(result.getMessage().charAt(0)); // t or f.
- break;
- case PluginResult.MESSAGE_TYPE_NUMBER: // n
- sb.append('n')
- .append(result.getMessage());
- break;
- case PluginResult.MESSAGE_TYPE_STRING: // s
- sb.append('s')
- .append(result.getStrMessage());
- break;
- case PluginResult.MESSAGE_TYPE_JSON:
- default:
- sb.append(result.getMessage()); // [ or {
+ synchronized (this) {
+ int length = queue.size();
+ if (length == 0) {
+ return null;
+ }
+ int totalPayloadLen = 16 * length; // accounts for try & finally.
+ for (JsMessage message : queue) {
+ totalPayloadLen += message.calculateEncodedLength(); // overestimate.
+ }
+ StringBuilder sb = new StringBuilder(totalPayloadLen);
+ // Wrap each statement in a try/finally so that if one throws it does
+ // not affect the next.
+ int i = 0;
+ for (JsMessage message : queue) {
+ if (++i == length) {
+ message.encodeAsJsMessage(sb);
+ } else {
+ sb.append("try{");
+ message.encodeAsJsMessage(sb);
+ sb.append("}finally{");
+ }
+ }
+ for ( i = 1; i < length; ++i) {
+ sb.append('}');
+ }
+ queue.clear();
+ return sb.toString();
}
- return sb.toString();
- }
+ }
/**
* Add a JavaScript statement to the list.
*/
public void addJavaScript(String statement) {
- enqueueMessage("J" + statement);
+ enqueueMessage(new JsMessage(statement));
}
/**
* Add a JavaScript statement to the list.
*/
public void addPluginResult(PluginResult result, String callbackId) {
- String message = encodePluginResult(result, callbackId);
- if (message != null) {
- enqueueMessage(message);
+ // Don't send anything if there is no result and there is no need to
+ // clear the callbacks.
+ boolean noResult = result.getStatus() == PluginResult.Status.NO_RESULT.ordinal();
+ boolean keepCallback = result.getKeepCallback();
+ if (noResult && keepCallback) {
+ return;
}
+ enqueueMessage(new JsMessage(result, callbackId));
}
- private void enqueueMessage(String encodedMessage) {
+ private void enqueueMessage(JsMessage message) {
synchronized (this) {
- queue.add(encodedMessage);
- if (registeredListeners[activeListenerIndex] != null) {
+ queue.add(message);
+ if (!paused && registeredListeners[activeListenerIndex] != null) {
registeredListeners[activeListenerIndex].onNativeToJsMessageAvailable();
}
}
@@ -251,8 +249,17 @@ public class NativeToJsMessageQueue {
/** Uses webView.loadUrl("javascript:") to execute messages. */
private class LoadUrlBridgeMode implements BridgeMode {
+ final Runnable runnable = new Runnable() {
+ public void run() {
+ String js = popAllAndEncodeAsJs();
+ if (js != null) {
+ webView.loadUrlNow("javascript:" + js);
+ }
+ }
+ };
+
public void onNativeToJsMessageAvailable() {
- webView.loadUrlNow("javascript:" + popAllAndEncodeAsJs());
+ cordova.getActivity().runOnUiThread(runnable);
}
}
@@ -336,4 +343,94 @@ public class NativeToJsMessageQueue {
}
}
}
+ private static class JsMessage {
+ final String jsPayloadOrCallbackId;
+ final PluginResult pluginResult;
+ JsMessage(String js) {
+ jsPayloadOrCallbackId = js;
+ pluginResult = null;
+ }
+ JsMessage(PluginResult pluginResult, String callbackId) {
+ jsPayloadOrCallbackId = callbackId;
+ this.pluginResult = pluginResult;
+ }
+
+ int calculateEncodedLength() {
+ if (pluginResult == null) {
+ return jsPayloadOrCallbackId.length() + 1;
+ }
+ int statusLen = String.valueOf(pluginResult.getStatus()).length();
+ int ret = 2 + statusLen + 1 + jsPayloadOrCallbackId.length() + 1;
+ switch (pluginResult.getMessageType()) {
+ case PluginResult.MESSAGE_TYPE_BOOLEAN:
+ ret += 1;
+ break;
+ case PluginResult.MESSAGE_TYPE_NUMBER: // n
+ ret += 1 + pluginResult.getMessage().length();
+ break;
+ case PluginResult.MESSAGE_TYPE_STRING: // s
+ ret += 1 + pluginResult.getStrMessage().length();
+ break;
+ case PluginResult.MESSAGE_TYPE_JSON:
+ default:
+ ret += pluginResult.getMessage().length();
+ }
+ return ret;
+ }
+
+ void encodeAsMessage(StringBuilder sb) {
+ if (pluginResult == null) {
+ sb.append('J')
+ .append(jsPayloadOrCallbackId);
+ return;
+ }
+ int status = pluginResult.getStatus();
+ boolean noResult = status == PluginResult.Status.NO_RESULT.ordinal();
+ boolean resultOk = status == PluginResult.Status.OK.ordinal();
+ boolean keepCallback = pluginResult.getKeepCallback();
+
+ sb.append((noResult || resultOk) ? 'S' : 'F')
+ .append(keepCallback ? '1' : '0')
+ .append(status)
+ .append(' ')
+ .append(jsPayloadOrCallbackId)
+ .append(' ');
+ switch (pluginResult.getMessageType()) {
+ case PluginResult.MESSAGE_TYPE_BOOLEAN:
+ sb.append(pluginResult.getMessage().charAt(0)); // t or f.
+ break;
+ case PluginResult.MESSAGE_TYPE_NUMBER: // n
+ sb.append('n')
+ .append(pluginResult.getMessage());
+ break;
+ case PluginResult.MESSAGE_TYPE_STRING: // s
+ sb.append('s')
+ .append(pluginResult.getStrMessage());
+ break;
+ case PluginResult.MESSAGE_TYPE_JSON:
+ default:
+ sb.append(pluginResult.getMessage()); // [ or {
+ }
+ }
+
+ void encodeAsJsMessage(StringBuilder sb) {
+ if (pluginResult == null) {
+ sb.append(jsPayloadOrCallbackId);
+ } else {
+ int status = pluginResult.getStatus();
+ boolean success = (status == PluginResult.Status.OK.ordinal()) || (status == PluginResult.Status.NO_RESULT.ordinal());
+ sb.append("cordova.callbackFromNative('")
+ .append(jsPayloadOrCallbackId)
+ .append("',")
+ .append(success)
+ .append(",")
+ .append(status)
+ .append(",")
+ .append(pluginResult.getMessage())
+ .append(",")
+ .append(pluginResult.getKeepCallback())
+ .append(");");
+ }
+ }
+ }
}