Monday, November 2, 2015

Android HTML5 App, how really it's?

In this post I just want to share HTML5 development, it's not using framework like apache cordova or something else. I just want to share how to communicate or interfacing between javascript and android java native code.

When I wrote this post, I use Android Studio Version 1.4.

For complete project you can download from this link  https://www.dropbox.com/s/02mor7925l1l3bg/MyApplication.zip?dl=0

Create new project with no activity using android studio



Edit AndroidManifest.xml to be like this,
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.amru.myapplication" >

    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme" >
        <activity android:name=".MainActivity" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>

</manifest>

Then add android assets folder inside main, same level with android res folder,
Create assets folder with some folder inside assets folder:
  • html (for html page)
  • css (for css style)
  • js (for javascript function)



Create HTMLUtil class for read html page to parse .html page.



Then put this line into HTMLUtil  class
package com.example.amru.myapplication;

import android.content.Context;
import android.content.res.AssetManager;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * Created by amru on 10/28/15.
 */
public class HTMLUtil {
    public static String ASSET_DIR = "file:///android_asset/";
    public static String HTML_PREFIX = ".html";
    public static String HTML_DIR = "html/";
    public static String HTML_MIME = "text/html";
    public static String UTF8_ENCODING = "UTF-8";

    private Context _context;

    public HTMLUtil(Context context){
        this._context = context;
    }

    // filename without .html prefix
    public String readHTML(String filename){
        StringBuilder html = new StringBuilder();
        try {
            AssetManager assetManager = this._context.getAssets();
            InputStream inputStream = assetManager.open(HTMLUtil.HTML_DIR + filename + HTMLUtil.HTML_PREFIX);
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));

            String line;
            while((line = bufferedReader.readLine()) != null){
                html.append(line);
            }
        }catch (IOException e){
            e.printStackTrace();
        }

        return html.toString();
    }
}

Create JsInterface class, and put this line
package com.example.amru.myapplication;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.webkit.JavascriptInterface;

/**
 * Created by amru on 10/28/15.
 */
public class JsInterface {
    private Context _context;

    public JsInterface(Context context){
        this._context = context;
    }

    // don't forget user @JavascriptInterface if you want call it from javascript
    // this is mandatory start from api 16
    @JavascriptInterface
    public void sayHello(){
        AlertDialog.Builder alert = new AlertDialog.Builder(this._context);
        alert.setTitle("halo");
        alert.setMessage("this from native dialog...");
        alert.setNegativeButton("Close", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        });
        alert.show();
    }

    // parameter should be in json
    // func is function name
    // call javascript function from native activity
    // need call from post runnable, make sure it's thread safe :)
    public static void callJS(String func, String param){
        final StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("javascript:");
        stringBuilder.append(func);
        stringBuilder.append("(");
        stringBuilder.append(param);
        stringBuilder.append(")");
        MainActivity.WEB_VIEW.post(new Runnable() {
            @Override
            public void run() {
                MainActivity.WEB_VIEW.loadUrl(stringBuilder.toString());
            }
        });
    }
}

On MainActivity class put this line,
package com.example.amru.myapplication;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.webkit.WebChromeClient;
import android.webkit.WebView;

public class MainActivity extends Activity {

    private HTMLUtil _mHTMLUtil;
    public static WebView WEB_VIEW;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        this._mHTMLUtil = new HTMLUtil(this);

        MainActivity.WEB_VIEW = new WebView(this);
        MainActivity.WEB_VIEW.setWebChromeClient(new WebChromeClient());

        MainActivity.WEB_VIEW.getSettings().setJavaScriptEnabled(true);
        MainActivity.WEB_VIEW.getSettings().setAllowContentAccess(true);
        MainActivity.WEB_VIEW.getSettings().setAllowFileAccess(true);
        MainActivity.WEB_VIEW.getSettings().setBuiltInZoomControls(false);
        MainActivity.WEB_VIEW.setWebChromeClient(new WebChromeClient());
        MainActivity.WEB_VIEW.loadDataWithBaseURL(HTMLUtil.ASSET_DIR + HTMLUtil.HTML_DIR,
                this._mHTMLUtil.readHTML("page"),
                HTMLUtil.HTML_MIME,
                HTMLUtil.UTF8_ENCODING,
                null);
        setContentView(MainActivity.WEB_VIEW);

        // register JsInterface class as native interface
        // we can call via NativeInterface.method_name
        MainActivity.WEB_VIEW.addJavascriptInterface(new JsInterface(this), "NativeInterface");


        // let's try to call javascript from native
        JsInterface.callJS("callMeJava", "");
    }
}


For testing the HTML5 App, we need to create some html pages and javascript code. Create page.html, fn.js inside html folder and js folder.

page.html
<html>
    <head>
        <script type="text/javascript" src="file:///android_asset/js/fn.js"></script>
    </head>
    <body>
        <button id="mybutton" onclick="testAlert();">click !</button>
    </body>
</html>

fn.js
// call native interface function
function testAlert(){
    NativeInterface.sayHello();
}

// this will call from java native
function callMeJava(){
    alert('call js function from java native.');
}

Then, we can start to test the app prototype. When app loaded it will trigger callMeJava function from fn.js. The method call from oncreate activity just test case only.

through this line on MainActivity
// let's try to call javascript from native
JsInterface.callJS("callMeJava", "");


Let's test call from javascript to native,

Through this line (JsInterface class)
// call native interface function
function testAlert(){
    NativeInterface.sayHello();
}

// this will call from java native
function callMeJava(){
    alert('call js function from java native.');
}



Thanks.

No comments:

Post a Comment