Implement the online events based Native->JS bridge.

This commit is contained in:
Andrew Grieve 2012-08-21 14:15:04 -04:00
parent 7fa4515c28
commit e239fd970f
2 changed files with 79 additions and 62 deletions

View File

@ -198,7 +198,7 @@ public class CordovaWebView extends WebView {
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@SuppressLint("NewApi") @SuppressLint("NewApi")
private void setup() { private void setup() {
jsMessageQueue = new NativeToJsMessageQueue(this); jsMessageQueue = new NativeToJsMessageQueue(this, cordova);
this.setInitialScale(0); this.setInitialScale(0);
this.setVerticalScrollBarEnabled(false); this.setVerticalScrollBarEnabled(false);

View File

@ -20,6 +20,8 @@ package org.apache.cordova;
import java.util.LinkedList; import java.util.LinkedList;
import org.apache.cordova.api.CordovaInterface;
import android.util.Log; import android.util.Log;
/** /**
@ -31,27 +33,33 @@ public class NativeToJsMessageQueue {
// This must match the default value in incubator-cordova-js/lib/android/exec.js // This must match the default value in incubator-cordova-js/lib/android/exec.js
private static final int DEFAULT_BRIDGE_MODE = 1; private static final int DEFAULT_BRIDGE_MODE = 1;
/**
* The list of JavaScript statements to be sent to JavaScript.
*/
private LinkedList<String> queue = new LinkedList<String>();
/** /**
* The index into registeredListeners to treat as active. * The index into registeredListeners to treat as active.
*/ */
private int activeListenerIndex; private int activeListenerIndex;
/**
* The list of JavaScript statements to be sent to JavaScript.
*/
private final LinkedList<String> queue = new LinkedList<String>();
/** /**
* The array of listeners that can be used to send messages to JS. * The array of listeners that can be used to send messages to JS.
*/ */
private BridgeMode[] registeredListeners; private final BridgeMode[] registeredListeners;
public NativeToJsMessageQueue(CordovaWebView webView) { private final CordovaInterface cordova;
registeredListeners = new BridgeMode[3]; private final CordovaWebView webView;
registeredListeners[0] = null;
registeredListeners[1] = new CallbackBridgeMode(webView); public NativeToJsMessageQueue(CordovaWebView webView, CordovaInterface cordova) {
registeredListeners[2] = new LoadUrlBridgeMode(webView); this.cordova = cordova;
reset(); this.webView = webView;
registeredListeners = new BridgeMode[4];
registeredListeners[0] = null;
registeredListeners[1] = new CallbackBridgeMode();
registeredListeners[2] = new LoadUrlBridgeMode();
registeredListeners[3] = new OnlineEventsBridgeMode();
reset();
// POLLING: 0, // POLLING: 0,
// HANGING_GET: 1, // HANGING_GET: 1,
// LOAD_URL: 2, // LOAD_URL: 2,
@ -63,30 +71,30 @@ public class NativeToJsMessageQueue {
* Changes the bridge mode. * Changes the bridge mode.
*/ */
public void setBridgeMode(int value) { public void setBridgeMode(int value) {
if (value < 0 || value >= registeredListeners.length) { if (value < 0 || value >= registeredListeners.length) {
Log.d(LOG_TAG, "Invalid NativeToJsBridgeMode: " + value); Log.d(LOG_TAG, "Invalid NativeToJsBridgeMode: " + value);
} else { } else {
if (value != activeListenerIndex) { if (value != activeListenerIndex) {
Log.d(LOG_TAG, "Set native->JS mode to " + value); Log.d(LOG_TAG, "Set native->JS mode to " + value);
synchronized (this) { synchronized (this) {
activeListenerIndex = value; activeListenerIndex = value;
BridgeMode activeListener = registeredListeners[value]; BridgeMode activeListener = registeredListeners[value];
if (!queue.isEmpty() && activeListener != null) { if (!queue.isEmpty() && activeListener != null) {
activeListener.onNativeToJsMessageAvailable(this); activeListener.onNativeToJsMessageAvailable();
} }
} }
} }
} }
} }
/** /**
* Clears all messages and resets to the default bridge mode. * Clears all messages and resets to the default bridge mode.
*/ */
public void reset() { public void reset() {
synchronized (this) { synchronized (this) {
queue.clear(); queue.clear();
setBridgeMode(DEFAULT_BRIDGE_MODE); setBridgeMode(DEFAULT_BRIDGE_MODE);
} }
} }
/** /**
@ -116,17 +124,17 @@ public class NativeToJsMessageQueue {
// Wrap each statement in a try/finally so that if one throws it does // Wrap each statement in a try/finally so that if one throws it does
// not affect the next. // not affect the next.
int i = 0; int i = 0;
for (String message : queue) { for (String message : queue) {
if (++i == length) { if (++i == length) {
sb.append(message); sb.append(message);
} else { } else {
sb.append("try{") sb.append("try{")
.append(message) .append(message)
.append("}finally{"); .append("}finally{");
} }
} }
for ( i = 1; i < length; ++i) { for ( i = 1; i < length; ++i) {
sb.append('}'); sb.append('}');
} }
queue.clear(); queue.clear();
return sb.toString(); return sb.toString();
@ -140,37 +148,46 @@ public class NativeToJsMessageQueue {
synchronized (this) { synchronized (this) {
queue.add(statement); queue.add(statement);
if (registeredListeners[activeListenerIndex] != null) { if (registeredListeners[activeListenerIndex] != null) {
registeredListeners[activeListenerIndex].onNativeToJsMessageAvailable(this); registeredListeners[activeListenerIndex].onNativeToJsMessageAvailable();
} }
} }
} }
private interface BridgeMode { private interface BridgeMode {
void onNativeToJsMessageAvailable(NativeToJsMessageQueue queue); void onNativeToJsMessageAvailable();
} }
/** Uses a local server to send messages to JS via an XHR */ /** Uses a local server to send messages to JS via an XHR */
private static class CallbackBridgeMode implements BridgeMode { private class CallbackBridgeMode implements BridgeMode {
private CordovaWebView webView; public void onNativeToJsMessageAvailable() {
public CallbackBridgeMode(CordovaWebView webView) { if (webView.callbackServer != null) {
this.webView = webView; webView.callbackServer.onNativeToJsMessageAvailable(NativeToJsMessageQueue.this);
} }
public void onNativeToJsMessageAvailable(NativeToJsMessageQueue queue) {
if (webView.callbackServer != null) {
webView.callbackServer.onNativeToJsMessageAvailable(queue);
}
} }
} }
/** Uses webView.loadUrl("javascript:") to execute messages. */ /** Uses webView.loadUrl("javascript:") to execute messages. */
public static class LoadUrlBridgeMode implements BridgeMode { public class LoadUrlBridgeMode implements BridgeMode {
private CordovaWebView webView; public void onNativeToJsMessageAvailable() {
public LoadUrlBridgeMode(CordovaWebView webView) { webView.loadUrlNow("javascript:" + popAll());
this.webView = webView;
}
public void onNativeToJsMessageAvailable(NativeToJsMessageQueue queue) {
webView.loadUrlNow("javascript:" + queue.popAll());
} }
} }
/** Uses online/offline events to tell the JS when to poll for messages. */
public class OnlineEventsBridgeMode implements BridgeMode {
boolean online = true;
final Runnable runnable = new Runnable() {
@Override
public void run() {
if (!queue.isEmpty()) {
online = !online;
webView.setNetworkAvailable(online);
}
}
};
public void onNativeToJsMessageAvailable() {
cordova.getActivity().runOnUiThread(runnable);
}
}
} }