Good Bye, Javascript Interface!

860bf040d996601213e05747dd661c23?s=47 Tomoaki Imai
November 29, 2016

Good Bye, Javascript Interface!

This slide describes how to handle Javascripts thru webview without using Javascript interface on Android

860bf040d996601213e05747dd661c23?s=128

Tomoaki Imai

November 29, 2016
Tweet

Transcript

  1. Good bye, Javascript Interface @Potatotips 11/29/2016 Tomoaki Imai

  2. http://mercan.mercari.com/entry/2016/11/18/110000 twitter: @tomoaki_imai github: tomoima525 ࠓҪ ஐষ Android Engineer @Mercari

    https://github.com/tomoima525/kotoha-slack
  3. WebView in Mercari

  4. Javascript Interface

  5. WebSettings settings = webView.getSettings();
 settings.setJavaScriptEnabled(true); webView.addJavascriptInterface(new JavaScriptInterface(activity), “android”); public class

    JavaScriptInterface { public JavascriptInterface(BaseActivity activity){ … this.activity = activity; } @JavascriptInterface
 public void showMessage(String text) { //Call Native method BaseActivity.showMessage(title, text); } } Javascript Interface basic <input type="button" value="test" onClick=“showMessage(‘Potato’,’This is message!')" /> <script type="text/javascript"> function showMessage(title, text) { android.showMessage(title, text); } </script>
  6. WebSettings settings = webView.getSettings();
 settings.setJavaScriptEnabled(true); webView.addJavascriptInterface(new JavaScriptInterface(activity), “android”); public class

    JavaScriptInterface { public JavascriptInterface(BaseActivity activity){ … this.activity = activity; } @JavascriptInterface
 public void showMessage(String text) { //Call Native method BaseActivity.showMessage(title, text); } } Javascript Interface basic <input type="button" value="test" onClick=“showMessage(‘Potato’,’This is message!')" /> <script type="text/javascript"> function showMessage(title, text) { android.showMessage(title, text); } </script>
  7. • Architecture is a black box https://developer.android.com/guide/webapps/webview.html#UsingJavaScript Problems with Javascript

    Interface
  8. • Architecture is a black box https://developer.android.com/guide/webapps/webview.html#UsingJavaScript • Thus, hard

    to figure out crashes Problems with Javascript Interface
  9. • Architecture is a black box https://developer.android.com/guide/webapps/webview.html#UsingJavaScript • Thus, hard

    to figure out crashes Fatal Exception: android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@42249680 is not valid; is your activity running? at com.kouzoh.mercari.util.WebViewUtil$DefaultJavaScriptInterface $2.run(WebViewUtil.java:484) Problems with Javascript Interface
  10. Problems with Javascript Interface • Code complexity on Front end

    <input type="button" value="Say hello" onClick=“showMessage(‘Potato’,’This is message!')" /> <script type="text/javascript"> function showMessage(title, text) { if(client.android){ android.showMessage(title, text); } else if(client.ios){ showMessageForIOS(title, text) } } function showMessageForIOS(title, text){…} </script>
  11. What can we do?

  12. class ViewController: UIViewController, UIWebViewDelegate { //. . . func webView(webView:

    UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool { if(request.URL!.scheme == "scheme"){ // … pass to native method return false } return true } how iOS do (using UIWebView)
  13. Evaluate url?

  14. Evaluating Url scheme://app/showMessage?title=Potato&text=This%20is%20%Message! Method: showMessage() Argument: title => Potato text

    => This is Message!
  15. Evaluating Url • No black box ( just parse Url!)

    • Has consistency with iOS Url evaluation
  16. Basic Architecture 6SJ.PEFM 8FC4DIFNF)BOEMFS $VTUPN8FC$MJFOU /BUJWFNFUIPE scheme://app/showMessage?title=Potato&text=This%20is%20%Message! method: showMessage arguments:

    title=Potato text=This%20is%20%Message! activity.showMessage("Potato", “This is Message!”); shouldOverrideUrloading() parse call
  17. public class UriModel { private final static String SCHEME =

    “scheme://app“
 String method;
 Map<String, String> params = new HashMap<>();
 
 public UriModel(Uri uri) {
 try { 
 String urlData = URLDecoder.decode( uri.toString().substring(SCHEME.length()), "UTF-8");
 urlData = urlData.replaceFirst("\\?", "¥¥");
 String[] splitUrl = urlData.split("¥¥");
 String method = splitUrl[0];
 this.setMethod(method);
 if (splitUrl.length == 1) return;
 
 Set<String> parameterNames = uri.getQueryParameterNames();
 Iterator<String> parameterIterator = parameterNames.iterator();
 
 while (parameterIterator.hasNext()){
 String key = parameterIterator.next();
 this.getParams().put(key, uri.getQueryParameter(key));
 }
 } catch (UnsupportedEncodingException e) {
 }
 } Uri Model Class QBSTFNFUIPE QBSTFQBSBNT
  18. public class CustomWebViewClient extends WebViewClient {
 
 protected WebActivity activity;


    
 
 public CustomWebViewClient(WebActivity activity) {
 super();
 this.activity = activity;
 
 }
 @Override
 public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
 
 
 
 
 return false;
 } } Custom WebViewClient
  19. public class CustomWebViewClient extends WebViewClient {
 private final static String

    APP_SCHEME = "scheme.*";
 protected WebActivity activity;
 
 
 public CustomWebViewClient(WebActivity activity) {
 super();
 this.activity = activity;
 
 }
 @Override
 public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
 
 
 
 
 return false;
 } } Custom WebViewClient
  20. public class CustomWebViewClient extends WebViewClient {
 private final static String

    APP_SCHEME = "scheme.*";
 protected WebActivity activity;
 WebSchemeHandler schemeHandler;
 
 public CustomWebViewClient(WebActivity activity) {
 super();
 this.activity = activity;
 schemeHandler = new WebSchemeHandler(activity);
 }
 @Override
 public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
 
 
 
 
 return false;
 } } Custom WebViewClient
  21. public class CustomWebViewClient extends WebViewClient {
 private final static String

    APP_SCHEME = "scheme.*";
 protected WebActivity activity;
 WebSchemeHandler schemeHandler;
 
 public CustomWebViewClient(WebActivity activity) {
 super();
 this.activity = activity;
 schemeHandler = new WebSchemeHandler(activity);
 }
 @Override
 public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
 if (request.getUri().getScheme().matches(APP_SCHEME)){
 schemeHandler.handleUri(view, request.getUri());
 return true;
 }
 return false;
 } } Custom WebViewClient
  22. public class WebSchemeHandler { WebActivity activity; public WebSchemeHandler(WebActivity activity) {


    this.activity = activity;
 } public void handleUri(WebView webView, Uri uri) {
 UriModel uriModel = new UriModel(uri);
 switch (uriModel.getMethod()) {
 case “showMessage": activity.showMessage( uriModel.getParams().get(“title”), uriModel.getParams().get(“text”)); . . . Handle Uri
  23. public class WebSchemeHandler { WebActivity activity; public WebSchemeHandler(WebActivity activity) {


    this.activity = activity;
 } void getDeviceInfo(WebView webView, final String funcName) {
 activity.runOnUiThread(() -> {
 if (webView != null) {
 String deviceInfo = activity.getDeviceInfo(); webView.evaluateJavascript(funcName + "('" + deviceInfo + "')", null);
 }
 });
 } Callbacks to Web
  24. Things to keep in mind • Only allow schemes when

    the web page is secure ‣ Only allow secured(≒ owned) websites ‣ Do not redirect or access to external unknown sites
  25. WebView 2.0?? • From iOS 8.0 ‣ UIWebView -> WKWebView

    • From Android 7.0 ‣ Compatibility issue https://developer.android.com/reference/android/webkit/WebView.html #evaluateJavascript(java.lang.String, android.webkit.ValueCallback<java.lang.String>)