desenvolvimento de um jogo multiusu´ario utilizando android

Transcrição

desenvolvimento de um jogo multiusu´ario utilizando android
UNIVERSIDADE DO ESTADO DO AMAZONAS - UEA
ESCOLA SUPERIOR DE TECNOLOGIA
ENGENHARIA DE COMPUTAÇÃO
VICTOR MATHEUS DE SOUZA PEREIRA
DESENVOLVIMENTO DE UM JOGO
MULTIUSUÁRIO UTILIZANDO ANDROID
Manaus
2012
VICTOR MATHEUS DE SOUZA PEREIRA
DESENVOLVIMENTO DE UM JOGO MULTIUSUÁRIO UTILIZANDO
ANDROID
Trabalho de Conclusão de Curso apresentado
à banca avaliadora do Curso de Engenharia
de Computação, da Escola Superior de
Tecnologia, da Universidade do Estado do
Amazonas, como pré-requisito para obtenção
do tı́tulo de Engenheiro de Computação.
Orientador: Prof. M. Sc. Jucimar Maia da Silva Júnior
Manaus
2012
ii
Universidade do Estado do Amazonas - UEA
Escola Superior de Tecnologia - EST
Reitor:
José Aldemir de Oliveira
Vice-Reitor:
Marly Guimarães Fernandes Costa
Diretor da Escola Superior de Tecnologia:
Mário Augusto Bessa de Figueirêdo
Coordenador do Curso de Engenharia de Computação:
Daniele Gordiano Valente
Coordenador da Disciplina Projeto Final:
Raimundo Correa de Oliveira
Banca Avaliadora composta por:
Data da Defesa: 23 / 06 / 2012.
Prof. M.Sc. Jucimar Maia da Silva Júnior (Orientador)
Prof. M.Sc. Raimundo Correa de Oliveira
Prof. M.Sc. Rodrigo Choji de Freitas
CIP - Catalogação na Publicação
P436d
Pereira, Victor
Desenvolvimento de um Jogo Multiusuário utilizando Android/ Victor
Pereira; [orientado por] Prof. MSc. Jucimar Maia da Silva Júnior - Manaus: UEA, 2012.
92 p.: il.; 30cm
Inclui Bibliografia
Trabalho de Conclusão de Curso (Graduação em Engenharia de Computação). Universidade do Estado do Amazonas, 2012.
CDU: 004
iii
VICTOR MATHEUS DE SOUZA PEREIRA
DESENVOLVIMENTO DE UM JOGO MULTIUSUÁRIO UTILIZANDO
ANDROID
Trabalho de Conclusão de Curso apresentado
à banca avaliadora do Curso de Engenharia
de Computação, da Escola Superior de
Tecnologia, da Universidade do Estado do
Amazonas, como pré-requisito para obtenção
do tı́tulo de Engenheiro de Computação.
Aprovado em: 23 / 06 / 2012
BANCA EXAMINADORA
Prof. Jucimar Maia da Silva Júnior, Mestre
UNIVERSIDADE DO ESTADO DO AMAZONAS
Raimundo Correa de Oliveira, Mestre
UNIVERSIDADE DO ESTADO DO AMAZONAS
Rodrigo Choji de Freitas, Mestre
UNIVERSIDADE DO ESTADO DO AMAZONAS
iv
Agradecimentos
É com muita emoção, após um ano de dedicação, os agradecimentos que ofereço às pessoas que se encontravam ao meu redor e fizeram com que eu permanecesse e enfrentasse
qualquer obstáculo:
À Deus, por ter me proporcionado essa oportunidade.
A minha mãe Silene Martins e ao meu irmão
Jamil Júnior, que sempre confiaram no meu
potencial apesar de toda dificuldade por qual
passamos.
Ao meu grande amigo e parceiro Ronny Martins, por ter me acompanhado durante essa
jornada.
Ao meu orientador, Prof.
Msc.
Jucimar
Júnior, e a todos os meus professores, que me
ensinaram e contribuı́ram para o meu crescimento profissional.
E a todos os meus amigos, aos quais me
ausentei por diversas vezes, mas sempre me
compreenderam e me deram toda força.
v
Resumo
Este trabalho apresenta o desenvolvimento de um jogo da velha multiusuário usando a
plataforma Android. O jogo, nomeado de OX Game, permite que dois usuários joguem,
um contra o outro, partidas utilizando as regras do jogo da velha tradicional. A plataforma
Android é uma plataforma aberta voltada para dispositivos móveis e que possui um kit de
desenvolvimento próprio, utilizando a linguagem de programação Java. O jogo OX Game
implementa a arquitetura cliente-servidor e a comunicação existente entre os jogadores é
realizada a partir de fluxos de entrada e saı́da de dados conhecidos como sockets.
Palavras-Chave: jogo, jogo da velha, Android, arquitetura cliente-servidor, sockets,
comunicação em rede, java.
vi
Abstract
This work presents the development of a multiplayer tic-tac-toe game using the Android
platform. This game, named by Ox Game, lets two users play the tic-tac-toe against each
other using the traditional rules. The Android platform is an open source platform for
mobile devices and it has a development kit itself, using the programming language Java.
The game OX Game implements the client-server architecture and the communication
between players is made by input and output data stream known as sockets.
Keywords: game, tic-tac-toe, Android, client-server architecture, sockets, network communication, java.
vii
Sumário
Lista de Figuras
1 Introdução
1.1 Objetivo . . . . . . . .
1.1.1 Especı́ficos . . .
1.2 Justificativa . . . . . .
1.3 Metodologia . . . . . .
1.4 Estrutura do Trabalho
ix
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
2 Plataforma Android
2.1 História . . . . . . . . . . . . . . . . . . . . .
2.2 Open Handset Alliance (OHA) . . . . . . . . .
2.3 Licença . . . . . . . . . . . . . . . . . . . . .
2.4 Arquitetura . . . . . . . . . . . . . . . . . . .
2.5 Segurança . . . . . . . . . . . . . . . . . . . .
2.6 Caracterı́sticas . . . . . . . . . . . . . . . . .
2.7 O Kit de Desenvolvimento de Software - SDK
3 Programação Distribuı́da usando Android
3.1 Componentes das Aplicações em Android .
3.1.1 Activities . . . . . . . . . . . . . .
3.1.2 Broadcast e Intent Receivers . . . .
3.1.3 Services . . . . . . . . . . . . . . .
3.1.4 Content Providers . . . . . . . . .
3.2 Programação Distribuı́da usando Android
3.2.1 Arquitetura Cliente-Servidor . . . .
3.2.2 Arquitetura Ponto-a-Ponto . . . . .
3.3 Serviços de Comunicação . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
1
2
2
3
3
4
.
.
.
.
.
.
.
5
5
7
9
9
11
11
12
.
.
.
.
.
.
.
.
.
15
15
17
20
21
22
22
23
24
25
viii
3.3.1
3.3.2
3.3.3
Implementando um servidor utilizando Sockets de fluxo . .
Implementando um cliente utilizando Sockets de fluxo . . .
Estabelecendo uma comunicação utilizando a arquitetura
servidor . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4 Desenvolvimento
4.1 Etapas de Desenvolvimento .
4.2 O Projeto . . . . . . . . . . .
4.3 Jogo da Velha . . . . . . . . .
4.3.1 Regras . . . . . . . . .
4.4 Arquitetura do Jogo . . . . .
4.5 Modelagem . . . . . . . . . .
4.6 Codificando o Jogo . . . . . .
4.6.1 Codificando o cliente .
4.6.2 Codificando o Servidor
4.7 Imagens do Jogo . . . . . . .
. . . . .
. . . . .
cliente. . . . .
26
28
30
.
.
.
.
.
.
.
.
.
.
34
34
35
36
36
37
38
45
45
61
68
5 Conclusão
5.1 Trabalhos Futuros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
75
75
Referências Bibliográficas
77
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
ix
Lista de Figuras
2.1
2.2
Membros da Open Handset Alliance - OHA . . . . . . . . . . . . . . . . .
Arquitetura da Plataforma Android . . . . . . . . . . . . . . . . . . . . . .
8
10
3.1
3.2
3.3
3.4
3.5
3.6
3.7
3.8
Exemplo de um projeto Android . . . . . . . . . . . .
Ciclo de vida de uma Activity . . . . . . . . . . . . .
Diagrama representando a arquitetura cliente-servidor
Diagrama representando a arquitetura ponto-a-ponto
Servidor aguardando uma requisição de conexão . . .
Confirmação de conexão do cliente com o servidor . .
Confirmação de fluxo de dados no servidor . . . . . .
Exemplo de um projeto Android . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
16
18
24
25
31
32
32
33
4.1
4.2
4.3
4.4
4.5
4.6
4.7
4.8
4.9
4.10
4.11
4.12
4.13
4.14
4.15
4.16
Tabuleiro 3x3 e sı́mbolos padrões utilizados no Jogo da Velha . . . . . .
Possibilidade de Vitoria no Jogo da Velha . . . . . . . . . . . . . . . .
Arquitetura do Jogo OX Game . . . . . . . . . . . . . . . . . . . . . .
Diagrama de Casos de Uso do Jogo da Velha OX Game . . . . . . . . .
Diagrama de Atividades do Jogo da Velha OX Game . . . . . . . . . .
Diagrama de Classes do Jogo da Velha OX Game referente ao Cliente .
Diagrama de Classes do Jogo da Velha OX Game referente ao Servidor
Localização dos arquivos XML no projeto OX Game . . . . . . . . . .
Primeira parte da tela do Jogo OX Game . . . . . . . . . . . . . . . . .
Primeira e segunda parte da tela do Jogo OX Game . . . . . . . . . . .
Primeira, segunda e terceira parte da tela do Jogo OX Game . . . . . .
Tela do Jogo OX Game . . . . . . . . . . . . . . . . . . . . . . . . . . .
Menu principal do jogo OX Game . . . . . . . . . . . . . . . . . . . . .
Diagrama de sequência do fluxo de dados do jogo OX Game . . . . . .
Fluxo de telas do jogo OX Game . . . . . . . . . . . . . . . . . . . . .
Tela de Aplicativos do Android . . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
36
37
37
40
41
43
44
47
49
51
52
53
54
67
69
70
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
x
4.17
4.18
4.19
4.20
4.21
4.22
4.23
Tela de Apresentação do Jogo . . . . . . . . . . . . . . . . . .
Menu principal do jogo . . . . . . . . . . . . . . . . . . . . . .
Tela de seleção de nı́vel e Tela de inserção do nome do jogador
Tela do jogo da velha e Tela de opções durante uma partida .
Tela com a lista de jogadores online . . . . . . . . . . . . . . .
Aviso de desafio a um jogador . . . . . . . . . . . . . . . . . .
Tela referente ao resultado do jogo . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
70
71
72
72
73
74
74
xi
Lista de Códigos
3.3.1 Código de criação de um objeto ServerSocket . . . . . . . . . . . . . . . . .
26
3.3.2 Código responsável por gerenciar requisições de conexão . . . . . . . . . . .
27
3.3.3 Código de criação dos objetos OutputStream e InputStream no Servidor .
27
3.3.4 Código exemplo de processamento de fluxo de dados . . . . . . . . . . . . .
28
3.3.5 Código de finalização dos fluxos de dados e Socket no Servidor . . . . . . .
28
3.3.6 Código de criação do Socket no Cliente . . . . . . . . . . . . . . . . . . . .
29
3.3.7 Código de criação dos objetos OutputStream e InputStream no Cliente . .
29
3.3.8 Código exemplo de processamento de fluxo de dados no Cliente . . . . . .
29
3.3.9 Código exemplo de processamento de fluxo de dados no Cliente . . . . . .
29
3.3.10Código fonte referente a implementação do servidor . . . . . . . . . . . . .
30
3.3.11Código fonte referente a implementação do cliente . . . . . . . . . . . . . .
31
4.6.1 Código fonte referente a implementação do cliente . . . . . . . . . . . . . .
46
4.6.2 Declaração das caracterı́sticas do arquivo telaJogo.xml . . . . . . . . . . .
48
4.6.3 Codificação da primeira parte da tela do Jogo OX Game . . . . . . . . . .
48
4.6.4 Codificação de um botão do tabuleiro 3x3 do Jogo OX Game . . . . . . . .
49
4.6.5 Codificação do tabuleiro 3x3 do Jogo OX Game . . . . . . . . . . . . . . .
50
4.6.6 Codificação do placar do Jogo OX Game . . . . . . . . . . . . . . . . . . .
51
4.6.7 Codificação dos detalhes de design do Jogo OX Game . . . . . . . . . . . .
52
4.6.8 Requisição de conexao ao Servidor . . . . . . . . . . . . . . . . . . . . . . .
54
4.6.9 Descrição dos métodos criarConexao(ip, porta) e conectar() . . . . . . . . .
55
4.6.10Classe responsável por aguardar o desafio de um oponente . . . . . . . . .
56
4.6.11Loop responsável pela análise das mensagens enviadas pelo servidor . . . .
57
xii
4.6.12Método responsável por avisar o jogador de um desafio e iniciar uma nova
partida . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
57
4.6.13Construtor da classe Partida localizada no servidor . . . . . . . . . . . . .
58
4.6.14Método responsável pelo clique de uma jogada no tabuleiro . . . . . . . . .
58
4.6.15Classe concorrente Analisador . . . . . . . . . . . . . . . . . . . . . . . . .
59
4.6.16Método avisarTerminou(boolean terminou) . . . . . . . . . . . . . . . . . .
60
4.6.17Loop de recebimento de jogadas na classe Mensageiro . . . . . . . . . . . .
60
4.6.18Método avisarReultado(String resultado) . . . . . . . . . . . . . . . . . . .
61
4.6.19Método desconectar() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
61
4.6.20Método desconectar() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
62
4.6.21Inicialização do servidor em uma porta especificada . . . . . . . . . . . . .
62
4.6.22Método desconectar() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
63
4.6.23Código que distingue qual o tipo de jogador . . . . . . . . . . . . . . . . .
64
4.6.24Construtor da classe Partida . . . . . . . . . . . . . . . . . . . . . . . . . .
64
4.6.25Método run() responsável pela rotina do jogo
. . . . . . . . . . . . . . . .
65
4.6.26Método lerJogada() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
65
4.6.27Método desativarJogadaOponente(int pos) . . . . . . . . . . . . . . . . . .
65
4.6.28Método trocarJogador(Jogador jogador) . . . . . . . . . . . . . . . . . . .
66
4.6.29Método desconectar() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
66
4.6.30Método desconectar() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
67
Capı́tulo 1
Introdução
O mercado de celulares está crescendo cada vez mais. Estudo realizado no ano de 2011
pela empresa Teleco - Inteligência em Telecomunicações, mostra que já existiam mais
de 6 bilhões de celulares, isso corresponde a mais ou menos um celular por pessoa no
mundo[Teleco2012]. O crescimento do número de celulares é caracterizado pela busca dos
usuários por diversos recursos como músicas, interface visual, jogos, GPS, acesso a internet
e e-mails, câmera e ainda TV digital[Lecheta2010].
Segundo Lecheta, o mercado corporativo também está crescendo muito, e diversas empresas estão buscando incorporar aplicações móveis ao dia-a-dia no intuito de agilizar negócios. Dessa forma, empresas e desenvolvedores focam em desenvolver aplicativos que visam
negócios e lucros enquanto os usuários buscam dispositivos móveis que abragem as necessidades do cotidiano[Lecheta2010].
Os celulares smartphones, celulares com funcionalidades avançadas que podem ser estendidas por meio de programas executados por seu sistema operacional, estão tendo um
diferencial quanto aos celulares das gerações passadas. Todo esse diferencial pode ser notado por meio das melhorias que vem acontecendo em hardware, como telas maiores e uma
resolução melhor, e em software, com CPUs mais rápidas ajudando na melhoria de desempenho. Além disso, vale ressaltar que esses celulares são capazes de se conectar a Internet
pelos vários tipos de conexão (GSM, GPRS, 3G, WiFi) além de prover de diversos meios
alternativos como tela touchscreen, GPS, camera, etc.
Dentre as áreas de interesse dos usuários, os jogos vem despertando a atenção dos mais
Objetivo
2
variados grupos de pessoas. Permitir que um usuário jogue jogos online com seus amigos
ou qualquer tipo de pessoa, é um entreternimento bastante complexo de ser oferecido, pois
o maior desafio é construir jogos multiusuários. O indı́ce de procura de jogos multiusuários
vem crescendo cada vez mais, pois pessoas jogam colaborando um com os outros ou competindo um com o outro, utilizando o mesmo ambiente, conectados por algum tipo de
rede[Geek2012].
A área de jogos e, principalmente, as aplicações de jogos para dispositivos móveis são
estimulantes pela complexidade dos desafios tecnológicos que apresentam e pelo potencial
de aplicabilidade que existe. O sistema operacional Android[Android2012], que surgiu
voltado para esse avanço tecnológico, permite a construção de jogos desse tipo. Esse sistema
operacional vem conquistando o mercado[NotTec2012] e proporcionando experiências aos
usuários e desenvolvedores, pois é um sistema que engloba as necessidades do cotidiano e
éopen source, facilitando o desenvolvimento de aplicações.
1.1
Objetivo
O objetivo desse trabalho é desenvolver um jogo multiusuário utilizando a plataforma
Android. O jogo é baseado no jogo tradicional e popular Jogo da Velha, no qual permite
a comunicação entre os jogadores a partir das jogadas realizadas.
1.1.1
Especı́ficos
• Aplicar as regras do jogo da velha na construção de um jogo multiusuário.
• Aplicar os conceitos da plataforma Android no desenvolvimento do jogo da velha.
• Aplicar o conceito de sockets na comunicação de dados do jogo.
• Desenvolver o protótipo de uma biblioteca de comunicação multiusuário por meio de
sockets.
Justicativa
1.2
3
Justificativa
Os jogos multiusuários são considerados jogos divertidos por permitirem que pessoas
joguem entre si sem ser necessário estarem próximos um dos outros. Além disso, a implementação de um jogo multiusuário requer a análise e decisão de como o jogo será implementado. Ao desenvolver um jogo desse tipo, há a necessidade de se ater a vários problemas
inerentes ao dispositivo em que é utilizado, como limitação de memória, recursos e processamento, tamanho da tela e mecanismo de entrada não apropriada, acrescido, ainda,
das dificuldades inerentes no desenvolvimento de sistemas distribuı́dos de tempo real como
tempo de resposta, sincronia de instruções, entre outros. Desenvolver um jogo multiusuário
é complexo por envolver muitas áreas de conhecimento e, ao mesmo tempo, um desafio para
quem possui conhecimentos na área de computação.
Esse trabalho se destaca por abordar problemas de infraestrutura de desenvolvimento
de componentes relacionados a jogos em dispositivos móveis e se enquadra em uma área
de pesquisa bem recente, denominada jogos computacionais, que vem despertando muito
interesse da comunidade cientı́fica e de empresas, por revolucionar substancialmente as
aplicações de entretenimento digital. No Brasil, a área de desenvolvimento de jogos ainda
está em processo de evolução, mas é bastante promissora do ponto de vista de empresas,
universidades e em outras áreas do conhecimento[Geek2012].
1.3
Metodologia
O projeto adota a seguinte metodologia de trabalho:
• Estudar e Implementar os conceitos da plataforma Android
• Projetar as telas que o jogo possui.
• Elaborar o fluxo das telas projetadas.
• Implementar uma interface gráfica para o jogo da velha utilizando XML
• Modelar o jogo utilizando a engine do jogo da velha
• Implementar o nı́vel básico para o jogo da velha
Estrutura do Trabalho
4
• Implementar o nı́vel avançado para o jogo da velha
• Implementar o conceito de multiusuário no jogo da velha utilizando socket
• Executar e testar o jogo em dispositivos móveis.
1.4
Estrutura do Trabalho
Nesse trabalho, é apresentado o jogo multiusuário OX Game.
No capı́tulo 2, a
plataforma Android, plataforma utilizada pelo jogo, é descrita, informando o tipo de
plataforma, o kit de desenvolvimento disponı́vel, a linguagem de programação utilizada,
entre outros. No capı́tulo 3, a comunicação de dados entre dispositivos móveis é o foco. No
capı́tulo 4, o desenvolvimento do jogo é tratado e, por fim, no capı́tulo 5, uma conclusão é
apresentada assim como sugestões de futuros desenvolvimentos utilizando o jogo OX Game
como base.
Capı́tulo 2
Plataforma Android
O Android é uma plataforma completa para dispositivos móveis em geral, contendo
um sistema operacional baseado em Linux, middleware, aplicativos e interface de
usuário[Pereira2009].
2.1
História
A tecnologia voltada aos dispositivos móveis está se desenvolvendo cada vez mais e
os seus usuários buscam um dispositivo que melhor atenda as necessidades do cotidiano.
Com base nesse cenário, Andy Rubin (co-fundador da Danger ), Rich Miner (co-fundador
da Wildfire Communications, Inc.), Nick Sears (vice-presidente da T-Mobile) e Chris White
(lı́der de projeto e desenvolvimento de interface na WebTV ) fundaram em outubro de 2003,
na cidade de Palato Alto, Califórnia, Estados Unidos, a Android, Inc. Inicialmente, pouco
se sabia sobre a Android, Inc. em virtude de operarem secretamente e apenas informarem
que estavam trabalhando em software para celulares.
Em julho de 2005, o Google adquiriu a Android, Inc. e os co-fundadores Andy Rubin,
Rich Miner, Nick Sears e Chris White passaram a fazer parte desse novo time. Enquanto
suposições surgiam de que o Google estaria planejando competir no mercado de dispositivos
móveis, o time liderado por Rubin tentava desenvolver uma plataforma poderosa baseado
no kernel do Linux e com a premissa de prover flexibilidade, tornando-o um sistema aberto
História
6
e de fácil atualização.
Segundo Nunes, especulações sobre a intenção do Google em entrar no mercado de
comunicações móveis continuaram a surgir até dezembro de 2006. Houveram rumores de
que o Google estaria desenvolvendo um celular com a sua marca, definindo especificações
técnicas e mostrando protótipos para os fabricantes de celular e operadores de telefonia
móvel. Além disso, em setembro de 2007, a InformationWeek cobriu um estudo da Evalueserve reportando que o Google apresentou vários pedidos de patentes na área de telefonia
móvel[Nunes2011].
Por volta de dois meses após o estudo levantado pela InformationWeek, no dia 05 de
novembro de 2007, um grupo formado por grandes empresas do mercado de telefonia móvel,
sob nome Open Handset Alliance (OHA), é criado e liderado pelo Google. Dentre as várias
empresas, destacam-se a Texas Instruments, Broadcom Corporation, Google, HTC, Intel,
LG, Marvell Technology, Motorola, Nvidia, Qualcomm, Samsung Electronics, Sprint Nextel
e T-Mobile. Segundo Lecheta, no site da OHA existe uma ótima descrição do que seria
essa aliança: ”Hoje, existem 1,5 bilhões de aparelhos de televisão em uso em todo o mundo
e 1 bilhão de pessoas têm acesso à Internet. No entanto, quase 3 bilhões de pessoas têm um
telefone celular, tornando o aparelho um dos produtos de consumo mais bem sucedidos do
mundo. Dessa forma, construir um aparelho celular superior melhoraria a vida de inúmeras
pessoas em todo o mundo. A Open Handset Alliance é um grupo formado por empresas
lı́deres em tecnologia móvel que compartilham essa visão para mudar a experiência móvel
de todos os consumidores[...].”[Lecheta2010]. Assim, o objetivo do grupo era definir uma
plataforma única e aberta para celulares de modo a deixar os consumidores mais satisfeitos
com o produto final além de criar uma plataforma moderna, flexı́vel para o desenvolvimento
de aplicações coorporativas.
Como resultado da OHA, seu primeiro produto consistiu na plataforma Android, uma
plataforma de desenvolvimento para aplicações móveis com sistema operacional baseado
no kernel 2.6 do Linux, interface visual, diversas aplicações instaladas e um ambiente
de desenvolvimento inovador, flexı́vel e poderoso. Essa nova plataforma surgiu a partir
do projeto Android Open Source Project (AOSP) e tem sido acompanhada com diversas
manutenções e atualizações[Lecheta2010][Nunes2011].
Open Handset Alliance (OHA)
2.2
7
Open Handset Alliance (OHA)
O Android surgiu da parceria do Google com a Open Handset Alliance (OHA), uma
aliança onde figuram as principais empresas do mercado móvel mundial. Segundo Pereira,
essa aliança possui a seguinte estrutura[Pereira2009]:
• Operadoras de telefonia móvel: responsável pela conexão, fornecem o serviço
para o usuário final;
• Fabricantes de aparelhos: responsáveis pela criação do hardware;
• Empresas Semicondutores: fazem os chips dos aparelhos celulares;
• Empresas de software: desenvolvem os softwares que serão executados no Android;
• Empresas de comercialização: responsáveis pela divulgação, marketing e comercialização dos produtos para o usuário.
No sı́tio oficial da OHA, é possı́vel verificar as 84 empresas que compõem a aliança,
conforme figura 2.1:
Open Handset Alliance (OHA)
Figura 2.1: Membros da Open Handset Alliance - OHA
8
Licença
2.3
9
Licença
O código fonte do Android está disponı́vel sob licenças livres e de plataforma aberta.
O Google publica a maior parte dos códigos fontes sob a Licença Apache versão 2.0 e o
restante, as mudanças do kernel do Linux, sob a Licença GNU General Public License
versão 2.0. A OHA desenvolve modificações ao kernel do Linux a parte, com código fonte
disponı́vel publicamente a qualquer momento, sob licença GPL do Android. O resto do
Android é desenvolvido em particular, com código fonte liberado publicamente quando
uma nova versão principal é lançada. A única versão cujo cógido fonte não foi publicado
após um lançamento de um nova versão foi a Honeycomb. Como justificativa, o Google
informou que a versão havia sido lançada de forma rápida e que era direcionada a tabletes,
evitando que os usuários a utilizassem em celulares até a mesclagem das funcionalidades
dessa versão com a versão destinada a celulares que viria posteriomente. Com o lançamento
da versão Ice Cream Sandwich, o código fonte foi liberado e assim eliminou as dúvidas sobre
o compromisso com o código aberto do Android[Nunes2011].
2.4
Arquitetura
A arquitetura da plataforma Android está estruturada conforme figura 2.2:
Arquitetura
10
Figura 2.2: Arquitetura da Plataforma Android
Segundo Schemberger, a arquitetura da plataforma Android é dividida em quatro camadas: Kernel GNU Linux, bibliotecas, framework para aplicações e as próprias aplicações - além da porção runtime, necessária para a execução dos aplicativos no dispositivo[Schemberger2012].
Todo o gerenciamento dos processos, threads, arquivos, pastas, redes e drivers dos
dispositivos são gerenciados pelo kernel 2.6 do Linux, a camada mais baixa da arquitetura. O runtime do Android é construı́do em cima do kernel 2.6 do Linux e é responsável por criar e executar aplicações Android. Cada aplicação é executada em seu próprio
processo e com a própria máquina virtual chamada Dalvik. As aplicações Android são
compactadas em um arquivo de formato .apk e armazenadas na pasta de aplicativos
chamada dados, no sistema operacional Android. Essa pasta é acessı́vel apenas para o
usuário root por razões de segurança. As bibliotecas de sistema são as responsáveis por
tarefas computacionalmente pesadas, como renderização gráfica, reprodução de audio e
Segurança
11
acesso ao banco de dados, que não seria adequado para a maquina virtual Dalvik, e a
camada de aplicação, por sua vez, reune as bibliotecas de sistema e o runtime. Essa
camada gerencia as aplicações e fornece um framework elaborado no qual as aplicações
funcionam[Lecheta2010][Pereira2009][Schemberger2012].
2.5
Segurança
Devido o sistema operacional do Android ser baseado no kernel 2.6 do Linux, a segurança do Android é baseada na segurança do Linux. Toda vez que um aplicativo é
instalado em um dispositivo Android, um novo usuário Linux é criado para aquele programa. Cada aplicativo no Android inicia um novo e único processo no sistema operacional, sendo que cada processo possui uma thread especı́fica para sua execução. Esses
processos são nomeados de sandbox, um mecanismo de segurança que separa os programas em execução em um ambiente isolado, com acesso limitado aos recursos do sistema
e com diretórios que serão usados pelo aplicativo e somente pelo usuário Linux correspondente. Como os aplicativos ficam completamente isolados uns dos outros, qualquer
tentativa de acessar informações de outro aplicativo é necessário ser explicitamente autorizado pelo usuário, podendo ser negado a instalação do aplicativo ou autorizado a instalação[Pereira2009][Lecheta2010][Schemberger2012][Nunes2011].
2.6
Caracterı́sticas
Segundo Pereira, o Android é uma plataforma para a tecnologia móvel completa, envolvendo um pacote com programas para celulares, já com um sistema operacional, middleware, aplicativos e interface do usuário. Foi construı́da para ser verdadeiramente aberta
e com intenção de permitir aos seus desenvolvedores criar aplicações móveis que possam
tirar proveito do que um aparelho portátil possa oferecer[Pereira2009].
Por possuir uma plataforma aberta, diversas atualizações são realizadas de modo a incorporar novas tecnologias conforme as necessidades surgem. Cada atualização no Android
refere-se a solução de algum bug ou a adição de alguma funcionalidade nova. Cada versão
é nomeada alfabeticamente por nomes de sobremesa:
O Kit de Desenvolvimento de Software - SDK
12
• Versão 1.5 - Cupcake
• Versão 1.6 - Donut
• Versão 2.0 - Éclair
• Versão 2.2 - Froyo
• Versão 2.3 - Gingerbread
• Versão 3.0/ 3.1/ 3.2 - Honeycomb
• Versão 4.0 - Ice Cream Sandwich
A versão 1.5 (Cupcake) adicionou suporte a bibliotecas nativas as aplicações Android,
que antes eram restritas a serem escritas em Java puro. Código nativo pode ter muitos
benefı́cios em situações onde o desempenho é o mais importante. A versão 1.6 (Donut)
introduziu suporte para diferentes resoluções de tela. Na versão 2.0 (Éclair ) foi adicionado
suporte a telas multi-toques, e na versão 2.2 (Froyo) adicionaram a compilação just-intime (JIT) a máquina virtual Dalvik. O JIT acelera a execução de aplicações Android,
dependendo do cenário, em até 5 vezes. A versão 2.3 (Gingerbread ) adicionou um garbage
collector concorrente ao da máquina virtual Dalvik e refinou a interface de usário, melhorando o teclado virtual e acrescentando a funcionalidade copiar-colar. As versões 3.0/
3.1/ 3.2 (Honeycomb) são orientadas para tabletes e com suporte para dispositivos de telas
grandes, com multiprocessadores e aceleradores de gráficos, sistema de encriptação completa, carregamento de mı́dia a partir de cartões de memória e suporte para dispositivos de
entradas via USB. A versão 4.0 (Ice Cream Sandwich) é a mais recente, anunciada em 19 de
outubro de 2011, e trouxe as funcionalidades da versão 3.0 (honeycomb) para smartphones
além de incluir desbloqueio por reconhecimento facial, controle e monitoramento de dados
via rede, unificação de contatos por redes sociais e busca de e-mails offline.
2.7
O Kit de Desenvolvimento de Software - SDK
O kit de desenvolvimento de software(SDK) é a ferramenta principal para desenvolver
aplicações utilizando a plataforma Android. O SDK inclui um conjunto abrangente de ferra-
O Kit de Desenvolvimento de Software - SDK
13
mentas de desenvolvimento, incluindo um depurador, bibliotecas, um emulador de terminal
móvel (baseada em QEMU - uma máquina virtual de código aberto para simular diferentes
plataformas de hardware), documentação, código de exemplo e tutoriais. As plataformas
de desenvolvimento suportadas incluem computadores que executam o Linux (qualquer distribuição Linux moderna), Mac OS X 10.4.9 ou superior e Windows XP ou posterior. O ambiente de desenvolvimento integrado (IDE) suportado oficialmente é o Eclipse (versões 3.4,
3.5 ou 3.6) usando o Android Development Tools (ADT) Plugin, embora os desenvolvedores
possam usar qualquer editor de texto para editar arquivos Java e XML e, em seguida, usar
ferramentas de linha de comando (Java Development Kit e Apache Ant são necessários)
para criar, construir e depurar aplicativos do Android[Lecheta2010][Schemberger2012].
Em 12 de novembro de 2007, ocorreu o pré-lançamento do SDK do Android. Em 15
de Julho de 2008, com o concurso de desenvolvimento de aplicações em Android, Android
Developer Challenge, a equipe organizadora acidentalmente enviou um e-mail a todos os
participantes do Android Developer Challenge anunciando que uma nova versão do SDK
estava disponı́vel em uma área privada de download. O e-mail era destinado somente
para os vencedores da primeira rodada do concurso. A revelação de que o Google estava
fornecendo novas versões do SDK para alguns desenvolvedores e não para outros, levou
muitos à frustração, sendo divulgado dentro da comunidade de desenvolvimento para Android.
Em 18 de Agosto de 2008, o Android SDK beta 0.9 foi liberado. Esta versão forneceu
uma API atualizada e ampliada, ferramentas de desenvolvimento melhoradas e um design
atualizado para a tela inicial. Instruções detalhadas para a atualização estavam disponı́veis
para aqueles que já trabalhavam com uma versão anterior. Em 23 de Setembro de 2008,
o Android SDK 1.0 foi lançado, e de acordo com as notas de lançamento, incluiu principalmente correções de bugs além de terem algumas funcionalidades adicionadas. Várias
versões foram lançados desde então.
Melhorias no SDK do Android estão fortemente ligadas com o desenvolvimento da
plataforma Android. O SDK também oferece suporte a versões mais antigas da plataforma,
no caso de desenvolvedores desejarem atingir suas aplicações em dispositivos mais antigos.
As ferramentas de desenvolvimento são componentes de download, sendo assim, depois
de se ter baixado a última versão e plataforma, plataformas mais antigas e ferramentas
O Kit de Desenvolvimento de Software - SDK
14
também pode ser baixadas para realizar testes de compatibilidade[Schemberger2012].
Segundo Nunes e Schemberger, as caracterı́sticas gerais do SDK são:
• O depurador, capaz de depurar aplicações executando em um dispositivo ou emulador;
• O profile de memória e desempenho, que ajudam a encontrar vazamentos de memória
e identificar trechos de código lento;
• O emulador de dispositivos, baseado em QEMU (uma máquina virtual de código
aberto para simular diferentes plataformas de hardware), que, apesar de preciso,
pode ser um pouco lento algumas vezes;
• Utilitários de linha de comando, que comunicam com os dispositivos;
• Scripts prontos e ferramentas para empacotamento e instalação de aplicações[Nunes2011][Schemberger2012].
Capı́tulo 3
Programação Distribuı́da usando
Android
Um sistema distribuı́do em Android é definido como um sistema no qual os componentes
de uma aplicação, localizado nos dispositivos móveis, interligados em rede, se comunicam
e coordenam suas ações apenas enviando e recebendo mensagens entre si.
3.1
Componentes das Aplicações em Android
As aplicações em Android podem ser divididas em quatro tipos de componentes básicos
que são definidos pela própria arquitetura[Ableson2007], que são:
• Activities
• Broadcast e Intent Receivers
• Services
• Content Providers
Nem toda aplicação precisa possuir todos os tipos de componentes básicos, mas pode
ser construı́da a partir de alguma combinação entre eles. Uma vez decidido quais componentes são necessários para a construção de um aplicativo, deve-se listá-los em um arquivo
Componentes das Aplicações em Android
16
chamado AndroidManifest.xml. Segundo Pereira, toda aplicação deve ter um arquivo AndroidManifest.xml no diretório raiz. Este arquivo de configuração descreve os elementos da
aplicação, as classes de cada componente a ser utilizado, qual o tipo de dado ele pode tratar,
quando pode ser ativado, ou seja, serve para definir os dados de cada elemento[Pereira2009].
Na figura 3.1, a estrutura básica de um projeto Android é representado. O projeto
ClienteAndroid possui a biblioteca de pacotes referente a versão 2.2 do Android, uma pasta
chamada sourcing (src) - contém todas as classes e activities especificadas pelo usuário
(ClienteAndroid, GerenciadorConexoes, Mensageiro e Mensagem), uma pasta chamada
generated Java files (gen)- contém arquivos que consistem em referências estáticas para
todos os recursos que possam ser referenciadas de forma fácil e dinâmica a partir do código
Java, uma pasta chamada resources (res) - contém todos os recursos do projeto: ı́cones,
imagens, cadeias de caracteres e layouts, e o arquivo AndroidManifest.xml.
Figura 3.1: Exemplo de um projeto Android
Componentes das Aplicações em Android
3.1.1
17
Activities
Activity, ou atividade, é uma tela da aplicação. A maioria das aplicações consiste de
várias telas. Por exemplo, uma aplicação de mensagens de texto, deve possuir uma tela
para exibir a lista de contatos, possı́veis destinatários da mensagem, uma segunda tela
para escrever a mensagem para o contato selecionado, e outras telas para ver as mensagens
antigas e alterar configurações. Cada uma dessas telas deve ser implementada como uma
activity. Cada activity é implementada como uma única classe que estende a classe de
base Activity. Essa classe exibe a interface com o usuário, responde a eventos e segue um
ciclo de vida especı́fico. O ciclo de vida de uma activity está representada conforme figura
3.2[Pereira2009]:
Componentes das Aplicações em Android
Figura 3.2: Ciclo de vida de uma Activity
18
Componentes das Aplicações em Android
19
• OnCreate(): método chamado quando a activity é inicialmente criada. É um método
obrigatório, chamado apenas uma vez.
• OnStart(): chamado quando a activity torna-se visı́vel para o usuário.
• OnResume(): é o topo da pilha de activity, chamado quando vai iniciar a interação
com o usuário.
• OnPause(): chamado quando o sistema está perto de começar uma próxima activity.
É utilizado para gravar as informações que ainda não foram salvas.
• OnStop(): é chamado quando a activity não estiver mais sendo utilizada pelo usuário
e perdeu o foco para outra activity.
• OnDestroy(): pode ser chamado quando a activity terminou, ou quando o sistema
precisa finalizar activities para liberação de recursos.
• OnRestart(): chamado quando a activity está interrompida e prestes a ser acionada
pelo usuário novamente.
Segundo Lecheta, uma activity tem um ciclo de vida bem definido. Quando uma
activity é iniciada, ela é inserida no topo de uma pilha, chamada de activity stack ou
pilha de atividades, e a activity anterior que estava em execução permanece abaixo dessa
nova[Lecheta2010].
As activities podem se encontrar em quatro estados[Pereira2009]:
• Executando: a activity está ativa no visor do dispositivo.
• Parada: uma activity que perdeu o foco para outra, mantendo todas as informações
de estado, porém não está interagindo com o usuário. Pode, ainda, ser finalizada em
situações de baixo nı́vel de memória disponı́vel.
• Interrompida: caso uma activity não esteja sendo utilizada pelo usuário, ela mantém as suas informações de estado, porém são muitas vezes finalizadas quando uma
recuperação de memória seja necessária, perdendo informações.
Componentes das Aplicações em Android
20
• Finalizada: o sistema pode remover uma activity da memória, caso esteja interrompida ou parada. O estado anterior só pode ser restaurado se os métodos tiverem sido
implementados pelo desenvolvedor.
Segundo a documentação do Android, existem três subnı́veis do ciclo de vida principal,
que por sua vez ficam se repetindo durante a execução da aplicação. Esses três ciclos são
basicamente[AndroidDev2012][Lecheta2010]:
• Entire Lifetime: inicia-se no método OnCreate(), onde a activity realiza toda a configuração, passa de um estado para outro e termina no método OnDestroy(), quando
todos os recursos utilizados por esta activity são liberados.
• Visible Lifetime: inicia-se no método OnStart() e termina no método OnStop. É
o ciclo onde a activity está disponı́vel e visı́vel para o usuáro, mesmo que este não
esteja interagindo com ela. Quando o usuário não visualizar mais a activity, o método
OnStop() é chamado, ou então, para colocar a activity visı́vel, chama o método
OnStart().
• Foreground Lifetime: ocorre entre os métodos OnResume() e OnPause(). Este é o
estado em que a activity está no topo da pilha de atividades e interagindo com o
usuário. Durante esse ciclo, a activity pode alternar frequentemente entre os estados
executando e pausado, sendo assim, recomendado que o código que executa os métodos OnPause() e OnResume() seja leve e rápido, proporcionando a troca dos estados
sem a necessidade de uma requisição robusta.
3.1.2
Broadcast e Intent Receivers
Brodacast e Intent Receivers são componentes que ficam aguardando a ocorrência de
um determinado evento. Um evento, nesse caso, pode ser a inicialização do sistema operacional, uma chamada de voz, a chegada de um SMS ou um evento disparado por uma
aplicação[Meier2009].
Intents são elementos chave no Android porque facilitam a criação de novas aplicações a
partir de aplicações já existentes. A classe Intent interage com outras aplicações e serviços
que proporcionam informações necessárias para uma aplicação.
Componentes das Aplicações em Android
21
Segundo Lecheta, uma intent está presente em todas as aplicações Android e representa
a necessidade da aplicação realizar algum processo. Uma intent é enviada ao sistema operacional como uma mensagem, chamada broadcast. Quando o sistema operacional recebe
essa mensagem, decisões são tomadas dependendo do processo a ser realizado. Uma intent
pode ser utilizada para:
• abrir uma nova tela de aplicação.
• solicitar ao sistema operacional que ligue para determinado número de celular.
• abrir o browser em um determinado endereço da internet.
• exibir algum endereço, localização ou rota no Google Maps.
• executar algum processamento pesado em segunda plano.
• abrir o mercado de aplicativos oficiais do Android (Play Store) para fazer a instalação
de um determinado aplicativo.
• enviar uma mensagem para outra aplicação para executar algum outro processo[Lecheta2010].
3.1.3
Services
Segundo Pereira, um service, que significa serviço, é um codigo sem interfaces de
usuário, que rodam em segundo plano, não sendo interrompidos quando da troca de activities pelo usuário. Ao contrário da activity que tem um ciclo de vida próprio e um
vida curta, os services mantêm o serviço ativo até que seja recebida outra ordem. Uma vez
conectado com o serviço, pode-se comunicar com este através de uma interface apresentada
para o usuário[Pereira2009].
Um bom exemplo é um media player reproduzindo músicas a partir de uma lista de
músicas. Em uma aplicação deste tipo, há uma ou mais activities que permitem ao usuário
escolher músicas e reproduzi-las. Entretanto, o playback da música não pode ser tratado
por uma atividade, pois o usuário espera que a música continue tocando mesmo depois que
ele tenha mudado de tela. Neste caso, a atividade do media player pode iniciar um serviço
Programação Distribuída usando Android
22
para rodar em segunda plano e manter a música tocando. O sistema mantém o serviço de
playback da música executando até que o serviço seja terminado.
3.1.4
Content Providers
Segundo Lecheta, o Android permite armazenar diversas informações utilizando banco
de dados, arquivos e o sistema de preferências. Contudo, geralmente essas informações
ficam salvas dentro do pacote da aplicação, e somente a aplicação que criou o banco de
dados ou o arquivo pode ter acesso às informações[Lecheta2010].
Content Provider, ou provedor de conteúdos, pode ser interpretado como uma classe que
permite que determinadas informações sejam públicas para todas as outras aplicações instaladas no sistema operacional. Utilizando essa classe, outras aplicações podem consultar,
inserir, alterar e excluir informações.
3.2
Programação Distribuı́da usando Android
Os aplicativos para Android são programados na linguagem Java, mas são compilados
e executados na máquina virtual Dalvik - máquina virtual do sistema operacional Android.
A IDE oficial para desenvolvimento em Android é o Eclipse, que fornece um ambiente Java
rico, incluindo ajuda sensı́vel ao contexto e sugestões de códigos[IBM2012].
Segundo Albuquerque, a linguagem Java apresenta caracterı́sticas que facilitam a implementação de aplicações distribuı́das. A linguagem é portátil e independente de plataforma,
isto é, o código pode ser desenvolvido em uma plataforma e executado em outra plataforma
diferente. Quando um programa Java é compilado, em vez de ser gerado um código destinado a uma plataforma especı́fica, é gerado um código para uma máquina virtual. Qualquer
máquina pode executar este código desde que saiba como interpretar as instruções geradas.
Esta é uma abordagem diferente de outras linguagens de programação onde o código fonte
precisa ser compilado para a plataforma onde será executado. Outra caracterı́stica útil
da linguagem Java no desenvolvimento de aplicações distribuı́das é o suporte a múltiplas
threads de execução. Isto facilita a implementação, por exemplo, de programas servidores.
Em tais programas pode ser necessário atender simultaneamente a solicitação de múltiplos
Programação Distribuída usando Android
23
clientes[Albuquerque2012].
A programação distribuı́da em Android utiliza classes existentes da linguagem Java e
pacotes de comunicação em rede disponibilizadas pelo SDK. Esses pacotes são conhecidos
como interfaces de programação de aplicativos - APIs (Application Programming Interface)
para comunicação entre aplicações. Essas APIs abstraem muitos dos detalhes de configuração e implementação necessários em outras linguagens além de diminuir o volume de
código da aplicação destinado a aspectos de comunicação e recursos de rede. Uma API é um
recurso de rede multiuso que permite que os desenvolvedores Java possam criar aplicativos
com esses pacotes. Um exemplo de API de comunicação, dentre outras, é o Socket.
Segundo Vieira, a arquitetura de um sistema distribuı́do é basicamente definido em
duas: a arquitetura cliente-servidor e a arquitetura ponto-a-ponto. Existem também variações destas arquiteturas que visam aumentar o desempenho e contornar alguns problemas ou necessidades especı́ficas que estes sistemas possam apresentar para diferentes
aplicações[Vieira2011].
3.2.1
Arquitetura Cliente-Servidor
A arquitetura Cliente-Servidor é o modelo mais tradicional para aplicações online. Essa
arquitetura constitui a maneira mais versátil de distribuir mensagens para um grande
número de usuários e é caracterizada por ser centralizada, ou seja, existe um servidor que
possui uma visão completa e permanentemente atualizada de todos os clientes conectados
a ele. Na figura 3.3, pode-se observar um diagrama representando-a:
Programação Distribuída usando Android
24
Figura 3.3: Diagrama representando a arquitetura cliente-servidor
Segundo Andrews, o principal problema desta arquitetura é o excesso de carga depositada no servidor. Muitas vezes os servidores precisam atender centenas ou milhares de
conexões, o que exige um significativo esforço computacional. Outro ponto importante é a
natureza centralizada da arquitetura, pois qualquer falha no hardware ou na conectividade
da estação servidora, todas as conexões existentes podem ser abortadas[Andrews2000].
3.2.2
Arquitetura Ponto-a-Ponto
A arquitetura ponto-a-ponto é definida por computadores que se encontram em rede e
que estão todos interligados em uma cadeia descentralizada, onde cada um possui funções
equivalentes, não havendo uma hierarquia entre eles. Todos os usuários são clientes e
servidores, funcionando, assim, de forma totalmente independente e livre da existência de
um servidor central. A figura 3.4 mostra a representação dessa arquitetura:
Serviços de Comunicação
25
Figura 3.4: Diagrama representando a arquitetura ponto-a-ponto
Segundo Vieira, pelo fato dessa arquitetura ser caracterizada como descentralizada e
robusta em relação aos problemas que afetam a arquitetura cliente-servidor, novos questionamentos surgem com esta descentralização. A largura de banda limitada de cada
”ponto”, a troca de dados entre os ”pontos”, a garantia de autenticidade e segurança dos
dados são alguns desses questionamentos. Todavia, uma arquitetura ponto-a-ponto estruturada, permite que técnicas de roteamento e organização sejam utilizadas com certa
eficiência, ao mesmo tempo em que a rede se torne escalável para uma grande quantidade
de clientes[Vieira2011].
3.3
Serviços de Comunicação
Aplicações distribuı́das usam os serviços de comunicação providos pela rede através de
interfaces de programação de aplicativos(APIs). Os recursos fundamentais de rede utilizados pelo Android são declarados pelas classes e interfaces do pacote java.net, no qual possui
funções que permitem o estabelecimento de comunicações baseadas em fluxo. Por intermé-
Serviços de Comunicação
26
dio dessas comunicações, os aplicativos visualizam a rede como um fluxo de dados, ou seja,
enviando e recebendo dados de forma contı́nua por essa rede[Albuquerque2012][IBM2012].
O Socket é exemplo de serviço de comunicação. Permite que um processo estabeleça
uma conexão com outro processo. Enquanto a conexão estiver no ar, os dados fluem entre
os processos em fluxos contı́nuos. Pode-se dizer que os Sockets são baseados em fluxo e que
fornecem um serviço orientado para conexão. Essa comunicação é feita através das classes
ServerSocket e Socket. O servidor usa a classe ServerSocket para aguardar conexões a partir
dos clientes e, quando ocorre a conexão, a comunicação é efetivada através de um objeto
da classe Socket. Estas classes escondem a complexidade presente no estabelecimento de
uma conexão e no envio de dados através da rede.
3.3.1
Implementando um servidor utilizando Sockets de fluxo
Implementar um servidor utilizando Sockets de fluxo e que trata as conexões e se comunica com os possı́veis clientes requer cinco passos[Deitel2010]:
1. Criar um objeto ServerSocket.
2. Esperar um conexão.
3. Obter os fluxos de entrada e saı́da do socket.
4. Realizar o processamento (comunicação).
5. Fechar a conexão.
No passo 1, deve-se criar um objeto do tipo ServerSocket. A criação desse objeto é
realizado pela chamada do construtor ServerSocket, demonstrado no código 3.3.1:
1
ServerSocket servidor = new ServerSocket( porta, limiteDeConexoes );
Código 3.3.1: Código de criação de um objeto ServerSocket
No construtor ServerSocket existem dois parâmetros. O parâmetro porta registra o
número da porta TCP disponı́vel para a conexão e o parâmetro limiteDeConexoes especifica
um número máximo de clientes que podem esperar para se conectar ao servidor, ou seja, o
Serviços de Comunicação
27
número máximo de conexões. O número da porta é utilizado pelos clientes para localizar
o aplicativo servidor no computador servidor. Isso costuma ser chamado de ponto de
handshake.
No passo 2, o servidor deve gerenciar as requisições de conexão dos clientes, para tanto
deve ser criado um objeto do tipo Socket para cada requisição aceita pelo objeto ServerSocket. O servidor passa a ouvir indefinidamente uma tentativa de conexão por meio do
método ServerSocket accept. Esse método encontra-se descrito no código 3.3.2:
1
Socket conexao = server.accept();
Código 3.3.2: Código responsável por gerenciar requisições de conexão
Esse método retorna um Socket quando uma conexão com um cliente é estabelecida.
O Socket, então, permite ao servidor interagir com o cliente. As interações com o cliente
ocorrem em uma porta diferente de servidor a partir do ponto de handshake, permitindo,
assim, que a porta especificada no passo 1 seja utilizada novamente em um servidor de
múltiplas threads de modo a aceitar outras conexões.
No passo 3, após a conexão entre o servidor e o cliente, faz-se necessário que a comunicação seja possı́vel. Desse modo, os objetos do tipo OutputStream e InputStream devem
ser criados de modo a permitir que o servidor se comunique com o cliente enviando e
recebendo bytes. O servidor envia informações ao cliente via um objeto OutputStream e
recebe informações do cliente via um objeto InputStream. Além disso, o servidor invoca o
método getOutputStream no Socket para obter a referência ao OutputStream do Socket e
invoca o método getInputStream no Socket para obter uma referência ao InputStream do
Socket. Esse passo encontra-se demonstrado no código 3.3.3:
1
2
ObjectInputStream entradaDeDados = new ObjectInputStream( conexao.getInputStream() );
ObjectOutputStream saidaDeDados = new ObjectOutputStream( conexao.getOutputStream() );
Código 3.3.3: Código de criação dos objetos OutputStream e InputStream no Servidor
No passo 4, a comunicação é realizada entre o servidor e o cliente via os objetos OutputStream e InputStream. Como exemplo, o código 3.3.4 utiliza os métodos writeObject(objeto) e readObject() de modo a enviar uma mensagem de sucesso referente a conexão
Serviços de Comunicação
28
realizada.
1
2
3
4
String mensagem = "Conexao realizada com sucesso!";
saidaDeDados.writeObject( mensagem );
saidaDeDados.flush();
String resposta = (String) entradaDeDados.readObject();
Código 3.3.4: Código exemplo de processamento de fluxo de dados
É importante lembrar que existem vários métodos que possibilitam a leitura e escrita a
partir de Streams. No exemplo mencionado, os métodos utilizados são de escrita e leitura
de objetos, no caso, especificamente, são enviadas e recebidas Strings. Entretanto é possı́vel
enviar e receber vários outros objetos, tipos primitivos, entre outros.
No passo 5, após a transmissão ter sido realizada, faz-se necessário finalizar os objetos
de comunicação, assim como a conexão do Socket. O encerramento ocorre utilizando o
método close() nos objetos InputStream e OutputStream e no Socket, conforme código
3.3.5:
1
2
3
entradaDeDados.close();
saidaDeDados.close();
conexao.close();
Código 3.3.5: Código de finalização dos fluxos de dados e Socket no Servidor
3.3.2
Implementando um cliente utilizando Sockets de fluxo
A implementação do cliente é bastante semelhante ao servidor. Para realizar essa
implementação se faz necessário executar quatro passos:
1. Criar um Socket.
2. Obter os fluxos de entrada e saı́da do socket.
3. Realizar o processamento (comunicação).
4. Fechar a conexão.
No passo 1, deve-se criar um Socket de modo a conectar-se ao servidor. O construtor
do Socket, conforme código 3.3.6, estabelece a conexão.
Serviços de Comunicação
1
29
Socket conexaoCliente = new Socket( enderecoDoServidor, porta );
Código 3.3.6: Código de criação do Socket no Cliente
O construtor do Socket possui duas variáveis: enderecoDoServidor e porta. A requisição
de conexao ao servidor é realizada indicando o endereço e a porta no qual a aplicação está
hospedada. Se a tentativa de conexão for bem sucedida, essa instrução retorna um Socket.
Caso contrário, uma falha de exceção de entrada e saı́da é invocada.
No passo 2, o cliente também necessita criar objetos InputStream e OutputStream e
utilizar os métodos getInputStream e getOutputStream para obter as referências ao InputStream e OutputStream do Socket. Desse modo, caso o servidor envie valores com um objeto
ObjectOutputStream, o cliente deverá ler esses valores com um objeto ObjectInputStream.
O código 3.3.7 relata essa implementação no cliente:
1
2
ObjectInputStream entrada = new ObjectInputStream( conexaoCliente.getInputStream() );
ObjectOutputStream saida = new ObjectOutputStream( conexaoCliente.getOutputStream() );
Código 3.3.7: Código de criação dos objetos OutputStream e InputStream no Cliente
No passo 3, ocorre a comunicação entre o cliente e servidor. As informações são enviadas
e recebidas utilizando os objetos InputStream e OutputStream. Como exemplo, o código
3.3.8 demonstra a informação recebida do servidor referente a confirmação de conexão e,
em seguida, o cliente encaminha uma outra mensagem.
1
2
3
4
String confirmacaoDeConexao = (String) entrada.readObject();
String ok = "OK!";
saida.writeObject(ok);
saida.flush();
Código 3.3.8: Código exemplo de processamento de fluxo de dados no Cliente
No passo 4, o cliente encerra os fluxos de dados e finaliza a conexão com o servidor.
Nesse caso, o método close é utilizado, conforme código 3.3.9:
1
2
3
entrada.close();
saida.close();
conexaoCliente.close();
Código 3.3.9: Código exemplo de processamento de fluxo de dados no Cliente
Serviços de Comunicação
3.3.3
30
Estabelecendo uma comunicação utilizando a arquitetura
cliente-servidor
A partir da utilização de Sockets de fluxo, uma comunicação entre cliente e servidor
pode ser implementada. Executando os passos para a implementação do servidor e do
cliente, demonstrado, respectivamente, nos subtópicos 3.3.1 e 3.3.2, pode-se obter uma
conexão e realizar a troca de informações. No código 3.3.10 é demonstrado a implementação
completa de um servidor simples que tem como objetivo receber uma conexão e enviar uma
confirmação ao cliente caso tenha sido bem sucedida.
1
2
3
4
5
6
7
8
9
10
11
12
import
import
import
import
import
java.io.ObjectInputStream;
java.io.ObjectOutputStream;
java.net.InetAddress;
java.net.ServerSocket;
java.net.Socket;
public class Servidor {
public static void main(String[] args) {
try {
//Passo 1: Criando um objeto ServerSocket
ServerSocket server = new ServerSocket(9876, 10);
System.out.println("Servidor -> Esperando Conexao.");
13
14
15
//Passo 2: Esperando uma conexao
Socket conexao = server.accept();
16
17
18
//Passo 3: Obtendo os fluxos de entrada e saida
ObjectInputStream entradaDeDados = new ObjectInputStream(conexao.getInputStream());
ObjectOutputStream saidaDeDados = new ObjectOutputStream(conexao.getOutputStream());
19
20
//Passo 4: Realizando o processamento/comunicacao
String mensagem = "Conexao realizada com sucesso!";
saidaDeDados.writeObject( mensagem );
saidaDeDados.flush();
String resposta = (String) entradaDeDados.readObject();
System.out.println("Servidor -> " + resposta);
21
22
23
24
25
26
27
28
//Passo 5: Finalizando os fluxos de dados e o socket
saidaDeDados.close();
entradaDeDados.close();
conexao.close();
} catch (Exception e) {
e.printStackTrace();
}
29
30
31
32
33
34
35
36
}
}
Código 3.3.10: Código fonte referente a implementação do servidor
No código 3.3.11 é demonstrado a implementação de um cliente de modo a requerer
uma conexão com o servidor. Caso a conexão seja realizada com sucesso, o cliente receberá
uma mensagem de sucesso e, em seguida, enviará uma outra mensagem de modo a informar
ao servidor que a comunicação está sendo efetuada corretamente.
Serviços de Comunicação
1
2
3
4
5
6
7
8
9
10
11
import
import
import
import
java.io.IOException;
java.io.ObjectInputStream;
java.io.ObjectOutputStream;
java.net.Socket;
public class Cliente {
public static void main(String[] args) throws ClassNotFoundException{
try {
//Passo 1: Criando um Socket
Socket conexaoCliente = new Socket("192.168.0.10", 9876);
//Passo 2: Obtendo os fluxos de entrada e saida do socket.
ObjectOutputStream saida = new ObjectOutputStream(conexaoCliente.getOutputStream());
ObjectInputStream entrada = new ObjectInputStream(conexaoCliente.getInputStream());
12
13
14
15
16
//Passo 3: Realizando o processamento
String confirmacaoDeConexao = (String) entrada.readObject();
String ok = "OK!";
saida.writeObject(ok);
saida.flush();
System.out.println(confirmacaoDeConexao);
17
18
19
20
21
22
//Passo 4: Fechando os fluxos de entrada, de saida e o socket
saida.close();
entrada.close();
conexaoCliente.close();
}catch (IOException e) {
System.out.println("Erro: "+ e);
}
23
24
25
26
27
28
29
30
31
31
}
}
Código 3.3.11: Código fonte referente a implementação do cliente
Ao executar o código 3.3.10 - código referente ao servidor, observa-se, na figura 3.5, que
o mesmo está aguardando a requisição de uma conexão:
Figura 3.5: Servidor aguardando uma requisição de conexão
Serviços de Comunicação
32
Ao executar o código 3.3.11 - código referente ao cliente, o servidor aceita a conexão e
envia a mensagem de confirmação. Após o recebimento dessa mensagem, o cliente envia
uma mensagem de modo a mostrar que o fluxo de dados ocorre normalmente. Na figura
3.6 temos a mensagem de confirmação no cliente, assim como na figura 3.7, a mensagem
no servidor de que o fluxo ocorre normalmente.
Figura 3.6: Confirmação de conexão do cliente com o servidor
Figura 3.7: Confirmação de fluxo de dados no servidor
Por fim, ambos fluxos e Sockets são finalizados, terminando assim todo esse processo.
Na figura 3.8 é representado o diagrama de sequência referente a essa comunicação. Em
resumo, o cliente cria um objeto Socket (new Socket()) e realiza a conexão com o servidor
(representado no diagrama pelo método conectar()). Por sua vez, o servidor autentica essa
Serviços de Comunicação
33
conexão e o fluxo de dados se inicia logo após os objetos (Streams) de entrada e saı́da serem
criados. Terminando a comunicação, esses objetos são finalizados (fecharStreams()) assim
como as respectivas conexões fecharConexao().
Figura 3.8: Exemplo de um projeto Android
Capı́tulo 4
Desenvolvimento
Este capı́tulo apresenta o desenvolvimento do jogo da velha multisuário online chamado
OX Game, onde são descritas as etapas de desenvolvimento, a abstração da arquitetura
utilizada e a codificação do sistema.
4.1
Etapas de Desenvolvimento
O processo de desenvolvimento de um jogo requer decisões que devem ser avaliadas de
modo a torná-lo mais funcional e atraente. Decisões como a metodologia de desenvolvimento a ser utilizada, o tipo de arquitetura e que linguagem de programação escolher são
cruciais para construir um jogo de qualidade. Segundo Bates, o desenvolvimento de jogos
não requer somente a programação, mas diversas etapas antes e depois da codificação. As
etapas mencionadas por Bates são descritas a seguir[Bates2004]:
• Reunião Criativa
Técnica adotada de modo a obter idéias de construção do jogo, baseada em uma
reunião chamada de brainstorming - ”tempestade de idéias”em português brasileiro.
• Rascunho
Definição das principais caracterı́sticas do jogo (interfaces, modo do jogo, regras,
fluxos, entre outros).
• Detalhamento
Momento de descrever as principais caracterı́sticas definidas na etapa Rascunho, como
quais ações serão desempenhadas pelo jogador, o sistema de pontuação, o estilo do
jogo, etc.
O Projeto
35
• Game Design Document
Etapa em que o jogo precisa ser descrito em um documento fı́sico, um roteiro que
permite guiar a construção do jogo durante o desenvolvimento e codificação.
• Level Design
É a etapa que mapeia todos os desafios que o jogador deve confrontar durante uma
partida do jogo.
• Produção de Arte
Construção do efeito sonoro e efeitos visuais do jogo.
• Integração
Etapa que realiza a integração entre a lógica do jogo com a linguagem de programação
definida e os mecanismos adotados pela plataforma a ser utilizada.
• Versão Beta
Versão que engloba as versões Alpha (versões parciais que são definidades com o
acréscimo de cada funcionalidade) e é composta por todas as funcionalidades propostas.
• Teste
Etapa que realiza testes de funcionalidade a fim de detectar qualquer erro até obter
a versão final do jogo.
4.2
O Projeto
As decisões do projeto de desenvolvimento do jogo OX Game estão descritas a seguir:
• Android versão 2.1 ou superior: compatı́vel com celulares de alta tecnologia e que
possui conectividade 2G, 3G e Wifi.
• Controles na tela (Touchscreen): uso da tela para a manipulação do jogo e que está
disponı́vel na maioria dos celulares que possui a plataforma Android.
• Jogo da Velha Tradicional: baseado em partidas entre dois jogadores e realizadas em
um tabuleiro 3x3 (9 posições).
Jogo da Velha
4.3
36
Jogo da Velha
É um jogo casual e que possui regras e caracterı́sticas acessı́veis a um público infantil,
juvenil e adulto. Esse jogo surgiu no Egito, mas o nome foi originado na Inglaterra quando
mulheres se reuniam a tarde para conversar e bordar. As mulheres idosas, por serem
impossibilitadas de bordar devido a problema oftamológicos, jogavam esse jogo simples,
que passou a ser conhecido como o da ”velha”.
4.3.1
Regras
O jogo da velha utiliza um tabuleiro 3x3, ou seja, uma matriz composta de três linhas e
três colunas. As partidas acontecem entre dois jogadores que fazem suas marcas em umas
das posições livres do tabuleiro. Geralmente os sı́mbolos utilizados são um cı́rculo (O) e
um xis (X), como mostrado na figura 4.1.
Figura 4.1: Tabuleiro 3x3 e sı́mbolos padrões utilizados no Jogo da Velha
Cada jogador realiza uma jogada enquanto existir posições livres no tabuleiro. As
jogadas são alternadas até que um jogador vença a partida ou ao completar o tabuleiro,
ocasionando um empate. Um jogador é considerado vencedor quando consegue colocar três
marcas em sequência, podendo ser no sentido horizontal, no sentido vertical ou diagonal,
de acordo com a figura 4.2. Dessa forma, o jogo consiste que um jogador coloque três
marcas em um dos sentidos que decide a vitória enquanto tenta evitar que seu oponente
faço o mesmo antes.
Arquitetura do Jogo
37
Figura 4.2: Possibilidade de Vitoria no Jogo da Velha
4.4
Arquitetura do Jogo
Ao desenvolver um jogo, é necessário analisar qual arquitetura é a mais adequada para
atender as funcionalidades requeridas. O jogo da velha ”OX Game” utiliza a arquitetura
cliente-servidor (explicado no subtópico 3.2.1) e é formado por quatro componentes principais, mostrados na figura 4.3:
Figura 4.3: Arquitetura do Jogo OX Game
A figura mostra quatro clientes, hospedados em dispositivos móveis diferentes, e um
servidor simples, em uma máquina (computador). Os quatros componentes possuem as
seguintes funções:
• Interface de Usuário
A interface de usuário, denominado pela letra ”I”, é uma unidade de interface no qual
é responsável por exibir ao jogador toda e qualquer funcionalidade do jogo, como por
exemplo o menu de opções e a própria funcionalidade de jogar.
Modelagem
38
• Cliente
O cliente, denominado pela letra ”C”, gerencia as comunicações com a interface e
com o servidor. A comunicação usuário-interface é realizada pela navegação de telas
do aplicativo. A comunicação com o servidor é obtida durante um jogo no qual um
determinado jogador mantém a partida com seus adversários, possibilitando, assim,
o fluxo das jogadas.
• Servidor
O servidor, denominado pela letra ”S”, é o responsável por processar e gerenciar o
jogo e as partidas que estão sendo jogadas pelos usuários. Para cada dois clientes
conectados e desafiados entre si, o servidor é responsável por conectá-los e iniciar a
partida.
• Partida
A partida, denominado pela letra ”P”, representa um jogo entre dois jogadores. O
gerenciamento e controle de quem é o vencedor, perdedor e se houve ou não um
empate é realizado pela partida, cabendo apenas ao servidor informar aos jogadores
o resultado.
• Mensageiro
O mensageiro, denominado pela letra ”M”, é o responsável por realizar a comunicação entre o cliente e o servidor. Essa comunicação é realizada utilizando sockets
(explicado no tópico 3.3). Esse componente esconde a interface baixo-nı́vel de socket
entre os dispositivos móveis e o computador que hospeda o servidor.
4.5
Modelagem
Segundo Booch, a modelagem de um sistema se caracteriza por ser uma parte central de
todas as atividades que levam à implantação de um bom software. Modelos são construı́dos
para comunicar a estrutura e o comportamento desejado do sistema, para visualizar e
controlar a arquitetura escolhida, para compreender melhor o sistema que deseja elaborar
e gerenciar possı́veis riscos, muitas vezes proporcionando oportunidades de simplificação
e reaproveitamento[Booch2005]. A modelagem consiste, então, em elaborar diagramas
Modelagem
39
que permitem alcançar quatro objetivos essenciais na compreensão do sistema que se está
desenvolvendo:
• Visualizar o sistema como ele é ou como é desejado.
• Permitir especificar a estrutura ou o comportamento de um sistema.
• Proporcionar um guia para a construção do sistema.
• Documentar as decisões tomadas.
O primeiro diagrama produzido foi o de Casos de Uso, que denota o comportamento
essencial do sistema. É identificado a interação dos atores com o sistema sem ser necessário
especificar como essa interação é realizada. No jogo OX Game existe apenas um jogador
humano que pode estar interagindo com a inteligência artificial ou com outro humano,
caracterizando a ação de jogar uma partida. É permitido, ainda, que esse jogador interaja
com o sistema de modo a usufruir de suas funcionalidades. O diagrama está descrito na
figura 4.4:
Modelagem
40
Figura 4.4: Diagrama de Casos de Uso do Jogo da Velha OX Game
O diagrama de atividades do jogo da velha OX Game está descrito na figura 4.5. Nesse
diagrama é possı́vel analisar o fluxo de controle de uma atividade para outra, ou seja, o
passo a passo do programa. Esse diagrama também mostra a concorrência bem como as
devidas ramificações de controle.
Modelagem
41
Figura 4.5: Diagrama de Atividades do Jogo da Velha OX Game
Modelagem
42
A partir do comportamento essencial do sistema, dos atores, suas atividades e do fluxo
das atividades no decorrer da execução do jogo, os diagramas de classes estão representados
nas figuras 4.6 e 4.7. Esses diagramas mostram o conjunto de classes e seus relacionamentos
no dispositivo móvel (cliente) e no servidor, respectivamente.
O Diagrama de classes referente ao cliente possui 10 classes. As classes estão descritas
a seguir:
• TelaSplash: classe responsável pela apresentação do jogo quanto ao seu desenvolvedor
e a razão de seu desenvolvimento.
• MenuPrincipal: apresenta o menu do jogo(praticar, novo jogo, desafiar, sair).
• Jogo: classe responsável pelo jogo entre um oponente humano e a inteligência artificial.
• JogoEspera: é uma especificação da classe Jogo. O jogador humano está aguardando
por um oponente humano.
• JogoRede: classe responsável pelo jogo entre dois jogadores humanos.
• ConexaoMensageiro: encapsula a solicitação do fluxo de entrada e saı́da de dados.
• Mensageiro: classe responsável pelo fluxo de entrada e saı́da de dados com o servidor.
• ListaJogadores: solicita a lista de jogadores disponı́veis que serão desafiados em uma
partido do jogo.
• ConstrutorLista: gera a lista de jogadores.
• ItemJogador: cria uma instância na lista para cada jogador conectado e esperando
por um desafio.
Modelagem
43
Figura 4.6: Diagrama de Classes do Jogo da Velha OX Game referente ao Cliente
O diagrama de classes referente ao servidor possui 4 classes. As classes estão descritas
a seguir:
• ServidorExecutavel: cria uma instância do servidor e é iniciado de modo a receber
conexões.
• Servidor: classe responsável por gerenciar as conexões dos clientes.
• Jogador: é a classe responsável por realizar o fluxo de dados e armazenar os dados
do jogador.
• Partida: é a classe responsável por analisar as jogadas e validá-las.
Modelagem
Figura 4.7: Diagrama de Classes do Jogo da Velha OX Game referente ao Servidor
44
Codicando o Jogo
4.6
45
Codificando o Jogo
Com a modelagem do sistema pronto, o próximo passo para implementação do jogo
OX Game consiste na codificação utilizando a plataforma Android. Por se tratar de um
jogo multiusuário online, cada componente do sistema possui um ou mais processos sendo
executados. Estes processos são trechos de código que são executados paralelamente e
possuem linhas de controle distintas. Dessa forma, cada processo é responsável por uma
atividade de modo a permanecer ativa enquanto necessária, caso contrário, o processo é
encerrado. Um processo se comunica por meio do fluxo de entrada e saı́da de dados,
utilizando sockets (abordado no tópico 3.3), e é por intermédio desse fluxo que cada processo
sabe o momento exato em que deve iniciar.
4.6.1
Codificando o cliente
Qualquer aplicação em Android que necessita realizar processos envolvendo rede, é de
extrema importância declarar a permissão de uso no arquivo AndroidManifest.xml. Como
mencionado anteriormente no tópico 3.1, este arquivo descreve os elementos da aplicação,
as classes de cada componente a ser utilizado, qual o tipo de dado ele pode tratar, quando
pode ser ativado, ou seja, serve para definir os dados de cada elemento[Pereira2009].
Com base nesse conceito, no trecho de código 4.6.1 se encontra o arquivo AndroidManifest.xml do jogo OX Game. Na linha 8 desse código, é possı́vel verificar a permissão do uso
da internet pela aplicação. De modo que um cliente possa utilizar o acesso a rede WiFi,
outra permissão precisa ser imposta, como na linha 9.
Codicando o Jogo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
46
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.teste"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="8" />
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
<application
android:icon="@drawable/logo"
android:label="@string/app_name" >
<activity
android:screenOrientation="nosensor"
android:label="@string/app_name"
android:name=".TelaSplash">
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:screenOrientation="nosensor"
android:label="@string/app_name"
android:name=".MenuPrincipal" >
</activity>
<activity
android:screenOrientation="nosensor"
android:label="@string/app_name"
android:name=".Jogo" >
</activity>
<activity
android:screenOrientation="nosensor"
android:label="@string/app_name"
android:name=".JogoEspera" >
</activity>
<activity
android:screenOrientation="nosensor"
android:label="@string/app_name"
android:name=".JogoEmRede" >
</activity>
<activity
android:screenOrientation="nosensor"
android:label="@string/app_name"
android:name=".ListaJogadores" >
</activity>
</application>
</manifest>
Código 4.6.1: Código fonte referente a implementação do cliente
Nas linhas 11-48 do trecho de código 4.6.1, entre as tags <application> e </application>é declarado cada componente Activity que existe no jogo. Dessa forma, é possı́vel
notar que existe seis telas em toda a aplicação. O nome de cada tela se refere a classe em
que a instancia, como por exemplo, na linha 7, a tela é denominada TelaSplash, dado por
android:name=”.TelaSplash”.
Por existir mais de um componente Activity, ao iniciar uma aplicação um desses com-
Codicando o Jogo
47
ponentes precisa ser declarado como o componente principal. De modo a definir qual componente deve iniciar uma aplicação, faz-se necessário a utilização das tags <intent-filter >
e </intent-filter> e a declaração desse componente como a activity principal (linha 19), e
executável (linha 20).
Interface do Jogo da Velha
A interface do aplicativo é baseado em XML. Toda a codificação da interface é realizada
separadamente da codificação lógica do jogo. Os arquivos XML estão localizados na pasta
res/layout-port, conforme figura 4.8.
Figura 4.8: Localização dos arquivos XML no projeto OX Game
Cada arquivo XML é responsável pela construção da interface de uma determinada
Codicando o Jogo
48
Activity. No trecho de código 4.6.2, as caracterı́sticas como o identificador da tela, a
utilização do espaço da tela, a orientação, o plano de fundo e o efeito do som são declarados. É importante ressaltar que os termos android : layoutw idth = ”matchp arent” e
android : layouth eight = ”matchp arent” definem a largura e a altura do layout na dimensão disponı́vel da tela.
1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout_id"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/fundo"
android:orientation="vertical"
android:soundEffectsEnabled="true" >
</LinearLayout>
Código 4.6.2: Declaração das caracterı́sticas do arquivo telaJogo.xml
Tendo em vista que codificar e gerenciar parte da tela permite controlar os eventos com
facilidade, no trecho de código 4.6.3 é definido a primeira parte da tela. Nesse trecho de
código é declarado uma imagem utilizando a tag <ImageView>. Nessa tag é informado
o identificador (android:id=”@+id/titulo2”) dessa imagem, para que possa ser chamado
durante a codificação lógica do jogo, as dimensões e a orientação.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout_id"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/fundo"
android:orientation="vertical"
android:soundEffectsEnabled="true" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="78dp"
android:orientation="vertical" >
<ImageView
android:id="@+id/titulo2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dip"
android:layout_marginTop="15dip"
android:background="@drawable/titulo2" />
</LinearLayout>
</LinearLayout>
Código 4.6.3: Codificação da primeira parte da tela do Jogo OX Game
Codicando o Jogo
49
Na figura 4.9, a visualização da parte codificada é apresentada:
Figura 4.9: Primeira parte da tela do Jogo OX Game
A segunda parte da tela é referente ao tabuleiro de 9 posições. Cada posição é um botão
e utiliza a tag <ImageButton> para instanciá-lo. Para cada botão existe um identificador
(android:id=”@+id/b3”)e as caracterı́sticas de orientação. No trecho de código 4.6.4 é
apresentado a criação de uma instancia desse botão:
1
2
3
4
5
6
7
8
<ImageButton
android:id="@+id/b3"
android:layout_width="60dp"
android:layout_height="67dp"
android:layout_gravity="center"
android:layout_marginLeft="40dip"
android:layout_weight="0.07"
android:tag="3" />
Código 4.6.4: Codificação de um botão do tabuleiro 3x3 do Jogo OX Game
De modo a organizar as noves posições do tabuleiro, essa segunda parte da tela é
dividida em três subpartes de modo a ficarem igualmente distribuı́dos. Cada subparte
engloba três botões. Sendo assim, cada subparte possui a implementação apresentado no
trecho de código 4.6.5:
Codicando o Jogo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal" >
<ImageButton
android:id="@+id/b3"
android:layout_width="60dp"
android:layout_height="67dp"
android:layout_gravity="center"
android:layout_marginLeft="40dip"
android:layout_weight="0.07"
android:tag="3" />
<ImageButton
android:id="@+id/b2"
android:layout_width="60dp"
android:layout_height="67dp"
android:layout_gravity="center"
android:layout_toLeftOf="@id/b3"
android:layout_weight="0.07"
android:tag="2" />
<ImageButton
android:id="@+id/b1"
android:layout_width="60dp"
android:layout_height="67dp"
android:layout_gravity="center"
android:layout_marginRight="40dip"
android:layout_toLeftOf="@id/b2"
android:layout_weight="0.07"
android:tag="1" />
</LinearLayout>
Código 4.6.5: Codificação do tabuleiro 3x3 do Jogo OX Game
Na figura 4.10, a visualização da parte codificada é apresentada:
50
Codicando o Jogo
51
Figura 4.10: Primeira e segunda parte da tela do Jogo OX Game
A terceira parte da tela do jogo é destinado para a visualização do placar. O trecho de código 4.6.6 define um texto na tela utilizando a tag <TextView>. A utilização
dessa tag permite que seja alterado constatemente por meio do identificador declarado (
android:id=”@+id/scoreboard”).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="3.5"
android:orientation="vertical" >
<TextView
android:id="@+id/scoreboard"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|center_vertical"
android:textColor="#000000"
android:textStyle="bold" />
</LinearLayout>
Código 4.6.6: Codificação do placar do Jogo OX Game
Como existe apenas a codificação gráfica na interface e o texto depende da informação
vinda da lógica do jogo, nenhuma valor é visualizado, mas o seu espaço na tela é reservado,
conforme figura 4.11:
Codicando o Jogo
52
Figura 4.11: Primeira, segunda e terceira parte da tela do Jogo OX Game
A última parte da tela é codificada de modo a incluir alguns detalhes de design. O
trecho de código 4.6.7 apresenta a inclusão de duas figuras. Ambas figuras possuem seus
identificadores assim como as configurações de orientação e tamanho.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<ImageView
android:id="@+id/detalhes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0.51"
android:background="@drawable/detalhes" />
<ImageView
android:id="@+id/android1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/androidgrande" />
</LinearLayout>
Código 4.6.7: Codificação dos detalhes de design do Jogo OX Game
Na figura 4.12, a visualização da parte codificada é apresentada:
Codicando o Jogo
53
Figura 4.12: Tela do Jogo OX Game
Dessa forma, a interface do jogo OX Game está construı́da. É importante ressaltar que
as tags <LinearLayout> e </LinearLayout> dividem a tela em tamanhos proporcionais
desejados. Como se pode observar, existem as tags LinearLayout principais e mais outras
tags de mesmo nome dividindo a tela em partes menores. No total existe um tela que está
dividida em quatro partes, e por sua vez, a segunda parte é dividida em mais três.
Criando uma conexão com o Servidor
O usuário do jogo OX Game solicita uma conexão com o servidor em duas circunstâncias: ao criar um novo jogo, aguardando por um oponente de modo a iniciar o jogo online,
ou ao solicitar a lista de jogadores disponı́veis para desafiar e iniciar uma partida. Ambas
situações acontecem a partir do menu principal do jogo, conforme apresentado na figura
4.13:
Codicando o Jogo
54
Figura 4.13: Menu principal do jogo OX Game
A requisição de uma conexão com o servidor é realizada por meio de sockets, conforme visto no tópico 3.2.1. Essa conexão é criada utilizando as classes MenuPrincipal e
ConexaoMensageiro. No trecho de código 4.6.8 - Classe MenuPrincipal, é possı́vel observar que a requisição dessa conexão é realizada pelos métodos criarConexao(ip, porta) e
conectar().
1
2
3
4
5
6
7
8
9
try {
// Inicia uma conexao com o Servidor de Socket
ConexaoMensageiro conexao = ConexaoMensageiro.criarConexao(ip, porta);
conexao.conectar();
} catch (Exception e) {
// Mostra erro na tela
Toast.makeText(MenuPrincipal.this, "Nao foi possivel conectar",
Toast.LENGTH_LONG).show();
}
Código 4.6.8: Requisição de conexao ao Servidor
Ao criar um objeto ConexaoMensageiro, um objeto de mesma classe é atribuı́do. Esse
objeto é instanciado utilizando os parâmetros host e porta que foram informados no método
Codicando o Jogo
55
criarConexao(ip, porta). A partir desse objeto, uma requisição de conexão é solicitada. O
método conectar(), por sua vez, é responsável por realizar a conexão com o servidor e
preparar o fluxo de entrada e de saı́da de dados. No trecho de código 4.6.9 é apresentado
essas funcionalidades:
1
2
3
4
5
6
private ConexaoMensageiro(String host, String porta) {
this.host = host;
this.porta = Integer.parseInt(porta);
}
public static ConexaoMensageiro criarConexao(String host, String porta) {
connection = new ConexaoMensageiro(host, porta);
return connection;
}
7
8
9
10
11
public void conectar() throws Exception {
this.socket = new Socket(host, porta);
dataOut = new DataOutputStream(socket.getOutputStream());
dataIn = new DataInputStream(socket.getInputStream());
}
12
13
14
15
Código 4.6.9: Descrição dos métodos criarConexao(ip, porta) e conectar()
Aguardando um Oponente
Dentre as funcionalidades do aplicativo OX Game, o jogador pode optar por inicializar
um novo jogo e aguardar um oponente. Quando essa opção é escolhida, a conexão com
o servidor, se realizada, permite ao usuário jogar contra a inteligência artificial enquanto
um oponente não o desafia. Durante essa partida, que pode ocorrer sucessivamente, um
processo concorrente é iniciado e fica aguardando esse jogador oponente.
O processo concorrente é executado em uma classe interna chamada MyThread. Essa
classe é criada dentro da classe JogoEspera de modo a facilitar o trabalho dessa classe sem
expor a complexidade existente. A classe MyThread é apresentada no trecho de código
4.6.10:
Codicando o Jogo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
56
private class MyThread extends Thread {
private boolean esperando;
public MyThread() {
esperando = true;
}
public void run() {
aguardarOponente();
mHandler.post(new Runnable() {
@Override
public void run() {
avisarDesafio();
}
});
}
private void aguardarOponente() {
do {
try {
esperando = ConexaoMensageiro.retornarConexao().aguardarOponente();
} catch (Exception e) {
e.printStackTrace();
}
} while (esperando);
}
}
Código 4.6.10: Classe responsável por aguardar o desafio de um oponente
Por se tratar de uma classe concorrente, o método obrigatório run() realiza toda a
atividade necessária. A chamada do método interno aguardarOponente() faz com que todo
o restante da atividade concorrente só se realize quando houver uma resposta do servidor.
Nessa caso, o servidor é responsável por enviar a esse método uma mensagem de que
existe um oponente desafiando o jogador. O método aguardarOponente(), por sua vez,
está relacionado com o socket do jogador e sendo trabalho na classe Mensageiro, classe
designada a se comunicar com o servidor.
Na classe Mensageiro, um loop é executado de modo a averiguar as mensagens enviadas
pelo servidor. No trecho de código 4.6.11, é possı́vel verificar que toda vez que ocorre um
envio de mensagem, essa mensagem é analisada. Caso exista uma solicitação de desafio, o
loop é encerrado e assim a atividade concorrente executa o método avisarDesafio().
Codicando o Jogo
1
2
3
4
5
6
7
8
9
10
11
57
try {
while (esperar) {
aguardarOponente = dataIn.readBoolean();
if (aguardarOponente == false) {
esperar = false;
}
}
} catch (IOException e) {
e.printStackTrace();
stop();
}
Código 4.6.11: Loop responsável pela análise das mensagens enviadas pelo servidor
O método avisarDesafio() inicia uma nova partida e conecta os dois jogadores. Esse
método está descrito no trecho de código a seguir:
1
2
3
4
5
6
7
8
9
public void avisarDesafio() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Um oponente esta lhe desafiando... Hora de jogar!!");
builder.setCancelable(true);
builder.setPositiveButton("OK",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog1, int which) {
Intent intent = new Intent(JogoEspera.this, JogoEmRede.class);
Bundle parametros = new Bundle();
parametros.putString("jogador2", jogador2);
parametros.putString("statusSom", statusSom);
parametros.putBoolean("conectado", true);
10
11
12
13
14
intent.putExtras(parametros);
15
16
17
18
19
20
21
22
startActivity(intent);
finish();
}
});
builder.show();
}
Código 4.6.12: Método responsável por avisar o jogador de um desafio e iniciar uma nova
partida
Enviando e recebendo as jogadas
Após o aviso de desafio de um oponente, os dois jogadores são sincronizados de modo a
compartilharem a mesma tela de jogo, alternando entre si as jogadas. A partida é executada
utilizando as classes JogoEmRede e Mensageiro. A classe Mensageiro recebe do servidor
uma mensagem informando qual jogador inicializa a partida. Essa mensagem é enviada
através do construtor da classe Partida no qual o servidor monitora, conforme trecho de
código 4.6.13.
Codicando o Jogo
1
2
3
4
5
6
58
public Partida(Jogador jogadorX, Jogador jogadorO) throws IOException{
this.PLAYER_X = jogadorX;
this.PLAYER_0 = jogadorO;
this.jogadorDaVez = PLAYER_X;
jogadorDaVez.dataOut.writeBoolean(false);
}
Código 4.6.13: Construtor da classe Partida localizada no servidor
Por sua vez, quando a classe Mensageiro recebe a mensagem de permissão de jogada,
e informa a classe JogoEmRede, a variável isMinhaVez permite que os botões bloqueados
sejam liberados e prontos para receber uma posição do jogador. Ao ser escolhida a posição
de jogada, o método enviarJogada() é executado. Caso essa jogada seja decisiva para o
fim do jogo, uma análise é realizada. Caso contrário, o socket realiza o envio da posição ao
servidor, de modo que possa ser distribuı́do ao cliente oponente. O método enviar jogada
é apresentado no trecho de código 4.6.14:
1
2
3
4
5
6
7
8
9
OnClickListener button_listener = new View.OnClickListener() {
public void onClick(View v) {
controleSomAndroid = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
if (SOM_LIGADO
&& (controleSomAndroid.getRingerMode() == AudioManager.RINGER_MODE_NORMAL)) {
media.start();
}
ImageButton ibutton = (ImageButton) v;
String posicao = "";
int pos = 0;
10
11
12
if (isMinhaVez) {
posicao = (String) ibutton.getTag(); // pega a posicao conforme a tag do button
pos = (int) posicao.charAt(0) - 48;
13
14
15
16
17
try {
ConexaoMensageiro.retornarConexao().enviarJogada(pos);
} catch (IOException e) {
e.printStackTrace();
}
atualizarTabuleiro(pos);
atualizaJogadas(pos);
resultado = verificaResultado();
if((resultado == true) || ((resultado == false) && isCompleto())){
ConexaoMensageiro.retornarConexao().avisarTerminou(true);
avisarResultado(resultado);
}else{
ativaThread();
}
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
}
}
};
Código 4.6.14: Método responsável pelo clique de uma jogada no tabuleiro
Em paralelo, a classe JogoEmRede possui uma classe interna chamada Analisador que
Codicando o Jogo
59
tem como objetivo verificar quando é a vez de receber a jogada do oponente. Essa classe
concorrente executa o método existente run() que, em seguida, executa um método interno
chamado receberJogadaOponente(). No trecho de código a seguir é possı́vel analisar a classe
Analisador :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private class Analisador extends Thread {
int pos;
public Analisador() {
isMinhaVez = false;
}
public void run() {
receberJogadaOponente();
mHandler.post(new Runnable() {
@Override
public void run() {
if(pos != 0){
atualizarTabuleiro(pos);
atualizaJogadas(pos);
resultado = verificaResultado();
if((resultado == true) || ((resultado == false) && isCompleto())){
ConexaoMensageiro.retornarConexao().avisarTerminou(true);
avisarResultado(resultado);
}else{
isMinhaVez = true;
}
}
}
});
}
34
35
36
private void receberJogadaOponente() {
pos = 0;
do {
try {
pos = ConexaoMensageiro.retornarConexao().recebeJogadaOponente();
} catch (Exception e) {
}
} while (pos == 0);
}
37
38
}
29
30
31
32
33
Código 4.6.15: Classe concorrente Analisador
O método receberJogadaOponente() verifica quando há uma jogada do oponente a receber e, quando existente, efetua o recebimento e verifica se ganhou a partida ou se houve
um empate.
Verificando o vencedor
Quandos os jogadores efetuam suas jogadas, análises são realizadas de modo a informar
ao servidor se houve ou não a finalização do jogo. Ao verificar que o jogo obteve um fim,
Codicando o Jogo
60
uma mensagem é informada ao servidor de modo que possa distribuir a informação ao
oponente. O método responsável por esse processo é chamado de avisarTerminou(boolean
terminou) e é descrito como:
1
2
3
public void avisarTerminou(boolean terminou){
mensageiro.setTerminou(terminou);
}
Código 4.6.16: Método avisarTerminou(boolean terminou)
Esse método, por sua vez, finaliza o loop de recebimento de jogadas na classe Mensageiro. Esse loop está apresentado a seguir:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public void run() {
while (running) {// Enquanto estiver executando
if (vezDeJogar) {
try {
while (esperar) {
aguardarOponente = dataIn.readBoolean();
if (aguardarOponente == false) {
esperar = false;
}
}
} catch (IOException e) {
e.printStackTrace();
stop();
}
}else{
try {
if(terminou == false){
jogadaOponente = dataIn.readInt();
}
} catch (IOException e) {
e.printStackTrace();
stop();
}
}
}
}
public void setTerminou(boolean terminou) {
this.terminou = terminou;
}
Código 4.6.17: Loop de recebimento de jogadas na classe Mensageiro
Por fim, uma mensagem é informada aos jogadores e, então, todo o fluxo de entrada e
saı́da de dados é encerrado. No trecho de código 4.6.18 é detalhado o método avisarReultado(String resultado) e no trecho de código 4.6.19, o método desconectar():
Codicando o Jogo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
61
public void avisarResultado(boolean resultado) {
String textoResultado = "";
if (resultado == true) {
if (isMinhaVez) {
// alterePlacar(1);
textoResultado = "Voce ganhou!!";
} else {
// alterePlacar(2);
textoResultado = "Seu Oponente ganhou!!";
}
} else if ((resultado == false) && isCompleto()) {
textoResultado = "Velhou!!";
}
mostraResultado(textoResultado);
}
public boolean mostraResultado(String mensagem) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(mensagem).setPositiveButton("Continuar",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
try {
ConexaoMensageiro.retornarConexao().desconectar();
} catch (Exception e) {
Toast.makeText(JogoEmRede.this, "Nao fechou!!!",
Toast.LENGTH_SHORT).show();
}
finish();
}
});
AlertDialog alert = builder.create();
alert.show();
return true;
}
Código 4.6.18: Método avisarReultado(String resultado)
1
2
3
4
5
6
public void desconectar() throws Exception {
dataIn.close();
dataOut.close();
mensageiro.desconectar();
socket.close();
}
Código 4.6.19: Método desconectar()
4.6.2
Codificando o Servidor
O servidor tem como objetivo gerenciar todas as conexões que são solicitadas pelos
jogadores. O fluxo de entrada e saı́da de dados do servidor também é baseada em sockets.
A codificação do servidor é bastante semelhante ao que foi implementado no subtópico
3.3.1. No trecho de código a seguir, os objetos de fluxo de dados assim como outras
variáveis que são utilizadas pelo servidor são instanciados.
Codicando o Jogo
1
2
3
4
5
6
7
8
9
10
11
62
public class Servidor {
private final int numeroConexoes = 14;
private boolean executando = true;
private Jogador[] jogadores = new Jogador[numeroConexoes];
private final int PORTA = 7771;
private ArrayList<Jogador> jogadoresEsperando = new ArrayList<Jogador>();
private boolean aguardandoOponente = false;
private DataInputStream in;
private DataOutputStream out;
private String oponenteEscolhido;
}
Código 4.6.20: Método desconectar()
A variável PORTA especifica em qual porta o servidor está hospedado e a variável
numConexoes define qual o número máximo de conexões que o servidor irá gerenciar. Para
cada conexão realizada, o servidor deve registrar quem são os jogadores conectados assim
como distinguir quais jogadores criaram um novo jogo. De modo a solucionar essas especificações, um vetor chamado jogadores e uma lista de jogadores chamada jogadoresEsperando,
resolvem esses requisitos, respectivamente. Os objetos responsáveis pela comunicação de
dados também são designados: in referente a entrada de dados e out referente a saı́da de
dados. Por fim, as demais variáveis servem de controle durante o processo de execução do
servidor.
Inicializando o Servidor e Estabelecendo Jogos entre 2 jogadores
O servidor possui apenas um único método chamado inicializar(). Esse método é
responsável por iniciar o servidor, permitir que haja conexões com os clientes, distinguir
os tipos de jogadores assim como permitir que dois jogadores joguem entre si.
De modo a permitir que jogadores se conectem, uma instância da classe nativa ServerSocket é criada, utilizando a porta informada.
O trecho de código 4.6.21 mostra a
declaração e inicialização desse servidor:
1
ServerSocket server = new ServerSocket(PORTA, numeroConexoes);
Código 4.6.21: Inicialização do servidor em uma porta especificada
Cada conexão existente no servidor se refere a um jogador. Dessa forma, para cada
conexão deve ser criado um objeto do tipo Jogador de modo a ter controle sobre o fluxo de
dados que ele realiza. A partir disso, essa conexão informa que tipo de jogador será. Todo
Codicando o Jogo
63
esse processo é repetido até alcançar o numero máximo de conexões. O código a seguir
demonstra como implementar esse processo:
1
2
3
4
5
for (int i = 0; i < numeroConexoes; i++) {
//Recebendo conexao de um novo jogador
jogadores[i] = new Jogador(server.accept());
//criando os fluxos de entrada e saida do jogador
in = new DataInputStream(jogadores[i].getSocket().getInputStream());
out = new DataOutputStream(jogadores[i].getSocket().getOutputStream());
6
7
8
9
10
11
//detectando se o jogador cria um novo jogo ou desafia um jogador
aguardandoOponente = in.readBoolean();
}
Código 4.6.22: Método desconectar()
A variável aguardandoOponente é responsável por distinguir qual o tipo de jogador.
Caso o jogador inicie uma nova partida, esse jogador é adicionado na lista jogadoresEsperando. Caso o jogador seja um oponente, o servidor envia uma lista de jogadores
disponı́veis e, assim, escolhe qual o jogador quer desafiar. Nesse caso, uma busca é realizada na lista jogadoresEsperando e, então, ambos jogadores são sincronizados, iniciando
uma partida. É importante ressaltar que por meio das instruções realizadas nas linhas 17,
18 e 19 do trecho de código 4.6.23, vários jogos podem ser iniciados, respeitando o limite
de conexões (jogadores) que o servidor suporta. Essa funcionalidade se encontra disponı́vel
em virtude dessas instruções serem executadas toda vez que há uma nova conexão do tipo
jogador desafiante (representado pelo loop demonstrado no código 4.6.22). O código 4.6.23,
localizado no loop for definido no código 2.6.22, representa essa implementação:
Codicando o Jogo
64
19
20
21
if(aguardandoOponente){
//jogador iniciou um novo jogo e aguarda por um oponente
jogadoresEsperando.add(jogadores[i]);
System.out.println("Jogador Esperando!!");
}else{
//Servidor envia a lista de jogadores disponiveis
out.writeInt(jogadoresEsperando.size());
for (int j = 0; j < jogadoresEsperando.size(); j++) {
out.writeUTF(jogadoresEsperando.get(j).getNome());
System.out.println(jogadoresEsperando.get(j).getNome());
}
//Jogador escolhe seu oponente
oponenteEscolhido = in.readUTF();
for (int j = 0; j < jogadoresEsperando.size(); j++) {
if(oponenteEscolhido.equals(jogadoresEsperando.get(j).getNome())){
//Uma partida eh iniciada com os dois jogadores
Partida jogo = new Partida(jogadoresEsperando.get(j), jogadores[i]);
jogadoresEsperando.remove(j);
jogo.start();
System.out.println("Jogo Comecou!!");
}
22
23
24
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
}
Código 4.6.23: Código que distingue qual o tipo de jogador
Envio e recebimento das jogadas
Ao iniciar um novo jogo, o construtor da classe Partida é executado. Com isso, o
jogador que criou esse jogo e o oponente são declarados, respectivamente, o jogador X e
o jogador O. Após essa declaração, o jogador X é designado como o jogador que inicia a
partida, recebendo uma mensagem do servidor sinalizando essa atividade. Esse processo é
implementado conforme o código 4.6.24.
1
2
3
4
5
6
public Partida(Jogador jogadorX, Jogador jogadorO) throws IOException{
this.PLAYER_X = jogadorX;
this.PLAYER_0 = jogadorO;
this.jogadorDaVez = PLAYER_X;
jogadorDaVez.dataOut.writeBoolean(false);
}
Código 4.6.24: Construtor da classe Partida
Devido o envio da mensagem de permissão de jogada, um método é codificado de modo
a cumprir a rotina do jogo. Esse método é chamado run() e está descrito no código 4.6.25.
Codicando o Jogo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
65
public void run() {
try {
while (executando) {
//recebe a jogada
posicao = jogadorDaVez.lerJogada();
//atualiza a posicao
atualizaJogadas(posicao);
//imprime o estado do tabuleiro
imprimir();
//envia ao oponente uma mensagem de modo a desativar o botao referente a jogada
desativarJogadaOponente(posicao);
//alterna entre os jogadores
trocarJogador(jogadorDaVez);
}
} catch (IOException e) {
e.printStackTrace();
}
}
Código 4.6.25: Método run() responsável pela rotina do jogo
A jogada é recebida pela leitura do fluxo de dados, via socket, de cada jogador. Essa
leitura é implementada pelo método jogadorDaVez.lerJogada() que se encontra na classe
Jogador, conforme código 4.6.26.
1
2
3
4
public int lerJogada() throws IOException {
int posicao = dataIn.readInt();
return posicao;
}
Código 4.6.26: Método lerJogada()
Após essa verificação, o método interno desativarJogadaOponente(posicao) é responsável por enviar a jogada ao oponente evitando assim que se repita uma jogada já realizada.
Caso o jogador que realize a jogada seja o jogador X, essa jogada é enviada ao jogador O,
e vice-versa. Esse método é apresentado no trecho de código 4.6.27.
1
2
3
4
5
6
7
8
9
10
11
12
13
public void desativarJogadaOponente(int pos) {
try{
if (jogadorDaVez.equals(PLAYER_X)) {
PLAYER_0.dataOut.writeInt(pos);
PLAYER_0.dataOut.flush();
} else if (jogadorDaVez.equals(PLAYER_0)) {
PLAYER_X.dataOut.writeInt(pos);
PLAYER_X.dataOut.flush();
}
}catch (IOException e) {
e.printStackTrace();
}
}
Código 4.6.27: Método desativarJogadaOponente(int pos)
Codicando o Jogo
66
Por fim, os jogadores são alternados utilizando o método interno trocarJogador(jogadorDaVez) e essa rotina de jogo ocorre até que haja um vencedor ou ocorra
um empate. Um ponto relevante é que antes de efetuar a troca dos jogadores, uma
análise é feita para que o servidor detecte o final da partida e encerre. O método trocarJogador(jogadorDaVez) está codificado a seguir:
1
2
3
4
5
6
7
8
private void trocarJogador(Jogador jogador) throws IOException {
acabouJogo();
if(jogador.equals(PLAYER_X)){
jogadorDaVez = PLAYER_0;
}else if (jogador.equals(PLAYER_0)){
jogadorDaVez = PLAYER_X;
}
}
Código 4.6.28: Método trocarJogador(Jogador jogador)
Finalizando a Partida
Quando um vencedor é definido ou a partida é finalizada por empate, o método acabouJogo() além de realizar essa verificação, encerra essa partida e fecha os fluxos de dados
existentes. O código 4.6.29 representa essa verificação:
1
2
3
4
5
6
7
8
private void acabouJogo() throws IOException{
if((resultado == true) || ((resultado == false) && isCompleto())){
System.out.println("Jogo Acabou!\n");
executando = false;
PLAYER_0.desconectar();
PLAYER_X.desconectar();
}
}
Código 4.6.29: Método desconectar()
Como cada jogador é uma conexão, e cada conexão possui um fluxo de dados, o método
desconectar(), presente no método acabouJogo() e instanciado na classe Jogador, realiza o
fechamento do fluxo de entrada e saida de dados além de encerrar o socket do jogador. O
código 4.6.30 apresenta o método desconectar().
Codicando o Jogo
1
2
3
4
5
6
67
public void desconectar() throws IOException {
dataIn.close();
dataOut.close();
this.socket.close();
}
Código 4.6.30: Método desconectar()
De modo a visualizar os sistema de fluxo de dados entre os jogadores e o servidor, o
diagrama de sequência a seguir representa esse relacionamento:
Figura 4.14: Diagrama de sequência do fluxo de dados do jogo OX Game
Imagens do Jogo
4.7
Imagens do Jogo
O jogo OX Game possui 9 telas principais:
• Tela 01: apresenta as credenciais do jogo OX Game
• Tela 02: apresenta o menu do jogo OX Game
• Tela 03: solicita o nı́vel do jogo
• Tela 04: solicita o nome do jogador
• Tela 05: apresenta a lista de jogadores esperando por um desafio
• Tela 06: informa um desafio a um jogador
• Tela 07: tela do jogo da velha
• Tela 08: informa o resultado do jogo
• Tela 09: informa as opcões disponı́veis durante um jogo
O fluxo dessas telas se encontra representado a seguir:
68
Imagens do Jogo
69
Figura 4.15: Fluxo de telas do jogo OX Game
A partir do menu de aplicações de um celular Android, é possı́vel iniciar o Jogo OX
Game, conforme figura a seguir:
Imagens do Jogo
70
Figura 4.16: Tela de Aplicativos do Android
Ao iniciar o jogo, a tela 01 do fluxo de dados é apresentada:
Figura 4.17: Tela de Apresentação do Jogo
Imagens do Jogo
71
Logo em seguida, o menu do jogo (tela 02) é visualizado e permanece disponı́vel para a
decisão do usuário:
Figura 4.18: Menu principal do jogo
Caso o usuário selecione o menu Praticar, o aplicativo solicitará o nı́vel do jogo e,
posteriormente, o nome do usuário. Essas duas solicitações ocorrem por meio das telas 03
e 04:
Imagens do Jogo
72
Figura 4.19: Tela de seleção de nı́vel e Tela de inserção do nome do jogador
Dada as informações solicitadas, uma partida é iniciada contra a inteligência artificial
(tela 07). Durante a partida, opções se encontram disponı́veis caso o usuário deseje realizálas(tela 09).
Figura 4.20: Tela do jogo da velha e Tela de opções durante uma partida
Imagens do Jogo
73
No entanto, duas outras opções de jogo ainda podem ser selecionadas. No caso do
menu Novo Jogo, o aplicativo solicitará o nome do jogador, utilizando a tela 03, e iniciará
um jogo contra a inteligência artificial (tela 07) enquanto aguarda um oponente humano.
No caso do menu Desafiar, o jogador deverá informar o nome e, em seguida, o jogador
oponente que desafiará. A solicitação do nome também é realizada por meio da tela 03 e,
a lista de jogadores, pela tela 05:
Figura 4.21: Tela com a lista de jogadores online
Após de um desafio realizado, o jogador que se encontra jogando contra a inteligência
aritificial, recebe uma notificação (tela 06) e, então, ambos os jogadores estarão iniciando
uma partida um contra o outro (tela 07).
Imagens do Jogo
74
Figura 4.22: Aviso de desafio a um jogador
Ao finalizar uma partida, uma notificação é informada por meio da tela 08, retornando
ao menu principal posteriormente.
Figura 4.23: Tela referente ao resultado do jogo
Capı́tulo 5
Conclusão
Neste trabalho foi desenvolvido um jogo multiusuário utilizando a plataforma Android.
Dessa forma, foi possı́vel concluir que essa plataforma por ser open source e ser uma das
plataformas mais atuais, tem bastante potencial de evoluir ainda mais. Caracterı́sticas
como ambiente multiplataforma, codificação nativa disponı́vel, utilização da linguagem de
programação Java e um ambiente de desenvolvimento integrado (Eclipse), contribuem para
um bom desenvolvimento de um jogo, visto que desenvolver um jogo é desafiador e bastante
interessante para o público de dispositivos móveis.
Em paralelo, foi possı́vel observar que um jogo multiusuário envolve estudos e recursos
ligados com percepção, hardware, software, interface do usuário, fatores humanos e suas
possı́veis aplicações. Para a elaboração de um jogo desse tipo, então, é necessário realizar
estudos sobre a plataforma requerida, interação, projeto de interfaces e sistemas simples e
distribuı́dos.
Para o desenvolvimento do Jogo OX Game, apresentado nesse trabalho, foi essencial
a análise dos requisitos do jogo e a modelagem desses requisitos, permitindo, assim, a
codificação de uma maneira simples das regras do jogo da velha. Enquanto isso, permitiu
um entendimento mais preciso de como o fluxo de dados funciona. É importante, ainda,
mencionar que independente de como o jogo é desenvolvido, problemas de comunicação
podem ocorrer por diversos fatores,como por exemplo a perda de dados na rede durante a
comunicação.
5.1
Trabalhos Futuros
Com base no jogo desenvolvido, é possı́vel realizar trabalhos futuros, como por exemplo:
Trabalhos Futuros
76
• Implementar esse mesmo jogo utilizando a arquitetura ponto a ponto (mencionada
no tópico 3.2.2).
• Aplicar o conceito de sockets apresentado no tópico 3.3 e durante o desenvolvimento
do jogo (capı́tulo 4) no desenvolvimento de outros jogos multiusuários.
• Realizar a comunicação do cliente a um servidor Web ao invés de um servidor de
sockets.
Referências Bibliográficas
[Lecheta2010] Lecheta, R. (2010). Google Android - Aprenda a Criar Aplicações para
Dispositivos Móveis com o Android SDK. Novatec.
[Pereira2009] Pereira, L. C. O. (2009). Android para Desenvolvedores. Brasport. São Paulo.
[Schemberger2012] Shemberguer, E. Freitas, I. Vani, R. (2012) Plataforma Android. Universidade Estadual do Oeste do Paraná.
[Nunes2011] Nunes, G. da R. M. (2011) Desenvolvimento de um Jogo de Dominó para
Android. Departamento de Engenharia de Computação. Universidade do Estado do
Amazonas.
[Albuquerque2012] Albuquerque, F. (2012) Programação Distribuı́da usando Java. Departamento de Ciência da Computação. Universidade de Brası́lia.
[Vieira2011] Vieira, L I. da S. (2011) Desenvolvimento de um Jogo Multiusuário Online
usando Programação Concorrente. Departamento de Engenharia de Computação. Universidade do Estado do Amazonas.
[Deitel2010] Deitel, P. Deitel, H. (2010) Java: Como Programar - Tradução Edson Furmankiewicz. Pearson Prentice Hall. São Paulo.
[Android2012] Site oficial do sistema operacional Android.
Android. Disponı́vel em:
<http://http://www.android.com/> Acesso em: 10/06/2012.
[AndroidDev2012] Android Developers. Guia Android para Desenvolvedores. Disponı́vel
em: <http://developer.android.com/guide/index.html> Acesso em: 25/04/2012.
REFERÊNCIAS BIBLIOGRÁFICAS
78
[IBM2012] IBM Developer Works. Introdução ao Desenvolvimento do Android. Disponı́vel
em: <http://www.ibm.com/developerworks/br/library/os-android-devel> Acesso em:
28/04/2012.
[Geek2012] Geek
Information.
Investimento
em
Jogos.
Disponı́vel
em:
<http://www.geek.com.br/posts/17764-brasileiros-deverao-gastar-us-2-bi-em-jogoseste-ano> Acesso em: 02/06/2012.
[Teleco2012] Teleco
Celulares
no
Mundo.
Disponı́vel
em:
<http://www.teleco.com.br/pais/celular.asp> Acesso em: 28/05/2012.
[NotTec2012] Notı́cias Tecnologia Android Supera Apple no Maior Mercado de Telefonia do
Mundo. Disponı́vel em: <http://www.noticiastecnologia.com.br/android-supera-appleno-maior-mercado-de-telefonia-do-mundo> Acesso em: 02/06/2012.
[Booch2005] Booch, G. Rumbaugh, J. Jacobson, I. (2005) UML: Guia do Usuário Tradução Fabio da Silva e Cristina de A. Machado. Elsevier. Rio de Janeiro.
[Alencar2012] Alencar, S. M. (2012) Receituário para Preparação de Textos Técnicos.
Universidade Federal de Campina Grande (UFCG).
[Andrews2000] Andrews, G. (2000) Foundations of Multithreaded, Parallel and Distributed
Programming. Addison-Wesley.
[Bates2004] Bates, B. (2004) Game Design. Premier Press.
[Ableson2007] Ableson, F. W. (2007) Unlocking Android - A Developers Guide. Manning.
[Meier2009] Meier, R. (2009) Professional Android Application Development. Indianapolis.
Wiley Publishing.
Apêndice
Os códigos-fonte desta monografia encontram-se no CD que acompanha a mesma.