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 2015/02/06 20:06:23 UTC

[3/3] android commit: Separate the registering of BridgeModes from NativeToJsMessageQueue

Separate the registering of BridgeModes from NativeToJsMessageQueue

This makes the class usable no matter how a webview's bridge is
implemented under-the-hood.
This also deletes the PrivateApi bridge mode, which has never been a
good idea to use, and which we should replace with a Lollipop
"evaluateJavascript"-based bridge.


Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/4cb64580
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/4cb64580
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/4cb64580

Branch: refs/heads/master
Commit: 4cb64580fd5d3e3c885847ef8d266aa9eaf87179
Parents: 5b2fa12
Author: Andrew Grieve <ag...@chromium.org>
Authored: Fri Feb 6 14:03:10 2015 -0500
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Fri Feb 6 14:03:10 2015 -0500

----------------------------------------------------------------------
 .../src/org/apache/cordova/AndroidWebView.java  |  20 +-
 .../apache/cordova/NativeToJsMessageQueue.java  | 197 +++++++------------
 2 files changed, 90 insertions(+), 127 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/4cb64580/framework/src/org/apache/cordova/AndroidWebView.java
----------------------------------------------------------------------
diff --git a/framework/src/org/apache/cordova/AndroidWebView.java b/framework/src/org/apache/cordova/AndroidWebView.java
index d0b6d05..6ad1af6 100755
--- a/framework/src/org/apache/cordova/AndroidWebView.java
+++ b/framework/src/org/apache/cordova/AndroidWebView.java
@@ -113,7 +113,7 @@ public class AndroidWebView extends WebView implements CordovaWebView {
 
     // Use two-phase init so that the control will work with XML layouts.
     @Override
-    public void init(CordovaInterface cordova, List<PluginEntry> pluginEntries,
+    public void init(final CordovaInterface cordova, List<PluginEntry> pluginEntries,
             Whitelist internalWhitelist, Whitelist externalWhitelist,
             CordovaPreferences preferences) {
         if (this.cordova != null) {
@@ -127,9 +127,23 @@ public class AndroidWebView extends WebView implements CordovaWebView {
         pluginManager = new PluginManager(this, this.cordova, pluginEntries);
         cookieManager = new AndroidCookieManager(this);
         resourceApi = new CordovaResourceApi(this.getContext(), pluginManager);
-        bridge = new CordovaBridge(pluginManager, new NativeToJsMessageQueue(this, cordova), this.cordova.getActivity().getPackageName());
+        NativeToJsMessageQueue nativeToJsMessageQueue = new NativeToJsMessageQueue();
+        nativeToJsMessageQueue.addBridgeMode(new NativeToJsMessageQueue.NoOpBridgeMode());
+        nativeToJsMessageQueue.addBridgeMode(new NativeToJsMessageQueue.LoadUrlBridgeMode(this, cordova));
+        nativeToJsMessageQueue.addBridgeMode(new NativeToJsMessageQueue.OnlineEventsBridgeMode(new NativeToJsMessageQueue.OnlineEventsBridgeMode.OnlineEventsBridgeModeDelegate() {
+            @Override
+            public void setNetworkAvailable(boolean value) {
+                AndroidWebView.this.setNetworkAvailable(value);
+            }
+
+            @Override
+            public void runOnUiThread(Runnable r) {
+                cordova.getActivity().runOnUiThread(r);
+            }
+        }));
+        bridge = new CordovaBridge(pluginManager, nativeToJsMessageQueue, this.cordova.getActivity().getPackageName());
         initWebViewSettings();
-        pluginManager.addService(CoreAndroid.PLUGIN_NAME, "org.apache.cordova.CoreAndroid");
+        pluginManager.addService(CoreAndroid.PLUGIN_NAME, CoreAndroid.class.getCanonicalName());
         pluginManager.init();
         
         if (this.viewClient == null) {

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/4cb64580/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 a697060..6f9db66 100755
--- a/framework/src/org/apache/cordova/NativeToJsMessageQueue.java
+++ b/framework/src/org/apache/cordova/NativeToJsMessageQueue.java
@@ -18,16 +18,10 @@
 */
 package org.apache.cordova;
 
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
+import java.util.ArrayList;
 import java.util.LinkedList;
 
-import org.apache.cordova.CordovaInterface;
-import org.apache.cordova.PluginResult;
-
-import android.os.Message;
 import android.util.Log;
-import android.webkit.WebView;
 
 /**
  * Holds the list of messages to be sent to the WebView.
@@ -63,7 +57,7 @@ public class NativeToJsMessageQueue {
     /**
      * The array of listeners that can be used to send messages to JS.
      */
-    private final BridgeMode[] registeredListeners;    
+    private ArrayList<BridgeMode> bridgeModes = new ArrayList<BridgeMode>();
     
     /**
      * When null, the bridge is disabled. This occurs during page transitions.
@@ -72,32 +66,26 @@ public class NativeToJsMessageQueue {
      */
     private BridgeMode activeBridgeMode;
 
-    private final CordovaInterface cordova;
-    private final CordovaWebView webView;
-
-    public NativeToJsMessageQueue(CordovaWebView webView, CordovaInterface cordova) {
-        this.cordova = cordova;
-        this.webView = webView;
-        registeredListeners = new BridgeMode[4];
-        registeredListeners[0] = new PollingBridgeMode();
-        registeredListeners[1] = new LoadUrlBridgeMode();
-        registeredListeners[2] = new OnlineEventsBridgeMode();
-        registeredListeners[3] = new PrivateApiBridgeMode();
-        reset();
+    public void addBridgeMode(BridgeMode bridgeMode) {
+        bridgeModes.add(bridgeMode);
     }
-    
+
     public boolean isBridgeEnabled() {
         return activeBridgeMode != null;
     }
 
+    public boolean isEmpty() {
+        return queue.isEmpty();
+    }
+
     /**
      * Changes the bridge mode.
      */
     public void setBridgeMode(int value) {
-        if (value < -1 || value >= registeredListeners.length) {
+        if (value < -1 || value >= bridgeModes.size()) {
             Log.d(LOG_TAG, "Invalid NativeToJsBridgeMode: " + value);
         } else {
-            BridgeMode newMode = value < 0 ? null : registeredListeners[value];
+            BridgeMode newMode = value < 0 ? null : bridgeModes.get(value);
             if (newMode != activeBridgeMode) {
                 Log.d(LOG_TAG, "Set native->JS mode to " + (newMode == null ? "null" : newMode.getClass().getSimpleName()));
                 synchronized (this) {
@@ -105,7 +93,7 @@ public class NativeToJsMessageQueue {
                     if (newMode != null) {
                         newMode.reset();
                         if (!paused && !queue.isEmpty()) {
-                            newMode.onNativeToJsMessageAvailable();
+                            newMode.onNativeToJsMessageAvailable(this);
                         }
                     }
                 }
@@ -146,7 +134,7 @@ public class NativeToJsMessageQueue {
             if (activeBridgeMode == null) {
                 return null;
             }
-            activeBridgeMode.notifyOfFlush(fromOnlineEvent);
+            activeBridgeMode.notifyOfFlush(this, fromOnlineEvent);
             if (queue.isEmpty()) {
                 return null;
             }
@@ -179,7 +167,7 @@ public class NativeToJsMessageQueue {
     /**
      * Same as popAndEncode(), except encodes in a form that can be executed as JS.
      */
-    private String popAndEncodeAsJs() {
+    public String popAndEncodeAsJs() {
         synchronized (this) {
             int length = queue.size();
             if (length == 0) {
@@ -260,7 +248,7 @@ public class NativeToJsMessageQueue {
             }
             queue.add(message);
             if (!paused) {
-                activeBridgeMode.onNativeToJsMessageAvailable();
+                activeBridgeMode.onNativeToJsMessageAvailable(this);
             }
         }
     }
@@ -275,133 +263,94 @@ public class NativeToJsMessageQueue {
         if (!value) {
             synchronized (this) {
                 if (!queue.isEmpty() && activeBridgeMode != null) {
-                    activeBridgeMode.onNativeToJsMessageAvailable();
+                    activeBridgeMode.onNativeToJsMessageAvailable(this);
                 }
             }   
         }
     }
 
-    private abstract class BridgeMode {
-        abstract void onNativeToJsMessageAvailable();
-        void notifyOfFlush(boolean fromOnlineEvent) {}
+    public static abstract class BridgeMode {
+        abstract void onNativeToJsMessageAvailable(NativeToJsMessageQueue queue);
+        void notifyOfFlush(NativeToJsMessageQueue queue, boolean fromOnlineEvent) {}
         void reset() {}
     }
 
     /** Uses JS polls for messages on a timer.. */
-    private class PollingBridgeMode extends BridgeMode {
-        @Override void onNativeToJsMessageAvailable() {
+    public static class NoOpBridgeMode extends BridgeMode {
+        @Override public void onNativeToJsMessageAvailable(NativeToJsMessageQueue queue) {
         }
     }
 
     /** Uses webView.loadUrl("javascript:") to execute messages. */
-    private class LoadUrlBridgeMode extends BridgeMode {
-        final Runnable runnable = new Runnable() {
-            public void run() {
-                String js = popAndEncodeAsJs();
-                if (js != null) {
-                    webView.loadUrlIntoView("javascript:" + js, false);
+    public static class LoadUrlBridgeMode extends BridgeMode {
+        private final CordovaWebView webView;
+        private final CordovaInterface cordova;
+
+        public LoadUrlBridgeMode(CordovaWebView webView, CordovaInterface cordova) {
+            this.webView = webView;
+            this.cordova = cordova;
+        }
+
+        @Override
+        public void onNativeToJsMessageAvailable(final NativeToJsMessageQueue queue) {
+            cordova.getActivity().runOnUiThread(new Runnable() {
+                public void run() {
+                    String js = queue.popAndEncodeAsJs();
+                    if (js != null) {
+                        webView.loadUrl("javascript:" + js);
+                    }
                 }
-            }
-        };
-        
-        @Override void onNativeToJsMessageAvailable() {
-            cordova.getActivity().runOnUiThread(runnable);
+            });
         }
     }
 
     /** Uses online/offline events to tell the JS when to poll for messages. */
-    private class OnlineEventsBridgeMode extends BridgeMode {
+    public static class OnlineEventsBridgeMode extends BridgeMode {
+        private final OnlineEventsBridgeModeDelegate delegate;
         private boolean online;
         private boolean ignoreNextFlush;
 
-        final Runnable toggleNetworkRunnable = new Runnable() {
-            public void run() {
-                if (!queue.isEmpty()) {
-                    ignoreNextFlush = false;
-                    webView.setNetworkAvailable(online);
+        public interface OnlineEventsBridgeModeDelegate {
+            void setNetworkAvailable(boolean value);
+            void runOnUiThread(Runnable r);
+        }
+
+        public OnlineEventsBridgeMode(OnlineEventsBridgeModeDelegate delegate) {
+            this.delegate = delegate;
+        }
+
+        @Override
+        public void reset() {
+            delegate.runOnUiThread(new Runnable() {
+                public void run() {
+                    online = false;
+                    // If the following call triggers a notifyOfFlush, then ignore it.
+                    ignoreNextFlush = true;
+                    delegate.setNetworkAvailable(true);
                 }
-            }
-        };
-        final Runnable resetNetworkRunnable = new Runnable() {
-            public void run() {
-                online = false;
-                // If the following call triggers a notifyOfFlush, then ignore it.
-                ignoreNextFlush = true;
-                webView.setNetworkAvailable(true);
-            }
-        };
-        @Override void reset() {
-            cordova.getActivity().runOnUiThread(resetNetworkRunnable);
+            });
         }
-        @Override void onNativeToJsMessageAvailable() {
-            cordova.getActivity().runOnUiThread(toggleNetworkRunnable);
+
+        @Override
+        public void onNativeToJsMessageAvailable(final NativeToJsMessageQueue queue) {
+            delegate.runOnUiThread(new Runnable() {
+                public void run() {
+                    if (!queue.isEmpty()) {
+                        ignoreNextFlush = false;
+                        delegate.setNetworkAvailable(online);
+                    }
+                }
+            });
         }
         // Track when online/offline events are fired so that we don't fire excess events.
-        @Override void notifyOfFlush(boolean fromOnlineEvent) {
+        @Override
+        public void notifyOfFlush(final NativeToJsMessageQueue queue, boolean fromOnlineEvent) {
             if (fromOnlineEvent && !ignoreNextFlush) {
                 online = !online;
             }
         }
     }
-    
-    /**
-     * Uses Java reflection to access an API that lets us eval JS.
-     * Requires Android 3.2.4 or above. 
-     */
-    private class PrivateApiBridgeMode extends BridgeMode {
-    	// Message added in commit:
-    	// http://omapzoom.org/?p=platform/frameworks/base.git;a=commitdiff;h=9497c5f8c4bc7c47789e5ccde01179abc31ffeb2
-    	// Which first appeared in 3.2.4ish.
-    	private static final int EXECUTE_JS = 194;
-    	
-    	Method sendMessageMethod;
-    	Object webViewCore;
-    	boolean initFailed;
-
-    	@SuppressWarnings("rawtypes")
-    	private void initReflection() {
-        	Object webViewObject = webView;
-    		Class webViewClass = WebView.class;
-        	try {
-    			Field f = webViewClass.getDeclaredField("mProvider");
-    			f.setAccessible(true);
-    			webViewObject = f.get(webView);
-    			webViewClass = webViewObject.getClass();
-        	} catch (Throwable e) {
-        		// mProvider is only required on newer Android releases.
-    		}
-        	
-        	try {
-    			Field f = webViewClass.getDeclaredField("mWebViewCore");
-                f.setAccessible(true);
-    			webViewCore = f.get(webViewObject);
-    			
-    			if (webViewCore != null) {
-    				sendMessageMethod = webViewCore.getClass().getDeclaredMethod("sendMessage", Message.class);
-	    			sendMessageMethod.setAccessible(true);	    			
-    			}
-    		} catch (Throwable e) {
-    			initFailed = true;
-				Log.e(LOG_TAG, "PrivateApiBridgeMode failed to find the expected APIs.", e);
-    		}
-    	}
-    	
-        @Override void onNativeToJsMessageAvailable() {
-        	if (sendMessageMethod == null && !initFailed) {
-        		initReflection();
-        	}
-        	// webViewCore is lazily initialized, and so may not be available right away.
-        	if (sendMessageMethod != null) {
-	        	String js = popAndEncodeAsJs();
-	        	Message execJsMessage = Message.obtain(null, EXECUTE_JS, js);
-				try {
-				    sendMessageMethod.invoke(webViewCore, execJsMessage);
-				} catch (Throwable e) {
-					Log.e(LOG_TAG, "Reflection message bridge failed.", e);
-				}
-        	}
-        }
-    }    
+
     private static class JsMessage {
         final String jsPayloadOrCallbackId;
         final PluginResult pluginResult;


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org