Friday 2 August 2013

How to make Android Webview support websocket

Background:

This comes from a chat project designing. Recently, someone (may) invite me to do a chat project, since it should execute on both web and android platform. I decided to use websocket (why applying websocket you can read http://en.wikipedia.org/wiki/WebSocket) to solve this problem, I think it would be make the problem simple as I can write java-script once, while it can execute on both platforms.

But after I read some document, I find it is pity that the android default Web browser do not support the websocket!!! And so the internal Webview is also do not support this feature. (http://caniuse.com/#feat=websockets) I think it is popular for android default webview class do not support many features and that features is just you need or you want to make. This article will show how to add an unsupported feature in default  webview for android app.

Principle:

About Webview class: (this part comes from Android develop reference)

 By default, a WebView provides no browser-like widgets, does not enable JavaScript and web page errors are ignored. If your goal is only to display some HTML as a part of your UI, this is probably fine; the user won't need to interact with the web page beyond reading it, and the web page won't need to interact with the user. If you actually want a full-blown web browser, then you probably want to invoke the Browser application with a URL Intent rather than show it with a WebView.

A WebView has several customization points where you can add your own behavior. These are:
  • Creating and setting a WebChromeClient subclass. This class is called when something that might impact a browser UI happens, for instance, progress updates and JavaScript alerts are sent here (see Debugging Tasks).
  • Creating and setting a WebViewClient subclass. It will be called when things happen that impact the rendering of the content, eg, errors or form submissions. You can also intercept URL loading here (via shouldOverrideUrlLoading()).
  • Modifying the WebSettings, such as enabling JavaScript with setJavaScriptEnabled().

Key points:

  • Injecting Java objects into the WebView using the addJavascriptInterface(Object, String) method. This method allows you to inject Java objects into a page's JavaScript context, so that they can be accessed by JavaScript in the page. (WebView can inject javascriptinterface to achieve js to java invocation. hehe, we can use this interface to support our defined javascript features. ;-)
This is also how  PhoneGap execute their many js features. Here I will bring some PhoneGap(https://github.com/anismiles/websocket-android-phonegap) code as example.

Finally, my main execution is:
   wv.addJavascriptInterface(new WebSocketFactory(wv), "WebSocketFactory")
(If your android api above API 17, you should also add  @JavascriptInterface  in every public methods which your js script can invoke, the details should be see in api)
and the js code is:
(function() {
// window object
var global = window;
 
// WebSocket Object. All listener methods are cleaned up!
var WebSocket = global.WebSocket = function(url) {
 // get a new websocket object from factory (check com.strumsoft.websocket.WebSocketFactory.java)
 this.socket = WebSocketFactory.getInstance(url);
 // store in registry
 if(this.socket) {
  WebSocket.store[this.socket.getId()] = this;
 } else {
  throw new Error('Websocket instantiation failed! Address might be wrong.');
 }
};
...
} 
in this way, you can simple do:

var websocket = new WebSocket(wsUri);
(the ws server is use the example on: http://www.websocket.org/echo.html)
The final results:

source code:

I put source code  on my new github: https://github.com/thinksource/vw_websocket
the apk: https://github.com/thinksource/vw_websocket/blob/master/bin/vw_websocket.apk
(I wrote for my i9100 android 4.0.3, so only above this version the app can work.)