the art of building bridges for android hybrid apps
TRANSCRIPT
The Art of Building Bridges
Android WebView Bridge in Depth
Bartłomiej Pisulak
Three types of Mobile Apps
Market Share
According to Gartner more than 50% of mobile apps deployed will be hybrid
ArchitectureIt’s all about WebView…
So what’s the difference?
The Bridge
Bridge between Native and JS
JS -‐> Native Native -‐> JS
JS_ObjectJavaScript -‐> Java
public class WebAppInterface { Context mContext;
/** Instantiate the interface and set the context */ WebAppInterface(Context c) { mContext = c; }
/** Show a toast from the web page */ @JavascriptInterface public void showToast(String toast) { Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show(); } }
Let’s build an interface:
JS_ObjectJavaScript -‐> Java
JS WebView
Quite good performance Use native bridging suggested by Google
Error prone
<script type="text/javascript"> function showAndroidToast(toast) { Android.showToast(toast); } </script>
WebSettings settings = webView.getSettings(); settings.setJavaScriptEnabled(true);
webView.addJavascriptInterface(new WebAppInterface(this), "Android");
Prompt JavaScript -‐> Java
JS WebChromeClient
Doesn’t crash the emulator It’s very slow
Overrides default prompt behaviour in WebView Criticised as antipattern
<script type="text/javascript"> prompt("toast", "This is Toast message"); </script> @Override
public boolean onJsPrompt (WebView view, String url, String message, String defaultValue, JsPromptResult result) { // message determines action if(message == "toast") { // make toast ... } }
loadUrl() Java -‐> JavaScript
Native JS
Quite fast Breaks inputs behaviour
<script type="text/javascript"> onLocationUpdate(int lat, int lng) { alert("Success: lat=" + lat + " lng: " + lng); } </script>
@Override public void onLocationChanged(Location location) { String lat = Double.toString(location.getLatitude()); String lng = Double.toString(location.getLongitude()); webView.loadUrl("javascript:onLocationUpdate(" + lat + "," + lng + ");"); }
Online Event Java -‐> JavaScript
Native JS
It’s fast Even faster than loadUrl()
Doesn’t break anything (yet) Problem in case of bridging more than one WebView
<script type="text/javascript"> var handleEvent = function() { // get result from native code // using exposed interface }
window.addEventListener("online", handleEvent); window.addEventListener("offline", handleEvent); </script>
boolean flipFlop; // (...) @Override public void informJavaScriptAboutResult() { flipFlop = !flipFlop; webView.setNetworkAvailable(flipFlop); }
Private APIJava -‐> JavaScript
Class webViewClass = WebView.class;
try { Field f = webViewClass.getDeclaredField("mProvider"); f.setAccessible(true); webViewObject = f.get(webView); webViewClass = webViewObject.getClass(); } catch (Throwable e) { // ... } 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) { // ... }
Be careful while using reflection…
Private APIJava -‐> JavaScript
Native JS
Super fast Not supported officially Works on Android 3.2+ May break in the future
<script type="text/javascript"> onLocationUpdate(int lat, int lng) { alert("Success: lat=" + lat + " lng: " + lng); } </script>
try { String js = "onLocationUpdate(" + lat + "," + lng + ");"; Message execJsMessage = Message.obtain(null, EXECUTE_JS, js); sendMessageMethod.invoke(webViewCore, execJsMessage); } catch (Throwable e) { // ... }
To Sum Up
Hybrid is the future
Choose proper bridge