Real Time applications with WebSockets / WorkShop
Sergi Almar i graupera @sergialmar
Romanian mobile systems community conference (mobos) November 2013 - cluj Napoca
Agenda
• Part 1 - Architecture and Dependency Injection with Android
• Part 2 - Building the Server in Node.js and Socket.io
• Part 3 - Building the Android client
Social FeedsMultiplayer Games
Collaborative Apps
Clickstream DataFinancial Tickets Sports Updates
Multimedia Chat
Location-based AppsOnline Education
Real-time data on the web
• Polling
• Long polling / Comet
• Flash
Problem Applications need two-way communication Too many connections and overhead with ajax / comet
WebSockets two-way real time communication
WebSockets
• Real-time full duplex communication over TCP
• Uses port 80 / 443 (URL scheme: ws:// and wss://)
• Small overhead for text messages (frames)
• 0x00 for frame start, 0xFF for frame end (vs HTTP 1K)
• Ping / pong frames for staying alive
WebSocket HandshakeGET /mychat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== Sec-WebSocket-Protocol: chat Sec-WebSocket-Version: 13 Origin: http://example.com
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= Sec-WebSocket-Protocol: chat
client sends a WebSocket handshake request
server response
WebSocket API
var ws = new WebSocket('ws://www.romobos.com/ws'); !// When the connection is open, send some data to the server ws.onopen = function () { ws.send('Ping'); // Send the message 'Ping' to the server }; !// Log errors ws.onerror = function (error) { ws.log('WebSocket Error ' + error); }; !// Log messages from the server ws.onmessage = function (e) { ws.log('Server: ' + e.data); };
What we are gonna build (real-time chat app)
websockets
websockets
Dependency Injection decouple components, flexible code, reduce
boilerplate, testable code
Dependency Injection in Android
• RoboGuice
• Dagger
• Transfuse
• Android annotations
RoboGuice
• Dependency Injection framework
• Uses Google Guice as the backbone
• Supports JSR-330
Extending from RoboGuice• RoboActivity
• RoboListActivity
• RoboMapActivity
• RoboPreferenceActivity
• RoboFragmentActivity
• RoboFragment
• RoboService
• …
Injecting Views<TextView android:id="@+id/text1" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center" />
@InjectView(R.id.text1) TextView mSampleText;
Injecting Resources
@InjectResource(R.anim.my_animation) Animation myAnimation; !@InjectResource(R.drawable.icon) Drawable icon; !@InjectResource(R.string.app_name) String myName;
Injection POJOs@Singleton public class MyPojo { private String myField; ! public void myMethod() { ... } }
@Inject private MyPojo myPojo;
Custom Bindingpublic class MyModule implements Module { @Override public void configure(Binder binder) { binder.bind(IFoo.class).to(SimpleFoo.class); } }
define a module
public class App extends Application { ! @Override public void onCreate() { super.onCreate(); ! RoboGuice.setBaseApplicationInjector(this, RoboGuice.DEFAULT_STAGE, RoboGuice.newDefaultRoboModule(this), new MyModule()); } }
let RoboGuice know about it
Traditional Approach
class AndroidWay extends Activity { TextView name; ImageView thumbnail; LocationManager loc; Drawable icon; String myName; ! public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); name = (TextView) findViewById(R.id.name); thumbnail = (ImageView) findViewById(R.id.thumbnail); loc = (LocationManager) getSystemService(Activity.LOCATION_SERVICE); icon = getResources().getDrawable(R.drawable.icon); myName = getString(R.string.app_name); name.setText( "Hello, " + myName ); } }
RoboGuice Approach
@ContentView(R.layout.main) class RoboWay extends RoboActivity { @InjectView(R.id.name) TextView name; @InjectView(R.id.thumbnail) ImageView thumbnail; @InjectResource(R.drawable.icon) Drawable icon; @InjectResource(R.string.app_name) String myName; @Inject LocationManager loc; ! public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); name.setText( "Hello, " + myName ); } }
Lab I https://github.com/salmar/android-websockets-
mobos2013/wiki
Part II Building the Server with NodeJS
and Socket.io
Node.js event driven, non-blocking server
side JS
Google V8 Engine
• Open source JS engine by Google (used in Google Chrome)
• No JIT, all JS compiled to assembler
• Optimisations like inlining, elision of runtime properties…
• Improved garbage collector
CommonJS
• Set of specifications for JS outside the browser
• Node.js implements some specifications
• i.e modules
• There should be a function called require
• There should be a var called exports
Modules• Node.js provides some core modules like http, tcp, fs, sys…
• will look for the module in the node_modules dir hierarchically
• if not found, will look in the paths outlined in NODE_PATH
var http = require('http');
Module Examplevar PI = Math.PI; exports.area = function (r) { return PI * r * r; }; exports.circumference = function (r) { return 2 * PI * r; };
module definition in myModule.js
var myModule = require('./myModule.js');
include myModule.js in some other file
Dependency Management
Sergis-MacBook-Air:tmp salmar$ npm install express npm http GET https://registry.npmjs.org/express npm http 200 https://registry.npmjs.org/express npm http GET https://registry.npmjs.org/express/-/express-3.4.4.tgz npm http 200 https://registry.npmjs.org/express/-/express-3.4.4.tgz npm http GET https://registry.npmjs.org/connect/2.11.0 npm http GET https://registry.npmjs.org/commander/1.3.2 npm http GET https://registry.npmjs.org/methods/0.1.0 npm http GET https://registry.npmjs.org/range-parser/0.0.4 npm http GET https://registry.npmjs.org/mkdirp/0.3.5 npm http GET https://registry.npmjs.org/cookie/0.1.0 npm http GET https://registry.npmjs.org/buffer-crc32/0.2.1 npm http GET https://registry.npmjs.org/fresh/0.2.0 npm http GET https://registry.npmjs.org/cookie-signature/1.0.1 npm http GET https://registry.npmjs.org/send/0.1.4 npm http GET https://registry.npmjs.org/debug npm http 200 https://registry.npmjs.org/methods/0.1.0 npm http 304 https://registry.npmjs.org/range-parser/0.0.4 npm http GET https://registry.npmjs.org/methods/-/methods-0.1.0.tgz npm http 200 https://registry.npmjs.org/commander/1.3.2 npm http GET https://registry.npmjs.org/commander/-/commander-1.3.2.tgz npm http 304 https://registry.npmjs.org/cookie/0.1.0 … [email protected] node_modules/express ├── [email protected] ├── [email protected] ├── [email protected] ├── [email protected] ├── [email protected] ├── [email protected] ├── [email protected] ├── [email protected] ├── [email protected] ([email protected]) ├── [email protected] ([email protected]) └── [email protected] ([email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected],
Node packet manager (npm) express (routing), socket.io (websockets)…
package.json{ "name": "Mobos Chat", "version": "1.0.0", "description": "Real time chat", "author": "salmar", "scripts": { "start": "node app.js" }, "dependencies": { "socket.io": "latest", "express": "latest", "jade": "latest" } }
npm install
var http = require('http'); !http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n'); }).listen(1337, '127.0.0.1'); !console.log('Server running at http://127.0.0.1:1337/');
Web Server in NodeJS
Running the appSergis-MacBook-Air:tmp salmar$ node app.js Server running at http://127.0.0.1:1337/
Express.js sinatra inspired web framework for node.js
Express.js
var express = require('express'); var app = express(); !app.get('/', function(req, res){ res.send('Hello World'); }); !app.listen(3000);
Socket.ioabstraction layer for WebSockets
http://caniuse.com/#feat=websockets / Nov 2013
Socket.io• Abstraction layer for WebSockets
• Fallback to:
• Flash socket
• AJAX Long-polling
• AJAX multi-part streaming
• JSONP polling
• iFrame
Handling Eventsio.sockets.on('connection', function(socket) {});
initial connection from client
socket.on('message', function(message) {})
message handler triggered when message is received
socket.on('disconnect', function() {})
triggered when socket disconnects
socket.on('custom_event', function(data) {})
event handler for custom event
Sending messages
socket.send(JSON.stringify({user:'sergi', message: 'Welcome to Mobos'}) );
sends a message to the connected client
socket.broadcast.send(JSON.stringify({user:’sergi', message: 'Welcome to Mobos'}) );
sends a message to all clients except the owner of the socket
Emitting Events
socket.emit('user:join', {name: 'sergi'});
triggers a custom event
socket.broadcast.emit('user:joined', data);
sends a message to all clients except the owner of the socket
Attach information to the socket
socket.set('nickname', data.name, <optional_callback>);
Lab II https://github.com/salmar/android-websockets-
mobos2013/wiki
Part III Building the Android client with
Socket.io
Otto enhanced event bus with emphasis on Android support
Activity
Activity
FRAGMENT
SERVICE
POJO
bus
Activity
Activity
FRAGMENT
subscribepublish
Otto
• Forked from Guava’s EventBus
• Lightweight - 19k
• Fast, optimised for Android
Publishing
publish the message
synchronous delivery
Bus bus = new Bus();
bus.post(new ServerMessage("This is awesome"));
creates the bus (better use dependency injection)
Subscribing
@Subscribe public void receiveMessage(ServerMessage serverMessage) { // TODO: React to the event somehow! }
Otto API
• register(), unregister(), post()
• @Subscribe, @Produce
• Thread confinement
• Easy to test
SocketIO for Android
• There’s no official library, but there are community libraries, sometimes buggy :(
• https://github.com/fatshotty/socket.io-java-client
• Server-like API
Callbacks
public void onMessage(JsonElement json, IOAcknowledge ack) { } ! public void onMessage(String data, IOAcknowledge ack) { } ! public void onError(SocketIOException socketIOException) { } ! public void onDisconnect() { } ! public void onConnect() { } ! public void on(String event, IOAcknowledge ack, JsonElement... args) { }
Lab III https://github.com/salmar/android-websockets-
mobos2013/wiki
Thank you! @sergialmar