The Google Android team released the Android 2.3 ("Gingerbread") SDK two days ago, to much fanfare. This has sent the tech blogging world into a publishing frenzy, as it usually does. However, a potentially disastrous bug has surfaced that could crash literally thousands of apps in the Android Market immediately after opening the app.
The problem is described succintly here:
http://code.google.com/p/android/issues/detail?id=12987
In short:
Many apps show all or part of their UI with embedded WebViews that can render HTML.
Those WebViews make use of a great feature that bridges JavaScript (in the HTML) to the native Java code that "surrounds" the WebView.
This bridge is completely broken in Android 2.3. Trying to make even a basic call breaks the WebView immediately and crashes the app.
I believe members of the Android team are aware of the problem, and from early reports, it does not affect the Nexus S (the first Android 2.3 phone). This doesn't really help those of us working against the emulator, however.
Here is a simple solution to work around this.
1.) In onCreate, check to see if the bridge is broken, and add the JavaScript interface only if not broken.
// Determine if JavaScript interface is broken. // For now, until we have further clarification from the Android team, // use version number. try { if ("2.3".equals(Build.VERSION.RELEASE)) { javascriptInterfaceBroken = true; } } catch (Exception e) { // Ignore, and assume user javascript interface is working correctly. } // Add javascript interface only if it's not broken if (!javascriptInterfaceBroken) { webView.addJavascriptInterface(this, "jshandler"); }
@Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); finishLoading(); // If running on 2.3, send javascript to the WebView to handle the function(s) // we used to use in the Javascript-to-Java bridge. if (javascriptInterfaceBroken) { String handleGingerbreadStupidity= "javascript:function openQuestion(id) { window.location='http://jshandler:openQuestion:'+id; }; " + "javascript: function handler() { this.openQuestion=openQuestion; }; " + "javascript: var jshandler = new handler();"; view.loadUrl(handleGingerbreadStupidity); } }
"javascript:function openQuestion(id) { window.location='http://jshandler:openQuestion:'+id; }; "
@Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if (javascriptInterfaceBroken) { if (url.contains("jshandler")) { // We override URL handling to parse out the function and its parameter. // TODO: this code can only handle a single parameter. Need to generalize it for multiple. // Parse out the function and its parameter from the URL. StringTokenizer st = new StringTokenizer(url, ":"); st.nextToken(); // remove the 'http:' portion st.nextToken(); // remove the '//jshandler' portion String function = st.nextToken(); String parameter = st.nextToken(); // Now, invoke the local function with reflection try { if (sMethod == null) { sMethod = MyActivity.class.getMethod(function, new Class[] { String.class }); } sMethod.invoke(MyActivity.this, parameter); } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } return true; } return false; }