Tutorial: How to Safely Implement Hybrid HTML5 Android Applications

Published Nov 03, 2014Last updated Jun 21, 2017

As the need for mobile applications continues to grow, the utility of hybrid HTML5-Native development reaches new heights. In this short tutorial we will discover how to integrate web applications into our native Android applications, including how to create a secure JavaScript-Java bridge.

Login Example

In this short tutorial we will create an example login application. The login view and basic logic will be created in HTML, CSS, and JavaScript while the overall application and certain UI elements will be created in XML and Java Android programming. In the end we will get something that looks like this

HTML5 Aspect of the Hybrid application

For this login sample we will create a small web application that simulates a login operation. First we need to create a www folder for storing our web assets. Then let us create an index.html in that www folder and two more folders css and js so that we have the following:

Now we will create a simple html form so that our index.html looks like:

<!DOCTYPE html>

<html>

<head>
     <meta charset="utf-8">

     <title>Safe Hybrid App</title>

     <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />

     <script>

     function login(){}

</script>

</head>

<body>

     <div id="login">

          <h1>LOGIN</h1>

          <form>

  <div class="label">username</div>

  <input type="text" name="username"><br/>

  <div class="label">password</div>

  <input type="password" name="lastname"><br/><br/>

  <button onclick="login()" type="button" class="btn">Login</button>

          </form>

     </div>

</body>

</html>

This simple form has two input fields for a username and password, and once the button has been clicked we call the currently empty login function. The HTML should currently look like

So now let us create a css file called styles.css and add it to our css folder. The css we used for this tutorial is

styles.css
body {

     background-color:rgba(255,255,255,0.1);

}

 

h1 {

     color: #001940;

     text-align: center;

     font-family: "Courier New";

     font-size: 50px;

     padding: 10px;

     margin: 0px;

}

 
#login{

     width:70%;

     margin:0 auto;

     margin-top:10%;

     margin-bottom:2%;

     padding: 2%;

     background-color: #eeeeee;

     border: 5px solid #0047B2;

     border-radius: 15px;
}



.btn {

     width: 50%;

     background-color: rgba(160, 204, 20, 0.5);

     border: 1px solid #0047B2;

     border-radius: 10px;

     margin: 0 auto;

     height: 50px;

     display: block;

}

 

input{

     width: 100%;

     background-color: rgba(12, 158, 232, 0.1);

     border: 1px solid #0047B2;

     border-radius: 10px;

     height: 40px;

     text-align: center;

     margin-top: 10px;

     margin-bottom: 10px;

}


.label{

     margin-left: auto;

     margin-right: auto;

     width: 100%;

     text-align: center;

     vertical-align: middle;

}

Now to add a link to the css file in the head element of our index.html so that it will stylize our html according to styles.css. Our head element in our index.htmlshould now look like this:

<head>

    <meta charset="utf-8">

    <title>Safe Hybrid App</title>

    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />

<link rel="stylesheet" href="css/styles.css">

    <script>

    function login(){}

</script>

</head>

Let us also now create a file for the custom JavaScript to Java components we will implement shortly. Let us call the file Bridge.js and place it in our js folder and add a link to that file so that the head element in our index.html now looks like:

<head>
    <meta charset="utf-8">

    <title>Safe Hybrid App</title>

    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />

<link rel="stylesheet" href="css/styles.css">

<script src="js/Bridge.js"></script>

    <script>

    function login(){}

</script>

</head>

Once we have finished the initial design for our web application we need to create a project in our preferred IDE (Eclipse or IntelliJ/Android Studio) and then we can integrate this web application into our project structure. For the purposes of techniques used in this tutorial the minimum sdk can be set as low as api 8.

Project Structure

The content for our web application can be loaded from local assets or from remote (web based) assets. Warning: Loading untrusted content from remote assets can risk the security of the users’ device and information. In this example we will choose the more secure option and only load local assets.

We are going to store our web application in a www folder inside of a local assets folder. In order to conform to development standards, the final structure should be as follows for

An Eclipse based project: make sure to create an assets folder (if it does not already exist) on the same level as the src and res folders for your project. Then create a www folder inside of that assets folder and copy the files we previously created into that directory. Your final result should look like the image below.

An IntelliJ or Android Studio project: create the assets folder on the same level as the java and res folders (src/main/). Then create a www folder inside of that assets folder and copy the files we previously created into that directory. When you have finished it should resemble the image below.

WebView: Android Web Container

Now that we have integrated the web application assets into our project structure, we will need to load it into our Android application. The WebView component in Android will allow us to not only load this local web application but also to control and manipulate it.

Now create an xml for the activity (if it does not already exist) which will host our web application. The xml will simply contain a WebView and a ProgressBar for showing a simulation of an authentication from our web application. The final result should look something like the following

activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

     >


    <WebView

        android:id="@+id/wvPortal"

        android:layout_width="match_parent"

        android:layout_height="match_parent"

        android:layout_alignParentLeft="true"

        android:layout_centerVertical="true"

        android:background="@android:color/transparent"

         />


    <ProgressBar

        android:id="@+id/pbLoading"

        style="?android:attr/progressBarStyleLarge"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_centerHorizontal="true"

        android:visibility="gone"

        android:layout_centerVertical="true" />


</RelativeLayout>

Then we will create our main activity (if it does not already exist) which will house our web application and host the hybrid bridge and initialize variables for our WebView and our ProgressBar.

MainActivity.java
public class MainActivity extends ActionBarActivity {

     /**CONSTANTS**/

     private static final String TAG = MainActivity.class.getCanonicalName();

     /**MEMBERS**/

     private WebView mWebView;

     private ProgressBar mLoading;


     protected void onCreate(Bundle savedInstanceState) {

          super.onCreate(savedInstanceState);

          setContentView(R.layout.activity_main);

          mWebView = (WebView)findViewById(R.id.wvPortal);

          mLoading = (ProgressBar) findViewById(R.id.pbLoading);

     }

}

Init Web Application

Now that we have our WebView available to us in our Android application, we can initialize our web application. In order to load our web application from our local assets we will utilize the WebView method loadUrl(String url) and we will also enable JavaScript in our WebView, which is disabled by default.

protected void onCreate(Bundle savedInstanceState) {

          super.onCreate(savedInstanceState);

          setContentView(R.layout.activity_main);

          mWebView = (WebView)findViewById(R.id.wvPortal);

          mLoading = (ProgressBar) findViewById(R.id.pbLoading);

          mWebView.loadUrl("file:///android_asset/www/index.html");

          WebSettings mWebSettings = mWebView.getSettings();

          mWebSettings.setJavaScriptEnabled(true);
       
     }

We should now have the desired UI in our Android application and now to connect our web application with our native Android code.

JavaScript to Java Communication

A method for injecting Java objects into a WebView is the addJavascriptInterface method of the WebView. Due to its security risks, especially on devices running Android earlier than 4.2, we will be utilizing a slightly different technique in this tutorial. If you do decide to utilize this method for JavaScript to Java communication, please ensure that you have reviewed all of the security risks and vulnerabilities before implementing.

In order to retrieve messages from our web application running in our WebView, we will override the JavaScript prompt mechanism by overriding the chrome client for our WebView. We will do this by setting a new client through setWebChromeClient(). We will define our client as an internal private class in our MainActivity.Java as below (compile errors will be explained in the following directions.)

private class BridgeWCClient extends WebChromeClient{

     @Override

     public boolean onJsPrompt(WebView view, String url, String title,

  String message, JsPromptResult result) {

          if(title.equals(BRIDGE_KEY)){

  JSONObject commandJSON = null;

  try{

       commandJSON = new JSONObject(message);

       processCommand(commandJSON);

  }

  catch(JSONException ex){

       //Received an invalid json

       Log.e(TAG, "Invalid JSON: " + ex.getMessage());

       result.confirm();

       return true;

  }

  result.confirm();

  return true;

          }

          else{

  return false;

          }   

     }

}

In our custom WebChromeClient we will override the function for all JSPrompts sent from our JavaScript and for any that have a title equal to BRIDGE_KEY (define this string key in your Java and JavaScript and make sure to obfuscate your code for added security) then it will be processed as a command for us to run in our Java Code (any without this title will be treated as normal JSPrompts). We will process the code as a JSONObject for this tutorial in the following fashion.

private void processCommand(JSONObject commandJSON)

  throws JSONException{

     String command = commandJSON.getString("method");

     if("login".equals(command)){

          int state = commandJSON.getInt("state");

          if(state == 0){

  MainActivity.this.runOnUiThread(new Runnable() {

       @Override

       public void run() {

            mLoading.setVisibility(View.VISIBLE);

       }

  });

          }

          else if(state == 1){

  MainActivity.this.runOnUiThread(new Runnable() {

       @Override

       public void run() {

            mLoading.setVisibility(View.GONE);

       }

  });

          }

     }

}

This code allows us to show a loading ProgressBar spinner when JavaScript sends a login command with a state of 0, and to hide the ProgressBar when the JavaScript sends a “login” command with a state of 1. Now we need to add this as our WebChromeClient by adding to the end of our onCreate:

protected void onCreate(Bundle savedInstanceState) {

     super.onCreate(savedInstanceState);

…

mWebView.setWebChromeClient(new BridgeWCClient());

}

Now to add the corresponding JavaScript code to complete our JavaScript to Java bridge to our previously created Bridge.js (should be in www/js folder.)

var Bridge = {};

Bridge.login = function(state){

     //build json string

     var message = {method:"login", state:state}

     prompt(BRIDGE_KEY, JSON.stringify(message));

}

Now we can add to our original login callback the following code to show the ProgressBar in our Java code and then hide it after a 3 second delay to simulate some other actions.

function login(){

     Bridge.login(0);

     //simulate some login operations

     window.setTimeout(

          function(){

  Bridge.login(1);}, 3000);

} 

Now once we run our updated application, we can press the login button and the ProgressBar should be shown for 3 seconds

Java to JavaScript Communication

For the final aspect of our bridge we will enable communication from our Java code to our JavaScript code running in our web application. This can be simply done by utilizing the same loadUrl call used to load the web application itself. In order to call a specific JavaScript function, we simply add the prefix “javascript:” to our loadUrl String argument in order to call a function instead of loading new web content.

First, let us add a callback function to our Bridge.js, where it will receive a result from our Java code and then if the login succeeded show an alert. Our Bridge.js should now look like:

var Bridge = {};


Bridge.login = function(state){

     //build json string

     var message = {method:"login", state:state}

     prompt("bridge_key", JSON.stringify(message));

}


Bridge.callBack = function(result){

     if(result.success){

          alert("login success");

     }

}

For this tutorial we will send a message to our JavaScript when we have hidden the ProgressBar by adding to our processCommand method’s else statement (state == 1) so that it reads:

…
else if(state == 1){

MainActivity.this.runOnUiThread(new Runnable() {

          @Override

  public void run() {

       mLoading.setVisibility(View.GONE);

       mWebView.loadUrl("javascript:Bridge.callBack({success:true, message:\"logged in\"})");

  }

     });

}
…

And with that addition we should now see an alert message presented when the Java code finishes hiding the ProgressBar. We can now communicate between our Java code and the JavaScript application running in our WebView, and we have effectively created a secure HTML5 hybrid application including a secure bridge.

More Advanced Topics

This tutorial has only touched on the topic of creating HTML5 hybrid applications in Android and there are many more components and capabilities that can be utilized to advance the features of a hybrid application (for instance the ability to control url loading components by overriding the shouldOverrideUrlLoading method of WebViewClient, or the ability to remotely debug your application by enabling debugging through setWebContentsDebuggingEnabled(true)). There are also platforms like PhoneGap that offer full featured capabilities for developing HTML5 hybrid applications.

Discover and read more posts from Michael Hantler
get started
Enjoy this post?

Leave a like and comment for Michael

9Replies
Julia Y
3 months ago

Also you should read article How to convert website to Android app - optimal ways to follow - https://www.cleveroad.com/blog/how-to-convert-website-to-android-app--optimal-ways-to-follow In my opinion it’s one of the fullest article about website converting

Mrinalini
10 months ago

I don’t understand where to define BRIDGE_KEY, because of it app is not running properly.
Whole tutorial is very useful, thank you.

insigniff
a year ago

I get the error “cannot find symbol BRIDGE_KEY” in the java file. Does anyone know to resolve this?
(I’ve tried declaring it as a private string, but it didn’t work…)

Navneet k soni
a year ago

simple that if your project require this bridge action then only make it done as its not useful for all apps its just a name of a js file

Show more replies

Get curated posts in your inbox

Read more posts to become a better developer