Handling Android 2.3 WebView's broken AddJavascriptInterface

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");
}

2.) Create a WebViewClient that passes in a new JavaScript object with the same name as your JavaScript interface object.
@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);
}
}

NOTE: for each of the Javascript-to-Java functions that you use, you will need to add a new Javascript function and pass it in as part of the loadUrl, like the defined below.
"javascript:function openQuestion(id) { window.location='http://jshandler:openQuestion:'+id; }; "


3.) In the same WebViewClient, override the URL handling portion to handle the URLs defined in step 2. After catching the URL, parse out the function and parameters, then use reflection to actually call the method.
@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;
}


With this solution, you only need to make a few Android code changes and do not need to modify server output.

Comments welcome.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值