Sunday, April 27, 2014

Hybrid Android Applications w/WebView

Many Android applications look entirely native but have at least a portion of the UI implemented using a WebView to display HTML and JavaScript.  (In this context, "native" means a UI created in the traditional Java/XML approach and not the NDK).

This post illustrates the use of WebView along w/WebViewClient and WebChromeClient.  I have created a small demonstration application ("AndroidWebView") which is available on github.

AndroidWebView has four tabs to demonstrate different use cases (screenshots below).

The first tab ("About") illustrates the simple use case of reading a locally stored HTML file into a WebView.  Using HTML is a frequent solution for non-interactive, wordy content such as T&C, EULA or perhaps application help pages.  As you can see AboutFragment.java was extremely simple to implement.

"About" tab selection
"Client" tab selection
The second tab ("Client") reads a remote web page and manages interaction w/the remote server using a WebViewClient.  Again ClientFragment,java is small and simple.

DemoWebViewClient.java reacts to events such as "page started", "page finished", "authorization requests", "errors", etc.  I have implemented the interesting methods to write log messages so we can view the progress using "adb logcat".

The received web page (above, on the right) and there are four navigation buttons (i.e. "Contact", "News", "Products", "Services") which are implemented as HTTP HREF(s).  Within DemoWebViewClient.shouldOverrideUrlLoading() I have arranged for the "News" button to invoke NewsDialogFragment.java rather than perform the usual HTTP GET.  This illustrates the use case of HTML content invoking an Android View.

It is useful to view the log output ("adb logcat") when viewing the "Client" tab.  When reviewing the log, you will see the various requests necessary to render the page.  This illustrates that you can detect and perhaps alter the content, or react to certain events.  The log output should look similar to this:

DemoWebViewClient(20221): interceptRequest:http://www.digiburo.com/mobi/db_index.html
DemoWebViewClient(20221): pageStarted:http://www.digiburo.com/mobi/db_index.html
DemoWebViewClient(20221): load:http://www.digiburo.com/mobi/db_index.html
DemoWebViewClient(20221): interceptRequest:http://www.digiburo.com/css/stylesheet2.css
DemoWebViewClient(20221): load:http://www.digiburo.com/css/stylesheet2.css
DemoWebViewClient(20221): interceptRequest:http://www.google-analytics.com/ga.js
DemoWebViewClient(20221): load:http://www.google-analytics.com/ga.js
DemoWebViewClient(20221): interceptRequest:http://www.digiburo.com/grafix/home_logo.png
DemoWebViewClient(20221): load:http://www.digiburo.com/grafix/home_logo.png
DemoWebViewClient(20221): interceptRequest:http://www.digiburo.com/grafix/canvas1.png
DemoWebViewClient(20221): load:http://www.digiburo.com/grafix/canvas1.png
DemoWebViewClient(20221): interceptRequest:http://www.google-analytics.com/__utm.gif? //deleted
DemoWebViewClient(20221): load:http://www.google-analytics.com/__utm.gif? //deleted
DemoWebViewClient(20221): pageFinished:http://www.digiburo.com/mobi/db_index.html







The third tab ("JS") illustrates interaction w/JavaScript.

JavaScriptFragment.java creates a WebChromeClient  DemoWebChromeClient.java which enables support like connecting the JavaScript console to write via the Android log or report loading progress.  If you wish to use JavaScript Alert() rather than an Android View, you will need to use WebChromeClient.onJsAlert() (which I have implemented for this purpose).  The "generateAlert" button produces a JavaScript alert.

JavaScriptFragment.java relies upon DemoJavaScript.java to expose Java methods which can be invoked from JavaScript.  I have implemented simple examples to illustrate using Android logging or return a String to JavaScript.  The "@JavascriptInterface" is limited in many ways regarding parameters and return results: primitives and Strings usually work but you will want to experiment before promising complicated arguments.  Note that generatePrimes() returns a String which contains a JSON formatted array, and this is a successful approach.  Pressing "generateLog" will cause an Android log message to be written (visible via "adb logcat").  "generatePrime" invokes a Java based prime number generator and returns the results as a JSON formatted String.

What about the use case of Java invoking JavaScript?  This is also possible and pressing "Invoke JavaScript" shows how.  "Invoke JavaScript" is a standard Android button which invokes WebView.loadUrl() for bridge.html which writes a log entry back to Java.

The fourth tab ("Simple") SimpleFragment.java demonstrates the trivial use case of reading a remote file without WebViewClient or WebChromeClient.  

2 comments:

  1. This comment has been removed by a blog administrator.

    ReplyDelete
  2. This comment has been removed by a blog administrator.

    ReplyDelete