I didn’t know that the default browser installed on Android is called Webview. I haven’t created any native Android app ever. But I wanted my web app to run on Android using Websockets. Unfortunately, websockets are not implemented in Android Webview.
In modern browsers, HTML5 provides a websocket API:
ws = new WebSocket('ws://' + host + ':'+ port);
ws.onmessage = function(msg){
var data = JSON.parse(msg.data);
ws.send(JSON.stringify({message: 'OK cool!'}));
};
You don’t need to include any JavaScript file in order for this to be valid code in modern browsers (when I saw it for the first time I was like…. “yeah cool but what library are you using?). Well you’d need a Websocket server. I’ve been trying ws for Node. It works very well with express.js and is very simple:
var WebSocketServer = require('ws').Server;
var wss = new WebSocketServer({server: server}); // server is express' server instance.
var clients = [];
wss.on('connection', function(ws) {
console.log('connected');
clients.push(ws);
ws.on('close', function(){
console.log('client closing');
});
ws.on('message', function(msg){
console.log('message received');
for (var i = 0, len = clients.length; i < len; i++)
try {
// do not use try-catch here, do it properly
if (clients[i])
clients[i].send(msg);
}
catch(e){
clients[i] = null;
}
});
});
The code above is broadcasting a message to all connected clients.
It's very nice but it doesn't work on Android!
Workarounds:
- Install Firefox or Chrome for Android, they support Websockets
- Use a library that supports fall back to XHR long polling like Socket.io
- Use Apache Cordova (phonegap) to create an Android app
- Use a Flash implementation of websockets
If you use Apache Cordova you need to "provide" the websockets funcionality into webview using Java code. There are several plugins for that:
- https://github.com/FreakDev/PhoneGap-Android-HTML5-WebSocket
- https://github.com/mkuklis/phonegap-websocket
- https://github.com/knowledgecode/WebSocket-for-Android
I haven't used any of those. The disadvantage is that users have to install an application. I just want them to use my web app without installing anything so I've discarded this option.
The flash implementation has some security constraints imposed by flash. I haven't dug into the subject much but I believe I need to open port 843 on the server and I can't do that on some PAAS providers. I can't confirm this, I might be wrong. The other thing is that browser needs the flash plugin. Take a look at this implementation (web-socket-js).
Eventually I've come back to Socket.io. Although people say it's outdated and it's got a poor websockets RFC implementation, it works very well for basic stuff and falls back to XHR long polling automatically:
var io = require('socket.io').listen(server); // server is express' server instance
io.configure(function () {
io.set("transports", ["websocket", "xhr-polling"]); // support both
io.set("polling duration", 10);
});
io.sockets.on('connection', function(socket){
socket.on("sendMessage", function(msg){
io.sockets.emit("messageReceived", msg); // broadcasting
});
});
On modern browsers implementing websockets, it will use them. On Android it will just use long polling.
To finish off, just remember that your mobile device and your desktop pc are two separate machines. If your client side js is trying to connect to "localhost" as the websockets server, it's not going to work from your mobile. I know it's absolutely obvious but I wasted more time than I expected not realizing that I had the wrong url in the client side js. It was written "localhost" rather than "192.168.0.100" for my tests using the Local Area Network. It was one of those silly mistakes that steal one's time more than they should 🙁