Building Real-Time Applications with Android and WebSockets

Preview:

Citation preview

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 … !express@3.4.4 node_modules/express ├── methods@0.1.0 ├── range-parser@0.0.4 ├── cookie-signature@1.0.1 ├── fresh@0.2.0 ├── debug@0.7.4 ├── buffer-crc32@0.2.1 ├── cookie@0.1.0 ├── mkdirp@0.3.5 ├── commander@1.3.2 (keypress@0.1.0) ├── send@0.1.4 (mime@1.2.11) └── connect@2.11.0 (methods@0.0.1, uid2@0.0.3, pause@0.0.1, qs@0.6.5, raw-body@0.0.3, bytes@0.2.1, negotiator@0.3.0,

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

Recommended