O documento descreve um grupo destinado a compartilhar e promover encontros de desenvolvedores mobile no Rio Grande do Sul, que colabora com eventos de desenvolvimento e realiza oficinas para desenvolvimento de aplicativos móveis em diversas plataformas.
3. GU Mobile
RS
Somos um grupo destinado a
compartilhar e promover
encontros de desenvolvedores
mobile no Rio Grande do Sul.
Eventos
Colaboramos para eventos
de desenvolvimento.
Oficínas
Realizamos encontros para
por em prática o
desenvolvimento de
aplicativos móveis em
diversas plataformas.
4. Aplicações real-time
Quando se fala de aplicações real-time, estamos
técnicamente falando de conexões bi-direcionais, que na
prática, é uma conexão que se mantem aberta (keep-alive)
para que os clientes interajam com o servidor em uma única
conexão aberta.
A grande vantagem disso é de não precisar mais das
requisições assíncronas (AJAX) para simular uma “ação
quase real-time”, digo quase pois AJAX não é uma
conexão bi-direcional capaz de se manter aberta até o
término de seu processamento.
5. Realtime
App
• Chat
• Stream de atividades
• Notificações
• Colaboração
• Jogos multiplayer
• Dados em tempo real
• Dashboards
• Experiências 2 telas
6. Comet
Comet é um modelo de
aplicação web que tem
como principal
característica a utilização de
uma ligação persistente
HTTP que permite ao
servidor transmitir dados
para o cliente sem que
exista um pedido explícito.
Esta técnica é chamada
de Tecnologia Push.
O termo Comet
representa um grupo de
recursos e técnicas
utilizados para a
interação bilateral.
Fonte:http://en.wikipedia.org/wiki/Comet_%28programming%29
7. Websockets
É uma tecnologia que
permite a comunicação
bidirecional por canais full-
duplex sobre um único
soquete (TCP).
Suporte
Todos os browsers mais
atuais com exceção do
browser Android suportam
a ultima especificação do
protocolo.
• Internet Explorer 10+
• Mozilla Firefox 4+
• Safari 5+
• Google Chrome 4+
• Opera 11+
8. WebSocket Protocol
Handshake
Enquanto o protocolo WebSocket em si é desconhecido
dos servidores proxy e firewalls, ele possui um handshake
HTTP compatível para que os servidores HTTP possam
compartilhar seu padrão HTTP e HTTPS portas (80 e 443)
com um gateway ou servidor WebSocket.
O protocolo WebSocket define um ws:// e wss:// prefixo
para indicar um WebSocket e uma conexão segura
WebSocket, respectivamente.
10. WebSocket Protocol
Handshake
Uma vez que a conexão é estabelecida entre o cliente e o
servidor, podemos enviar dados ou estruturas de texto em
ambas as direções no modo full-duplex.
16. Node.js
Node.js se tornou popular devido a sua facilidade em
trabalhar com real-time, simplesmente por que o protocolo
WebSockets (Protocolo do HTML5 para conexões bi-
direcionais) utiliza-se Javascript também.
• Orientado a eventos
• Lado do servidor
• JS não bloqueante
Mais em: http://udgwebdev.com/nodejs-para-leigos-introducao/
17. Node.js
Google V8 Motor
• Engine JS Open source do Google (usado no Google
Chrome)
• Sem JIT (Just-In-Time), todos JS compilado para
assembler
• Otimizações como inlining, elisão de propriedades em
tempo de execução ...
• Garbage Collector implementado / melhorado
http://en.wikipedia.org/wiki/Inlining
http://en.wikipedia.org/wiki/V8_(JavaScript_engine)
http://en.wikipedia.org/wiki/Copy_elision
18. Node.js
CommonJS
• Conjunto de especificações para JS fora do navegador
• Node.js implementa algumas especificações
– ex. módulos
• Deve haver uma função chamada require
• Deve haver uma var chamada exports
19. Node.js
Módulos
• O Node.js fornece alguns módulos principais como o http,
tcp, fs, sys...
– procurará o módulo na pasta node_modules hierarquicamente
– se não for encontrado, vai olhar nos caminhos descritos no
NODE_PATH
var http = require ('http');
20. Node.js
Exemplo de Módulo
var PI = Math.PI;
exports.area = function (r) {
return PI * r * r;
};
exports.circumference = function (r) {
return 2 * PI * r;
};
Exemplo de definição em meuModulo.js
var meuModulo = require(‘./meuModulo .js’);
Include do meuModulo.js em outro arquivo
22. Node.js
Package.json
{
"name": ”Fisl 15 Chat",
"version": "1.0.0",
"description": "Real time chat com Android",
"author": ”Jackson F. de A. Mafra",
"scripts": { "start": "node app.js" },
"dependencies": { "socket.io": "latest", "express": "latest", "jade": "latest" }
}
npm install
23. Node.js
Webserver em Node.JS
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World, Wellcome to FISL15n');
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');
node app.js
Server running at http://127.0.0.1:1337/
24. Node.js
Express.js
Uma web framework para node.js inspirada em sinatra
var express = require('express');
var app = express();
app.get('/', function(req, res){
res.send('Hello World');
});
app.listen(3000);
25. Node.js
Socket.io
É uma camada de abstração para WebSockets com fallback para :
• Flash socket
• AJAX Long-polling
• AJAX multi-part streaming
• JSONP polling
• iFrame
26. Node.js
Manipulação Eventos
io.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
31. Injeção de
Dependência
Injeção de dependência é um padrão de projeto de software
que permite a remoção de dependências hard-coded e
torna possível para mudá-los, seja em tempo de execução
ou em tempo de compilação. [Fonte: Wikipedia]
http://en.wikipedia.org/wiki/Dependency_injection
32. Injeção de
Dependência
A Injeção de Dependências (também conhecida como
Inversão de Controle) está presente em diversos frameworks
populares como Spring ou Google Guice. Porém, estes
últimos foram desenvolvidos pensando na JVM e não em
ambientes móveis como o Android.
Enquanto o RoboGuice procura melhorar a usabilidade do
Guice no Android, o Dagger segue uma abordagem
diferente, concentrando-se em funcionalidades simplificadas
e melhor desempenho.
http://www.infoq.com/br/news/2012/10/dagger-injecao-android
33. Injeção de
dependência
Injeção de Classes
Dagger
Injeção de views
Butter Knife
Universal
RoboGuice
Android Annotations
(AA)
Mais em: http://java.dzone.com/articles/how-become-lazy-productive
34. Criando o projeto
O cliente de chat Android deve ser capaz de enviar e receber
mensagens a partir de uma sala de bate-papo.
Inicie o Android Studio e crie um novo projeto chamado
FislChat com o package io.github.gumobilers.fislchat
Criar uma atividade em branco chamado ChatActivity
35. Definindo o layout
O fragmento irá conter o layout da nossa aplicação de chat. Abra o
fragment_chat.xml (ou o nome que você deu para o layout) e defina:
• Uma ListView que conterá as mensagens de bate-papo
(dica: android: stackFromBottom = "true" android: transcriptMode =
"alwaysScroll" pode ajudá-lo)
• Um EditText para escrever a mensagem que deseja enviar para a sala
de bate-papo
• Um botão para enviar a mensagem
• Execute o aplicativo e ver que você começa o layout desejado
36. Adicionando o
RoboGuice
A instalação do RoboGuice requer que você baixe os seguintes arquivos JAR e
adicioná-los ao seu classpath.
http://repo1.maven.org/maven2/org/roboguice/roboguice/2.0/roboguice-2.0.jar
http://repo1.maven.org/maven2/com/google/inject/guice/3.0/guice-3.0-no_aop.jar
http://repo1.maven.org/maven2/com/google/code/findbugs/jsr305/1.3.9/jsr305-1.3.9.jar
38. Adicionando o
RoboGuice
Para utilizar RoboGuice para a injeção de dependência, é preciso adicioná-lo
como uma dependência do projeto.
• Abra o arquivo gradle.build e adicionar a dependência RoboGuice sob o
elemento dependências .
• compile 'org.roboguice: roboguice: 3.0b-experimental’
• Certifique-se de que RoboGuice aparece em suas bibliotecas externas
• Nossos componentes Android precisará estender classes específicas
RoboGuice ter DI no lugar.
• Edite seu fragmento e estendem-se desde RoboFragment
• Edite você Atividade e estendem-se desde RoboFragmentActivity
39. Huston we have a
problem!
Em caso de problemas:
No Android Studio clique no botão Sync Project with Gradle File, deixe o AVD
Manager fazer o trabalho. Sem restart, sem ./gradlew clean ou ./gradlew build.
Tambem pode ir em Tools-->Android-->Sync Project with Gradle File.
40. Injetando
dependências
Para interagir com os componentes de layout, vamos usar a injeção dependência
RoboGuice
• Adicionar um campo privado do tipo ListView e anotá-lo com @InjectView
para obter uma referência da lista declarados
(ex: @InjectView (R.id.messageList) private mMessageList ListView;)
• Adicionar um campo privado do tipo EditText e anotá-lo com @InjectView
para obter a referência da entrada de texto declarado.
• Adicionar um campo privado do tipo Button e anotá-lo com @InjectView para
obter a referência do botão declarado.
41. Adicionando a lógica
A injeção acontece durante o método onViewCreated em seu fragmento, de
forma a garantir que as dependências já foram injetadas estaremos interagindo
com eles no mesmo ponto.
1. Sobrescrever o método onViewCreated:
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
1. De forma a interagir com o elemento da lista, é preciso fornecer um adaptador.
2. Vamos adicionar um simples ArrayAdapter de String para agora:
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_1);
mMessageList.setAdapter(adapter);
42. Injetando um serviço
de sistema
Vamos agora fazer o nosso telefone vibrar quando enviar uma mensagem.
Adicionar a permissão Vibrar em seu AndroidManifest.xml
<uses-permission android:name="android.permission.VIBRATE" />
Adicionar um campo particular do tipo Vibrator em seu fragmento e anotá-lo
com a JSR-330 anotação @Inject.
Adicionar a lógica para vibrar 1000 milissegundos quando o envio da mensagem
44. Criando o projeto
Node.js
Para ter a estrutura do projeto, vamos contar com o express.js (semelhante ao
sinatra). Instale expressar globalmente com o seguinte comando:
npm install –g express
Agora vamos criar um aplicativo baseado expressa chamado fislchat
express fislchat
46. Criando o projeto
Node.js
Observe que um arquivo chamado package.json contendo as dependências foi
criado.
Execute npm install para adicioná-las ao projeto (verifique que a pasta
node_modules é criada e contém as dependências).
O app.js é o ponto de entrada para o nosso aplicativo, ele importa os módulos
necessários, define algumas rotas e cria o servidor http.
Abra e dê uma olhada nisso. Isso deve ser o suficiente para começar a nossa
aplicação.
Executar node app ou npm start para iniciar o aplicativo.
Verifique se digitando 'http://localhost:3000/' no seu navegador lhe dá a página
de boas-vindas.
47. Adicionando o
Socket.io
Até agora, você deve estar familiarizado com packages.json. De forma que para
adicionar o suporte ao Socket.io precisamos defini-lo como uma dependência
de tal arquivo, abra-o e adicione:
"socket.io": "~ 0.9.16".
Execute npm install novamente para buscar a dependência.
Vamos agora criar um módulo que irá encapsular a lógica do socket.io. Crie um
arquivo chamado socket.js dentro da pasta rotas.
Este módulo irá usar a dependência socket.io já definida.
Importe no módulo :
var socketio = require ('socket.io');
48. Adicionando o
Socket.io
Os módulos contêm lógica privada para a próprio módulo, que encapsulam a
lógica, mas lembre-se que eles também podem expô-la usando a convenção
CommonJS (com exportações das variáveis).
Vamos expor uma função chamada initialize então podemos inicializá-la a partir
de outro módulo:
exports.initialize = function(server) {
io = socketio.listen(server);
}
Nós adicionamos um parâmetro de servidor (este será o servidor http já criado
no arquivo app.js) e nós inicializamos socketio, a fim de começar a aceitar
conexões.
Agora é hora de usar o nosso módulo.
49. Usando nosso módulo
Socket.io
var server = http.createServer(app).listen(app.get('port'), function(){
console.log('Express server listening on port ' + app.get('port'));
});
Abra a app.js e adicione o módulo que acabamos de criar
var io = require (’./routes /socket.js.').
Lembre-se que nós expomos a função initialize esperando um servidor http
como parâmetro. Se você rolar no final do arquivo, você vai ver como o servidor
http é criado (por padrão escutar no porto 3000).
Permite obter uma referência nesse servidor para que possamos passá-lo para o
módulo
50. Usando nosso módulo
Socket.io
Agora você pode chamar a função initialize, passando o parâmetro do servidor:
io.initialize (server);
Inicie o aplicativo novamente (app nó) e verificar se você ver algo como
info - socket.io
iniciada no console.
51. Manipulação de
eventos
Agora temos o app já ouvindo as conexões WebSocket , mas precisamos lidar
com os eventos ( nova conexão, desconexão , mensagens ...) .
Vamos adicionar um listener de conexão dentro do método initialize :
io.sockets.on('connection', function(socket) {
// All your handlers here, socket will be the connected client
});
52. Manipulação de
eventos
Fornecer um handler para o evento 'user : setname ‘.
Este será acionado pelo cliente logo após a conectar.
( Dica : . Lembre socket.on ( 'event' , function ( data) { })
A mensagem recebida será algo semelhante a { ' nickname': ' jackson'} .
Defina o valor para a sessão de socket para que possamos lembrar o nome do
usuário e não forçá-lo a ser enviar toda vez ( dica: use socket.set ) .
Transmita uma mensagem como { ‘mensagem: ' jackson entrou !’ } para todos
os usuários conectados .
53. Manipulação de
eventos
Forneça um handler para o evento 'user : mensagem’.
Este evento será acionado quando um usuário envia uma mensagem para o chat.
Os dados será um JSON semelhante a este: {' mensagem ': ' Olá '} .
Transmita a mensagem para todos os outros clientes que adicionaram o apelido
para ele ( dica: use socket.get(var, function (err, nickname) {}) ) para obter o apelido previamente
armazenado ) .
A mensagem resultante deve ser como :
{' mensagem ': ' Olá ', ' apelido ': ' jackson'}
54. Manipulação de
eventos
BÔNUS:
Guarde os nomes de usuário em um array var users = []; , armazenar o nome
quando se conectar e remova-o quando ele se desconectar.
56. Incluindo
Dependências
Não há nenhum suporte para Socket.io no Android , mas vamos adicionar uma
biblioteca a partir de um repositório Maven.
Abra o arquivo build.gradle e adicione o seguinte repositório sob o elemento
repositórios:
repositories {
mavenCentral()
maven {
url "http://audiobox.keytwo.net"
}
}
Vamos adicionar agora as duas dependências que requerem
compile 'io.socket:socket.io-client:0.2.1'
compile 'com.squareup:otto:1.3.4'
Socket-io: https://github.com/fatshotty/socket.io-java-client
Otto: https://github.com/square/otto
57. Criando o serviço
De forma a manter a conexão em segundo plano, vamos criar um serviço que irá
gerenciar nossos WebSockets.
Crie uma classe chamada ChatService que estenda RoboService.
Adicione a definição de serviço no arquivo AndroidManifest.xml, para
conhecimento da aplicação: <service android:name=".ChatService" />
Agora que temos o serviço, nós vamos ter que iniciá-lo. Um bom lugar para
iniciá-lo, seria de uma classe de aplicação(Application).
Criar e uma classe chamada ChatApplication e inicie o serviço no método
onCreate. Você deve ter algo semelhante a isto:
58. Criando o serviço
public class ChatApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// Start the service here
}
}
Volte para o arquivo AndroidManifest.xml e adicione o nome do aplicativo que
você acabou de criar:
<application
android:name=".ChatApplication"
59. Adicionando o evento
de barramento
Otto é um barramento de evento destinado a separar
diferentes partes do seu aplicativo enquanto ainda permite
que elas se comuniquem de forma eficiente.
http://square.github.io/otto/
60. Bus bus = new Bus();
//creates the bus (better use dependency injection)
bus.post(new ServerMessage("This is awesome"));
//publish the message
//synchronous delivery
Publicando
Adicionando o evento
de barramento
62. • register(), unregister(), post()
• @Subscribe, @Produce
• Thread confinement
• Testes simples
Otto API
Adicionando o evento
de barramento
63. Vamos criar um módulo RoboGuice para tornar o singleton Bus e reutilizá-lo em
todo o aplicativo.
Criar uma classe interna no ChatApplication:
Otto API
Adicionando o evento
de barramento
class ChatModule implements Module {
@Override
public void configure(Binder binder) {
// Figure out how to declare a singleton Bus
}
}
64. Agora adicione essa linha logo antes de iniciar o serviço, para que o RoboGuice
saiba sobre o nosso módulo:
Otto API
Adicionando o evento
de barramento
RoboGuice.setBaseApplicationInjector(this, RoboGuice.DEFAULT_STAGE,
RoboGuice.newDefaultRoboModule(this), new ChatModule());
65. Agora você está pronto para injetar o Bus tanto no ChatService e ao
ChatFragment.
Crie um campo privado do tipo Bus e usar a anotação @Inject.
Otto API
Adicionando o evento
de barramento
66. Uma última coisa, a fim de receber eventos (também se aplica aos produtores),
uma instância de classe precisa se registrar com o barramento.
Adicione o seguinte código para o ChatFragment
Otto API
Adicionando o evento
de barramento
@Override public void onResume() {
super.onResume();
mBus.register(this);
}
@Override public void onPause() {
super.onPause();
// Always unregister when an object no longer should be on the bus.
mBus.unregister(this);
}
67. Adicione o seguinte código para o ChatService
Otto API
Adicionando o evento
de barramento
@Override
public void onCreate() {
super.onCreate();
mBus.register(this);
}
@Override
public void onDestroy() {
super.onDestroy();
mBus.unregister(this);
}
68. Adicione a permissão INTERNET para o seu AndroidManifest.xml para que
possa se conectar ao servidor.
Interagindo com o
Servidor com o
Socket.io
<uses-permission android:name="android.permission.INTERNET"/>
Vamos agora abrir um WebSocket com o servidor no ChatService.
Crie um campo privado do tipo SocketIO e um método de inicialização
(initialize) para inicializar Socket.io:
69. socket = new SocketIO("http://127.0.0.1:3000/");
socket.connect(new IOCallback() {
@Override
public void onMessage(JsonElement json, IOAcknowledge ack) {
System.out.println("Server said:" + json.toString());
}
@Override
public void onMessage(String data, IOAcknowledge ack) {
System.out.println("Server said string: " + data);
}
@Override
public void onError(SocketIOException socketIOException) {
System.out.println("an Error occured");
socketIOException.printStackTrace();
}
...
Interagindo com o
Servidor com o
Socket.io
70. @Override
public void onDisconnect() {
System.out.println("Connection terminated.");
}
@Override
public void onConnect() {
System.out.println("Connection established");
}
@Override
public void on(String event, IOAcknowledge ack, JsonElement... args) {
System.out.println("Server triggered event '" + event + "'");
}
});
Interagindo com o
Servidor com o
Socket.io
71. Logo após ter uma conexão bem-sucedida, emite evento de user: setname e
enviar uma mensagem como {'apelido': ’jackson'}
(você pode codificar o nome de usuário até agora)
Definindo o apelido
72. • servidor irá enviar mensagens JSON no seguinte formato {’message': ’Ola',
'Apelido': ’jackson'}.
Crie uma classe chamada ServerMessage que representa esse esquema e
sobreescreve o método toString (vamos usá-lo para exibir as mensagens na
interface do usuário).
Vamos interpretar mensagens sem apelido para ser mensagens do sistema, então
isso vai como vamos mostrar mensagens:
As mensagens do sistema -> / / mensagem (ex:, / / jackson entrou na sala)
Mensagens do usuário -> apelido: mensagem (ex:, jackson: Chatiado!)
Recebendo
mensagens
73. public String toString() {
return nickname != null ? nickname + ": " + message : "//" + message;
}
Agora você pode implementar o método onMessage (a versão String),
convertendo a mensagem recebida para um ServerMessage com Gson (você
tem já como uma dependência, dica: use o método fromJson).
Esta mensagem precisa ser exibida na interface do usuário, mas como queremos
ter tudo dissociado, vamos enviá-la para o barramento de evento, para um
assinante pode processá-lo. AVISO: Por padrão, toda a interação com uma
instância Bus é confinada na Main Thread, mas o serviço está sendo executado
em uma Thread diferente. A fim de ser capaz de postar o evento para a Main
Thread apartir do ChatService, estaremos usando uma solução de meio hacky.
Recebendo
mensagens
74. private final Handler mHandler = new Handler(Looper.getMainLooper());
public void post(final Object event) {
if (Looper.myLooper() == Looper.getMainLooper()) {
mBus.post(event);
} else {
mHandler.post(new Runnable() {
@Override public void run() {
mBus.post(event);
}
});
}
}
Copie e cole o seguinte método no ChatService e postar a mensagem de usá-lo:
Recebendo
mensagens
75. Temos agora a mensagem publicada para a o barramento, vamos adicionar um
assinante para o evento.
O assinante deve ser definido no ChatFragment para que possamos atualizar o
adaptador base no evento.
Altere a definição do adaptador de ArrayAdapter <String> para ArrayAdapter
<ServerMessage> e atualizá-lo a partir do método de assinantes.
Execute o aplicativo e verificar que as mensagens são recebidas e adicionado à
lista.
Recebendo
mensagens
76. Quando pressionar o botão Enviar queremos que as mensagens sejam enviadas
para o WebSocket.
Para isso, vamos criar uma classe chamada ClientMessage que representa o
esquema a ser enviado ({'mensagem': 'Olá'}).
Usando a instancia de barramento que injetamos, postamos a mensagem e
definimos um assinante no ChatService , responsável por enviá-lo para a WS
(dica: use Gson novamente para converter de Object para String, toJson)
Enviando mensagens
78. Crie um arquivo em seu diretório público chamado chat.html (O Node.js irá
servi-la como um recurso estático) e adicione o seguinte esqueleto HTML5:
Construindo o cliente
do navegador
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Mobos Chat</title>
<!--[if lt IE 9]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body>
</body>
<script src="/socket.io/socket.io.js" ></script>
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
</html>
79. Adicione um input para escrever a mensagem, um botão e um div para exibir as
mensagens de bate-papo.
Crie um arquivo chamado chat.js dentro da pasta javascript para encapsular a
lógica do socket.io
Construindo o cliente
do navegador
var socket = io.connect('http://localhost');
socket.on('yourevent', function (data) {
console.log(data);
socket.emit('my other event', { my: 'data' });
});
var socket = io.connect('http://localhost'); socket.on('yourevent', function (data) { console.log(data); socket.emit('my other event', { my: 'dat