Você está na página 1de 66

Indice

1.1 1.2 1.3 1.4 Introdu c ao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Objectivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Avalia c ao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Instala c ao Ambiente Desenvolvimento Android . . . . . . . . . . . . . . . . . . . . 1.4.1 Lista de Software a instalar . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.2 Windows 7 Pro 64 bits: Instalar JDK 1.6.0 64 bits + Eclipse 64 bits + Android SDK 32 bits + ADT Plugin + MoSync 32 bits + JRE 1.7.0 32 bits 1.4.3 Mac OS X 64 bits: Instalar Eclipse 64 bits + Android SDK + ADT Plugin + MoSync . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.4 Actualiza c oes Outubro 2011 . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.5 APIs do Android . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.6 Congurar o Eclipse para criar uma aplica c ao Android . . . . . . . . . . . . 1.4.6.1 Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Arquitectura das Aplica c oes Android . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5.1 Hello World em Android . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5.1.1 Hello World em Android mas sem usar o estilo do Android . . . . 1.5.1.2 Hello World no estilo Android . . . . . . . . . . . . . . . . . . . . 1.5.2 Utiliza c ao do Log do Android . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5.3 GUIs em Android . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5.4 O ciclo de vida das aplica c oes Android . . . . . . . . . . . . . . . . . . . . . 1.5.5 Tarefas Ass ncronas em Android . . . . . . . . . . . . . . . . . . . . . . . . 1.5.6 Thread que executa c alculos em background com indicador de progresso . . Bases de Dados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ler e Escrever Ficheiros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . GUI widgets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.8.1 ListView, Spinner, GridView . . . . . . . . . . . . . . . . . . . . . . . . . . 1.8.2 ExpandableListView ( arvore com 2 n veis) . . . . . . . . . . . . . . . . . . . 1.8.3 ActionBar (tabs) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.8.4 Criar activities com sub-activities . . . . . . . . . . . . . . . . . . . . . . . . Escrever e ler de um Ficheiro de Texto . . . . . . . . . . . . . . . . . . . . . . . . . Aplica c ao de Manuten c ao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Indice de Fragmentos de C odigo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bibliograa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Indice de Ficheiros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Indice Remissivo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 2 2 2 3 4 7 7 8 9 10 12 15 15 16 17 19 22 22 26 36 43 44 44 48 48 49 52 59 61 64 65 66

1.5

1.6 1.7 1.8

1.9 1.10 1.11 1.12 1.13 1.14

Vers ao: $Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ NOTA: partes do documento por completar ou desenvolver est ao marcadas com a palavra FIXME.

1.1

Introdu c ao

Este documento serve de apoio aos alunos da unidade curricular de Computa c ao M ovel, que e dada em conjunto a alunos provenientes do terceiro ano de Sistemas de Informa c ao e Software, Redes de Comunica c ao e Telecomunica c oes, e Tecnologias de Comunica c ao Multim edia (op c ao de Internet e Computa c ao M ovel ). Dependendo da proveni encia, estes alunos nos anos anteriores aprenderam a programar em C#, Java, ActionScript e/ou Python. Este documento vai estar em permanente actualiza c ao (pelo que s o raramente deve ser impresso). A u ltima vers ao pode ser descarregada de http://portodigital.pt/avs/cm2012-2c35/.

Computa c ao M ovel

Em termos da bibliograa as refer encias principais para a mat eria abordada nesta unidade curricular s ao [Ableson et al. 2011], [Darcey e Conder 2011], [Mednieks et al. 2011], [Steele e To 2011], [Collins et al. 2012] e [Neil 2012].

1.2

Objectivos

S ao estes os objectivos desta unidade curricular: 1. (objectivo principal) Conseguir que o aluno crie programas para dispositivos m oveis, estando ciente das suas diferen cas, vantagens e limita c oes face a aplica c oes para computadores desktops 2. (objectivo principal) Conseguir que o aluno consiga a partir da descri c ao de um problema ou da especica c ao de um programa, n ao muito complexo mas tamb em n ao trivial, pr oprio para uma aplica c ao m ovel, criar no sistema operativo Android um programa com algumas dezenas a poucas centenas de linhas de c odigo que permita resolver esse problema ou implementar o pretendido; 3. (objectivo secund ario) Familiarizar o aluno com as classes existentes nas bibliotecas standards do Android; 4. (objectivo secund ario) Familiarizar o aluno com o uso do ambiente de desenvolvimento integrado1 Eclipse para escrever c odigo, compilar, fazer a depura c ao (debug), e pesquisar na documenta c ao das bibliotecas do Android.

1.3

Avalia c ao

FIXME avalia c ao cont nua, trabalhos individuais, etc

1.4

Instala c ao Ambiente Desenvolvimento Android

Esta sec c ao fornece instru c oes sobre onde ir buscar e como instalar dois ambientes de desenvolvimento para Smartphones ou Tablets com o sistema operativo Android. Ambos os sistemas de desenvolvimento s ao baseados no Eclipse, mas um deles utiliza uma vers ao normal do Eclipse para desenvolvimento na linguagem de programa c ao Java (isto e, funciona ` a custa da instala c ao de um Plugin), enquanto que o outro utiliza uma vers ao especial do Eclipse que j a vem inclu da no cheiro de instala c ao e permite desenvolver para Android utilizando a linguagem de programa c ao C/C++. A vantagem do primeiro sistema de desenvolvimento (disponibilizado pela Google) e que representa a maneira standard de desenvolver software para Android (usando Java), a vantagem do segundo sistema de desenvolvimento (disponibilizado pela MoSync AB) e que permite escrever programas em C/C++ que depois s ao convertidos para Java (de modo a poderem ser executados no Android), permitindo igualmente que a partir do mesmo c odigo fonte se crie programas para outros tipos de Smartphones (iPhone, Symbian, etc.) As duas sec c oes que se seguem, criadas em 1 de Outubro 2012 pelo que se referem ` as vers oes de software existentes nessas datas, possuem objectivos distintos. A primeira apresenta a lista do software a instalar, consoante a vers ao do sistema operativo da m aquina de desenvolvimento (Windows de 32 bits, Windows de 64 bits, Mac OS X, Linux), e a segunda sec c ao apresenta em detalhe o modo de instalar ambos os sistemas de desenvolvimento em Windows 7 Professional de 64 bits (o sistema operativo instalado na maioria dos computadores do ISMAI). A sec c ao 1.4.4 sintetisa a informa c ao sobre novas vers oes do software que tenham sa do ap os 1 de Outubro 2012 e chama a aten c ao para eventuais problemas, incompatibilidades ou diferen cas de comportamento.
1 IDE,

Integrated Development Environment

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

1.4.1

Lista de Software a instalar

Para criar software para Smartphones ou Tablets com o sistema operativo Android, usando o ambiente de desenvolvimento Eclipse, e necess ario obter e instalar nesta ordem, em fun c ao do sistema operativo e da arquitectura (32 bits ou 64 bits) da m aquina utilizada para o desenvolvimento: 1. JDK (java development kit) 1.7.0_72 Windows XP, Vista ou 7 (32 bits): jdk-7u7-windows-i586.exe Windows Vista ou 7 (64 bits): jdk-7u7-windows-x64.exe Mac OS X (Lion e Mountain Lion): jdk-7u7-macosx-x64.dmg Mac OS X (Snow Leopard 10.6.8): aparentemente o jdk-7u7-macosx-x64.dmg n ao funciona nesta vers ao, utilizar o software update (actualiza c oes do sistema operativo) para automaticamente obter a u ltima vers ao do JDK (1.6.0_35), n ao tentar instalar ou descarregar o software de outra forma. Para vericar a vers ao do Java instalada abrir o Terminal (Applications/Utilities/Terminal) e escrever javac -version, deve-se obter como resposta javac 1.6.0_35 Linux 32 bits: jdk-7u7-linux-i586.tar.gz (ou jdk-7u7-linux-i586.rpm se for para um sistema que suporte RPMs) Linux 64 bits: jdk-7u7-linux-x64.tar.gz (ou jdk-7u7-linux-x64.rpm se for para um sistema que suporte RPMs) 2. Eclipse3 (tem de ser Eclipse 3.6.2 ou superior, podendo ser o Eclipse Classic, ou o Eclipse IDE for Java Developers, ou o Eclipse IDE for Java EE Developers 4 ): Windows XP, Vista ou 7 (32 bits): eclipse-SDK-4.2.1-win32.zip (Eclipse Classic) ou eclipse-java-juno-SR1-win32.zip (Eclipse IDE for Java Developers) Windows Vista ou 7 (64 bits): eclipse-SDK-4.2.1-win32-x86_64.zip (Eclipse Classic) ou eclipse-java-juno-SR1-win32-x86_64.zip (Eclipse IDE for Java Developers) Mac OS X Snow Leopard 10.6.8: eclipse-SDK-4.2.1-macosx-cocoa-x86_64.tar.gz (Eclipse Classic 64 bits) ou eclipse-SDK-4.2.1-macosx-cocoa.tar.gz (Eclipse Classic 32 bits) ou eclipse-java-juno-SR1-macosx-cocoa-x86_64.tar.gz (Eclipse IDE for Java Developers 64 bits) ou eclipse-java-juno-SR1-macosx-cocoa.tar.gz (Eclipse IDE for Java Developers 32 bits) Linux 32 bits: eclipse-SDK-4.2.1-linux-gtk.tar.gz Linux 64 bits: eclipse-SDK-4.2.1-linux-gtk-x86_64.tar.gz 3. Android SDK5 Windows XP, Vista ou 7 (32 bits): installer_r20.0.3-windows.exe entico vers ao 32 Windows Vista ou 7 (64 bits): installer_r20.0.3-windows.exe (id bits) Mac OS X Snow Leopard 10.6.8: android-sdk_r20.0.3-macosx.zip Linux 32 bits (pode ser de 64 bits mas com capacidade para correr aplica c oes de 32 bits): android-sdk_r20.0.3-linux.tgz FIXME 4-oct-2012: below not yet updated
http://www.oracle.com/technetwork/java/javase/downloads/jdk7u7-downloads-1836413.html http://www.eclipse.org/downloads/ 4E prefer vel n ao se escolher o Eclipse IDE for Java EE Developers j a que n ao se vai utilizar as funcionalidades da Enterprise Edition do Java, de resto tanto faz, pode-se escolher o Eclipse Classic ou o Eclipse IDE for Java Developers 5 http://developer.android.com/sdk/index.html
3 2

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Computa c ao M ovel

4. Android Development Tools (ADT), plugin para o Eclipse: (a) Arrancar com o Eclipse, seleccionar Help > Install New Software ... (b) Clicar Add no canto superior direito (c) Na janela de di alogo Add Repository que aparece colocar ADT Plugin no Name e na URL da Location colocar: https://dl-ssl.google.com/android/eclipse/ (se isto der problemas trocar https por http) c ao, duas plataformas Android, exemplos, e APIs espec cas da Go5. SDK tools, documenta ogle e da Samsung: (a) Android SDK Platform-tools (b) Documentation for Android SDK (c) SDK Platform Android 2.3.4 (API Level: 10) (d) SDK Platform Android 3.1 (API Level: 12) (e) Samples6 for SDK API 10 (f) Samples for SDK API 12 (g) Third party add-ons: i. Google Inc. add-ons (dl-ssl.google.com), necess ario para usar Google Maps ii. Samsung Electronics add-ons (innovator.samsungmobile.com) 6. Instalar o MoSync7 2.5, que permite escrever um programa em C/C++ e automaticamente converter o mesmo para este poder ser executado em iOS (iPhone / iPad /iPod Touch), Android 1.5, 1.6, 2.1, Java ME MIDP 2, Symbian S60 (2nd, 3rd, 5th editions), Windows Mobile 5.0-6.5, Windows Pocket PC 2003, ou Windows Smartphone 2003. Espera-se8 que at e ao m do ano de 2011 o Mosync passe a ter suporte para Blackberry e Windows Phone 7 (WP7). Note-se que esta e uma forma alternativa de desenvolver software para o Android. Neste caso utiliza-se a linguagem de programa c ao C/C++ em vez de Java embora depois o c odigo seja convertido9 para Java pelo que corre nativamente no Android (e o mesmo c odigo e como que convertido para a linguagem de programa c ao Objective C de modo a correr no iPhone). Windows XP, Vista ou 7 (32 bits): MoSyncWindows-2.5.exe Windows Vista ou 7 (64 bits): MoSyncWindows-2.5.exe (Nota: e prov avel que depois de instalado n ao funcione a n ao ser que se instale um Java Run-time Environment (JRE) de 32 bits) Mac OS X Snow Leopard 10.6.8: MoSyncOSX-2.5.dmg Linux 32 bits: pode-se tentar criar o MoSync a partir do c odigo fonte mas nem tudo e suportado10 , pelo que n ao e aconselhado o uso do MoSync em Linux (excepto dentro de uma m aquina virtual a correr Windows)

1.4.2

Windows 7 Pro 64 bits: Instalar JDK 1.6.0 64 bits + Eclipse 64 bits + Android SDK 32 bits + ADT Plugin + MoSync 32 bits + JRE 1.7.0 32 bits

A instala c ao em Windows 7 vers ao de 64 bits introduz alguns problemas visto que nem todas as ferramentas s ao de 64 bits (algumas s ao de 32 bits) pelo que e necess ario resolver alguns problemas
6 Exemplos 7 8 9 10

de c odigo http://www.mosync.com/download http://www.mosync.com/content/mosync-roadmap http://www.mosync.com/documentation/manualpages/runtime-architecture http://www.mosync.com/documentation/manualpages/building-mosync-source-linux

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

adicionais de modo a conseguir que elas quem a funcionar correctamente. O que e descrito abaixo foi testado numa m aquina virtual criada especicamente para permitir efectuar este teste, pelo que se forem seguidos (nesta ordem) os passos aqui indicados o sistema deve car a funcionar correctamente: 1. Instala c ao de Windows 7 Professional com o Service Pack 1, vers ao de 64 bits, numa m aquina virtual (neste caso o VMware Fusion 3.1.3 que suporta o Windows 7 Service Pack 1 de 64 bits, a correr em Mac OS X 10.6.8). Foi usada a vers ao inglesa do Windows 7 mas n ao h a raz ao para se esperar problemas com a utiliza c ao da vers ao portuguesa. 2. Aplicar todas as actualiza c oes ao sistema operativo Windows 7 Pro e instalar um antivirus (neste caso foi usado o Microsoft Security Essentials11 ) 3. Em 28 de Julho 2011 a Oracle disponibilizou a nova vers ao do java, o Java Development Kit (JDK) 1.7.0. Embora de uma forma geral o JDK 1.7.0 seja compat vel com o JDK 1.6.0_26 existe uma diferen ca ao n vel do c odigo gerado12 (byte codes para a m aquina virtual java) na medida em que a vers ao do cheiro .class gerado pelo JDK 1.7.0 e a 51 (devido ` a introdu c ao do invokedynamic byte code), e a vers ao dos cheiros criados pelo JDK 1.6.0 e a 5013 . Isto signica que nalgumas circunst ancias se bem que n ao existam problemas em usar o Java Run-Time 1.7 para correr c odigo de vers oes anteriores do Java podem existir problemas se se compilar com o JDK 1.7 e se executar com vers oes mais antigas do JRE. Assim sendo, para evitar problemas optou-se por se instalar a u ltima vers ao do JDK 1.6 de 64 bits e, para resolver um problema de incompatibilidade entre 32 e 64 bits ao n vel de carregar bibliotecas din amicas (DLLs), instalar-se igualmente a vers ao 1.7 do JRE de 32 bits. Instalar o JDK 1.6.0_26 escolhendo a vers ao de 64 bits14 : jdk-6u26-windows-x64.exe Se o JDK estiver instalado mas n ao estiver denida a vari avel de ambiente JAVA_HOME algum software pode n ao encontr a-lo. Para que isso n ao aocnte ca e para que a partir da linha de comando sem se colocar o caminho completo se possa executar o compilador de java (javac.exe) e necess ario actualizar o PATH e criar o JAVA_HOME: Control Panel / System and Security / System / Advanced system settings / Environment Variables / System variables / seleccionar Path / Edit ... adicionar ao m desta algo de semelhante a ;c:\program files\java\jdk1.7.0_7\bin (n ao esquecer de colocar o ponto e v rgula como separador, muita cautela para n ao estragar o que j a estava no Path, sen ao o computador pode deixar de funcionar correctamente). Criar igualmente uma nova vari avel de ambiente chamada JAVA_HOME e colocar nela o valor c:\program files\java\jdk1.7.0_7 (isto e, n ao tem o ponto e v rgula nem tem o subdirect orio bin). 4. Instalar o 7-Zip15 de modo a se ter uma ferramenta de linha de comando que permita descomprimir arquivos ZIP, e adicionar a localiza c ao do bin ario do 7z e dos bin arios do JDK ao PATH16
http://www.microsoft.com/en-us/security_essentials/default.aspx http://www.oracle.com/technetwork/java/javase/compatibility-417013.html 13 JDK 1.1 cria classes com a vers ao 45, JDK 1.2 vers ao 46, JDK 1.3 vers ao 47, JDK 1.4 vers ao 48, JDK 1.5 vers ao 49, JDK 1.6 vers ao 50, JDK 1.7 vers ao 51. 14 http://www.oracle.com/technetwork/java/javase/downloads/jdk-6u26-download-400750.html 15 http://www.7-zip.org, seleccionar a vers ao de 64 bits: 7z920-x64.msi 16 Abrir o pain el de controle, seleccionar Systam and Security, System, Advanced System Settings, Environment Variables, System variables, seleccionar a vari avel Path (pode ser necess ario fazer scroll para a ver na lista de vari aveis do sistema), escolher Edit... e adicionar ao m do Variable value um ponto e v rgula se n ao existir j a um no m do texto, seguido do caminho completo para o direct orio onde o 7-Zip foi instalado (se se aceitou o valor do local fornecido por omiss ao para a instala c ao do programa este cou instalado em c:\Program Files\7-Zip), outro ponto e v rgula, e o caminho completo para o direct orio onde o JDK cou instalado (se se aceitou o valor do local fornecido por omiss ao para a instala c ao do programa este cou instalado em c:\Program Files\jdk1.6.0_26 e os bin arios est ao no subdirect orio bin), pelo que se deve adicionar ao Path a string ;c:\program files\7-Zip;c:\Program Files\jdk1.6.0_26\bin.
12 11

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Computa c ao M ovel

5. Descarregar o Eclipse 3.7.0 Classic17 para Windows 64 bit: eclipse-SDK-3.7-win32-x86_64.zip. Mover o cheiro zip para o direct orio onde se pretende instalar o software (por exemplo c:\Program Files) e descomprimir o cheiro usando por exemplo o comando 7z x eclipse-SDK-3.7-win32-x86_64.zip, isto cria o sub-direct orio eclipse. Para correr o eclipse executar o programa c:\Program Files\eclipse\eclipse.exe 6. Instalar o SDK do Android18 : installer_r12-windows.exe. NOTA IMPORTANTE: e muito prov avel que ao instalar o SDK do Android d e o erro de que n ao consegue encontrar o JDK, nesse caso basta seleccionar o bot ao de Back no wizard, e a seguir voltar a seleccionar o bot ao de Next que na segunda passagem j a encontra o JDK (isto parece acontecer quando se mistura vers oes de 64 bits com vers oes de 32 bits, existem outras solu c oes para este problema mas esta e a mais simples19 ). Ao acabar a instala c ao aceitar a op c ao de deixar correr o SDK Manager do Android SDK Tools para instalar as imagens das plataformas que interessarem, ele selecciona automaticamente um conjunto de plataformas, adicionar a essas j a pr e-seleccionadas as seguintes (basta clicar no nome do lado esquerdo e depois seleccionar o radio button Accept): Google APIs by Google Inc., Android API13, revision 1 Google USB Driver Package, revision 4 Isto instala um total de 23 pacotes. 7. Instalar o Android Development Tools (ADT), plugin para o Eclipse: (a) Arrancar com o Eclipse, seleccionar Help > Install New Software ... (b) Clicar Add no canto superior direito (c) Na janela de di alogo Add Repository que aparece colocar ADT Plugin no Name e na URL da Location colocar: https://dl-ssl.google.com/android/eclipse/ (se isto der problemas trocar https por http). Instalar os 6 pacotes embora s o os 4 primeiros sejam relevantes: Android DDMS, Android Development Tools, Android Hierarchy Viewer, Android Traceview, Tracer for OpenGL ES, Android Native Development Tools. Ao instalar aceitar a licen ca e aceitar instalar conte udo n ao assinado, depois de se rearrancar com o Eclipse em Help / About / Installation details no tab Installed software aparecem esses 4 ou 6 nomes ao lado do Eclipse SDK 8. No Eclipse seleccionar Window /Preferences, escolher Android do lado esquerdo, e colocar o direct orio do SDK do Android, se ele cou instalado no local sugerido por omiss ao trata-se de C:\Program Files (x86)\Android\android-sdk (aten c ao que como este local consiste num nome que tem espa cos embebidos isto pode originar um erro, ver Sec c ao 1.4.6) A partir de agora j a deve ser poss vel ir ao Eclipse e seleccionar, File / New Project, expandir a entrada Android, seleccionar Android Project, fazer Next, e deve ser vis vel a possibilidade de criar o projecto para um conjunto alargado de vers oes da API do Android (desde a 1.5 at e` a 3.2) 9. Instalar o MoSync20 2.5: MoSyncWindows-2.5.exe NOTA IMPORTANTE: Muito provavelmente depois de se instalar o MoSync este d a erro ao arrancar, algo do g enero de n ao conseguir carregar uma biblioteca (apesar do cheiro existir no caminho indicado na mensagem de erro): Failed to load the JNI shared library C:\Program Files\Java\jdk1.6.0_26\bin\..\jre\bin\server\jvm.dll. Tratase muito provavelmente do problema de se estar a usar o JRE que veio junto com o JDK e que portanto e de 64 bits e o MoSync estar ` a espera de um JRE de 32 bits.
17 18 19 20

http://www.eclipse.org/downloads/ http://developer.android.com/sdk/index.html http://stackoverflow.com/questions/4382178/android-sdk-installation-doesnt-find-jdk http://www.mosync.com/download

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

A melhor solu c ao para este erro e instalar agora (isto e, n ao instalar antes mas somente se e quando este erro aparece) a vers ao de 32 bits do Java Run-time Environment (JRE) 1.7.0. Ao se escolher esta vers ao facilita-se conseguir-se distinguir mais facilmente entre as 2 vers oes do Java, isto e, o JDK 1.6.0 + JRE 1.6.0 de 64 bits e o JRE 1.7.0 de 32 bits, j a que se pode usar os comandos java -version ou javac -version para, respectivamente, se saber a vers ao do run-time e a vers ao do compilador de Java. Portanto neste caso instala-se o JRE 1.7.0 de 32 bits21 : jre-7-windows-i586.exe Ap os o JRE 1.7.0 de 32 bits estar instalado voltar a tentar arrancar o MoSync e este erro deve agora necess ter desaparecido e o programa deve arrancar normalmente. E ario proceder ao registo do software criando um username e uma password e fornecendo um email, recebendo depois por email o link que activa o MoSync.

1.4.3

Mac OS X 64 bits: Instalar Eclipse 64 bits + Android SDK + ADT Plugin + MoSync

Usar as explica c oes detalhadas apresentadas na Sec c ao 1.4.2, adaptando-as ao ambiente Mac OS X: 1. Instalar o Eclipse IDE for Java Developers (vers ao para Mac OS X 64 bits)22 2. Instalar o SDK do Android. Por exemplo descarregar o cheiro com a u ltima vers ao do SDK23 , abrir um terminal (/Applications/Utils/Terminal.app) e executar os seguintes comandos: Mac OS X: instalar o Android SDK 7
cd / mkdir /Developer2 cd /Developer2 unzip ~/Downloads/android-sdk_r15-macosx.zip

estando isto feito tem-se acesso ao SDK Manager. Escolheu-se instalar em /Developer2 para o caso de se ter instalado o Xcode da Apple em /Developer. Em Mac OS X o SDK Manager e o programa /Developer2/android-sdk-macosx/tools/android 3. Usando o SDK Manager instalar as imagens das plataformas que interessarem 4. Instalar o Android Development Tools (ADT), plugin para o Eclipse 5. No Eclipse seleccionar Eclipse / Preferences... /Android, e colocar em SDK location o direct orio onde se instalou o SDK, neste caso trata-se de /Developer2/android-sdk-macosx 6. Instalar o MoSync

1.4.4

Actualiza c oes Outubro 2011

A vers ao instalada nos computadores do ISMAI baseou-se no conte udo da Sec c ao 1.4.2, que foi escrita em 31 de Julho 2011. Entretanto sairam novas vers oes de software: ao de produ c ao do MoSync 2.6; inclui24 Em 16 de Setembro 2011 foi disponibilizada a vers actualiza c oes da API (nomeadamente m aquina fotogr aca, manipular os contactos, fontes nativas e sensores, e uma API multi-plataforma para o Facebook); suporta a utiliza c ao de emuladores nativos (isto e, usar o emulador do iOS ou do Android em vez do MoRE); e suporta proling 25 no MoRE.
http://www.oracle.com/technetwork/java/javase/downloads/java-se-jre-7-download-432155.html http://www.eclipse.org/downloads e seleccionar eclipse-java-indigo-SR1-macosx-cocoa-x86_64.tar.gz 23 http://dl.google.com/android/android-sdk_r15-macosx.zip 24 http://www.mosync.com/documentation/manualpages/whats-new-mosync-26-pyramid 25 Ver gloss ario
22 21

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

Computa c ao M ovel

Em 25 de Outubro 2011 foi disponibilizada a vers ao de produ c ao do MoSync 2.7; inclui26 suporte para HTML5, suporte para a C++ Standard Template Library (STL), e suporte para a NFC (Near Field Communication) API Em 22 de Setembro 2011 foi disponibilizado o SDK r13 do Android que inclui o SDK platform Android 3.2 (API level 13). Em 19 de Outubro 2011 foi disponibilizado o SDK r14 do Android que inclui o SDK platform Android 4.0 (API level 14), e em 28 de Outubro o SDK r15 (bug xes do r14) Apesar disso originar software ligeiramente diferente do dispon vel no ISMAI, nos computadores pessoais de cada aluno n ao parece haver problema em actualizar o MoSync da vers ao 2.5 para a 2.7 nem em usar o SDK Android r15, desde que no Android tamb em se instale as APIs 2.3.4 (API level 10), 3.1 (API level 12) e os Samples (exemplos) relativos ao SDK API 10 e API 12.

1.4.5

APIs do Android

O API level identier no Android serve para identicar qual e a vers ao mais alta da framework que e suportada pelo dispositivo, permite que as aplica c oes digam qual e a vers ao da framework que necessitam, e permite que o sistema operativo controle que n ao sejam instaladas no dispositivo aplica c oes que necessitem de vers oes da framework Android que n ao sejam suportadas nesse dispositivo. As aplica c oes no cheiro manifest.xml podem usar o elemento <uses-sdk> para dizer qual e a vers ao m nima da framework que suportam (android:minSdkVersion), a vers ao da framework para a qual a aplica c ao foi desenhada (android:targetSdkVersion, s o passou a existir a partir da API 4), e a vers ao m axima da framework na qual a aplica c ao consegue funcionar (android:maxSdkVersion, s o passou a existir a partir da API 4), esta u ltima deve ser usada com bastante cautela porque pode originar que uma aplica c ao desapare ca do telem ovel logo a seguir ao sistema operativo ser actualizado e as frameworks do Android s ao concebidas para serem compat veis com as vers oes anteriores. Devido a isto vers oes do Android superiores ` a 2.0.1 deixaram de obedecer a android:maxSdkVersion durante a instala c ao ou actualiza c ao de software, somente o Android Market e que continua a utilizar este atributo como um ltro ao apresentar as aplica c oes dispon veis para download. Eis a lista das vers oes do SDK, da API e da vers ao do SQLite3 utilizada, e as respectivas datas de disponibiliza c ao: 19-Ago-2008: Android SDK 0.9 Beta 25-Set-2008: Android SDK 1.0 R1 ao 1.0 base, API 1) 8-Dez-2008: Android SDK 1.0 R2 (vers 7-Mar-2009: Android SDK 1.1 R1 (vers ao 1.1 base 1 1, API 2) 15-Abr-2009: Android SDK 1.5 Pre 28-Abr-2009: Android SDK 1.5 R1 26-Jun-2009: Android SDK 1.5 R2 17-Jul-2009: Android SDK 1.5 R3 (vers ao 1.5 cupcake, API 3), SQLite 3.5.9 16-Set-2009: Android SDK 1.6 Release 1 (vers ao 1.6 donut, API 4), SQLite 3.5.9 28-Out-2009: Android SDK 2.0 Release 1 (vers ao 2.0 eclair, API 5), SQLite 3.5.9 4-Dez-2009: Android SDK 2.0.1 Release 1 (vers ao 2.0.1 eclair 0 1, API 6), SQLite 3.5.9
26

http://www.mosync.com/documentation/manualpages/whats-new-mosync-27-pyramid

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

13-Mai-2010: Android SDK R5 21-Mai-2010: Android SDK R6 ao 2.1.x eclair MR1, API 7), SQLite 3.5.9 6-Dez-2010: Android SDK R7 (vers 6-Dez-2010: Android SDK R8 (vers ao 2.2.x froyo, API 8), SQLite 3.6.22 27-Jan-2011: Android SDK R9 (vers ao 2.3, 2.3.1, 2.3.2 gingerbread, API 9), SQLite 3.6.22 23-Fev-2011: Android SDK R10 (vers ao 2.3.3 e 2.3.4 gingerbread MR1, API 10), SQLite 3.6.22 16-Jul-2011: Android SDK R11 (vers ao 3.0.x honeycomb, API 11), SQLite 3.7.4 16-Jul-2011: Android SDK R12 (vers ao 3.1.x honeycomb MR1, API 12), SQLite 3.7.4 23-Set-2011: Android SDK R13 (vers ao 3.2 honeycomb MR2, API 13), SQLite 3.7.4 19-Out-2011: Android SDK R14 (vers ao 4.0.1 ice cream sandwich, API 14), SQLite 3.7.4 28-Nov-2011: Android SDK R15 (vers ao 4.0.2 ice cream sandwich, API 15), SQLite 3.7.4 16-Dez-2011: Android SDK R16 (vers ao 4.0.3 ice cream sandwich, API 15), SQLite 3.7.4 Mar-2012: Android SDK R17 Apr-2012: Android SDK R18 Apr-2012: Android SDK R19 9-Jul-2012: Android SDK R20 (vers ao 4.1.2 jelly bean, API 16) 13-Nov-2012: Android SDK R21 (vers ao 4.2 jelly bean, API 17) 24-Jul-2013: Android SDK R22 (vers ao 4.3 jelly bean, API 18) ao 4.4 kit kat, API 19) ??-Out-2013: Android SDK R2? (vers Detalhes das principais funcionalidades associadas a cada vers ao do SDK podem ser obtidas das release notes 27 , detalhes das principais funcionalidades associadas a cada n vel da API podem ser obtidas de http://developer.android.com/guide/appendix/api-levels.html

1.4.6

Congurar o Eclipse para criar uma aplica c ao Android

Depois de se instalar o software de desenvolvimento para Android (ver Sec c ao 1.4.2 ou Sec c ao 1.4.3) e necess ario congurar o Eclipse para que este permita criar programas em Android. Sendo assim deve-se efectuar a seguinte sequ encia de passos: 1. Criar um novo Workspace no caso do workspace do Eclipse que estivermos a usar j a conter c odigo de outras aplica c oes Java. Para isso usa-se File / Switch workspace / Other ... e escolhe-se um novo nome. 2. Embora neste momento j a se possa escolher File / New / Android project , a verdade e que n ao conseguimos terminar o wizard (isto e, s o podemos premir cancel ) porque n ao conseguimos seleccionar um SDK target
27

http://mac.softpedia.com/progChangelog/Google-Android-SDK-Changelog-31208.html

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

10

Computa c ao M ovel

3. Para que um ou mais SDK targets estejam dispon veis e necess ario ir a Window / Preferences / Android (em Mac OS X e Eclipse / Preferences... /Android ) e preencher o caminho para o SDK Location. Note-se que se estivermos numa m aquina Windows 7 de 64 bits e o SDK for instalado no local seleccionado por omiss ao ele ca em C:\Program Files (x86)\Android\android-sdk, e isso origina problemas devido ao caminho possuir espa cos embebidos. Embora se possa premir o bot ao Apply e mal isso seja feito apare ca a lista com as diversas vers oes do SDK que est ao instalados, se depois criarmos uma aplica c ao Android usando File / New / Android Project , se depois se tentar compilar e correr essa aplica c ao obt em-se o erro: erros: erro se o Android SDK foi instalado num local com espa cos no nome 10
... [2011-10-30 [2011-10-30 [2011-10-30 [2011-10-30 22:11:36 22:11:36 22:11:36 22:11:36 lixo1] Launching a new emulator with Virtual Device Avs1 Emulator] invalid command-line parameter: Files. Emulator] Hint: use @foo to launch a virtual device named foo. Emulator] please use -help for more information

10

note-se que o erro invalid command-line parameter: Files. adv em do local de instala c ao ser C:\Program Files (x86)\Android\android-sdk, isto e, o Files da mensagem de erro e a parte do nome que vem logo a seguir ao espa co. A solu c ao e no Windows / Preferences / Android alterar o caminho passando a usar o nome curto do direct orio (nome com um m aximo de 8 caracteres e que n ao possui espa cos). Para saber qual e o nome curto de um direct orio ir para uma consola (linha de comando) do Windows e executar o comando dir /x c:\, isto deve dizer algo do g enero que o nome curto de Program Files (x86) e PROGRA~2, nesse caso no Windows / Preferences / Android substituir C:\Program Files (x86)\Android\android-sdk por C:\PROGRA~2\Android\android-sdk. A partir da j a se consegue compilar e executar no emulador programas Android. 1.4.6.1 Threads

Hoje em dia a maioria dos programas que aceitam interac c oes com o utilizador j a n ao se podem dar ao luxo de serem single-threaded, isto e, s o terem um uxo ou sequ encia de execu c ao, e t em de ser multi-threaded, isto e, possuem mais do que uma sequ encia de execu c ao a executar simultaneamente28 . Uma thread e um processo mais leve, isto e que utiliza muito menos recursos do que criar um novo processo. Todas as threads existem dentro do mesmo processo, pelo que partilham recursos incluindo a mem oria e os cheiros abertos. A desvantagem e que qualquer outra thread pode potencialmente ter acesso aos dados de uma thread e se algo correr mal numa thread o processo pode fazer crash, o que termina todas as threads. Cada thread est a associada com uma inst ancia da classe Thread. Uma aplica c ao cria uma thread e fornece o c odigo que deve ser executado nessa thread. Existem duas maneiras de fazer isto: Criar um objecto que implemente o interface Runnable. O interface Runnable dene um u nico m etodo, run() que e suposto conter o c odigo que e para ser executado na thread. O objecto que implementa o interface Runnable e passado ao construtor da classe Thread. Criar uma subclasse de Thread. Como Java n ao suporta heran ca m ultipla e normalmente prefer vel a primeira op c ao, isto e, a nossa classe implementar o interface Runnable em vez de ser uma subclasse de Thread, desta forma pode herdar de quem quiser O exemplo que se segue cria 3 threads, cada uma delas dorme, respectivamente, durante 300, 200 e 400 milisegundos antes de acordar e continuar. Note-se que pode ocorrer a excep c ao
28 Mesmo em sistemas com processadores s o com um core utiliza-se programas multi-threaded, nesses casos quando uma thread e interrompida ou executa at e ao m da unidade de tempo que lhe foi atribu da outra thread ca com o controle do CPU.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

11

InterruptedException que e lan cada pelo sleep quando outra thread tenta interromper esta thread enquanto sleep est a activo. Este programa origina o seguinte output: Hello Hello Hello Hello Hello Hello
11

um, vou dormir durante 300 milisegundos dois, vou dormir durante 200 milisegundos tr^ es, vou dormir durante 400 milisegundos da thread dois, acordei da thread um, acordei da thread tr^ es, acordei version/androidDevel/HelloNumaThread.java 11
import java.lang.InterruptedException; public class HelloNumaThread implements Runnable { protected String nome; protected int duracaoDoSono; public HelloNumaThread(String s, int tempo) { nome = s; duracaoDoSono = tempo; } public void run() { System.out.println("Hello da thread " + nome + ", vou dormir durante " + duracaoDoSono + " milisegundos"); try { Thread.sleep(duracaoDoSono); } catch (InterruptedException e) { System.out.println("Hello outra vez da thread " + nome + ", fui acordada"); } System.out.println("Hello outra vez da thread " + nome + ", acordei"); } public static void main(String args[]) { Thread t1, t2, t3; t1 = new Thread(new HelloNumaThread("um", 300)); t2 = new Thread(new HelloNumaThread("dois", 200)); t3 = new Thread(new HelloNumaThread("tr^ es", 400)); t1.start(); t2.start(); t3.start(); } }

da thread da thread da thread outra vez outra vez outra vez

This code is written to le version/androidDevel/HelloNumaThread.java. Denes: HelloNumaThread, never used. Uses main().

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

12

Computa c ao M ovel

1.5

Arquitectura das Aplica c oes Android

As aplica c oes Android recorrem ` a utiliza c ao de um conjunto de 6 classes principais (Intent, IntentFilter, Activity, Service, BroadcastReceiver, e ContentProvider), e ` a utiliza c ao de um conjunto de 3 cheiros XML principais (AndroidManifest.xml, main.xml, e strings.xml). Eis o contexto da sua utiliza c ao e as suas caracter sticas principais: 1. Classe Intent: representa aquilo que temos a inten c ao de fazer (por exemplo ver o conte udo de um registo dos contactos, abrir um site internet, mostrar uma dada factura). Desta forma um Intent deve ser visto como uma declara c ao de uma necessidade que necessita de ser satisfeita (por um IntentFilter). c ao (action) de um Intent e normalmente um verbo, por exemplo O atributo ac VIEW, PICK, ou EDIT. Para ver o conte udo de um registo podia-se por exemplo usar android.content.Intent.ACTION_VIEW O atributo dados (data) de um Intent e uma URI. Por exemplo para ver todos os contactos usa-se content://contacts/people, e para ver o contacto 18 usa-se content://contacts/people/18
12a

enero de: Para por exemplo obter os registos dos contactos usar-se-ia algo do g Android: ver os registos da base de dados de contactos 12a
Intent pickIntent = new Intent( Intent.ACTION_PICK,Uri.parse("content://contacts/people")); startActivity(pickIntent);

A identica c ao do IntentFilter que deve ser usado e efectuada em run-time (em vez de em compile time ). H a a possibilidade de se substituir funcionalidade standard do Android substituindo um IntentFilter (mas n ao esquecer que isto torna mais dif cil por exemplo o suporte t ecnico remoto j a que deixa de se ter a certeza de numa dada situa c ao qual e o comportamento efectivo do dispositivo m ovel em quest ao) 2. Classe IntentFilter: declara c ao da capacidade e interesse em disponibilizar uma dada funcionalidade para quem dela necessite (um Intent). Os IntentFilters t em de ser registados, esse processo e feito ` a custa do cheiro AndroidManifest.xml 3. Classe Activity: embora uma aplica c ao Android n ao tenha de ter um GUI, se o tiver ent ao ter a obrigatoriamente pelo menos um Activity. Normalmente existe uma rela c ao de um para um entre um Activity e um ecr a vis vel. Um Activity efectua o display de um interface gr aco e responde a eventos iniciados pelo sistema e pelos utilizadores. Uma das tarefas principais efectuadas pela classe Activity e efectuar o display dos elementos do interface gr aco (que s ao implementados na forma de Views) e s ao denidos em cheiros XML com o layout. Normalmente a interac c ao e ass ncrona (isto e, n ao se ca bloqueado ` a espera do resultado), mas se se quiser uma interac c ao s ncrona ela e poss vel (utilizando o m etodo startActivityForResult()) Android: exemplo de uma Activity 12b
package com.msi.manning.chapter1; import android.app.Activity; import android.os.Bundle; public class Activity1 extends Activity { @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main); } }
Uses onCreate() 27.

12b

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

13

13

4. Classe Service: se a aplica c ao e suposto ter um tempo de execu c ao longo (por exemplo uma tarefa de backup) ent ao ela deve ser colocada num servi co. Os servi cos devem ser iniciados periodicamente ou quando forem necess arios sendo nesse caso iniciados como reac c ao a um alarme do sistema, e devem terminar a execu c ao (de modo a libertar recursos) quando a tarefa estiver terminada. Os servi cos n ao possuem um GUI [Ableson et al. 2011, p ag. 18] Android: exemplo de um Service 13
package com.msi.manning.chapter1; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.util.Log; public class Service1 extends Service implements Runnable { public static final String tag = "service1"; private int counter = 0; @Override protected void onCreate() { super.onCreate(); Thread aThread = new Thread (this); aThread.start(); } public void run() { while (true) { try { Log.i(tag, "service1 firing : # " + counter++); Thread.sleep(10000); } catch(Exception ee) { Log.e(tag,ee.getMessage()); } } } @Override public IBinder onBind(Intent intent) { return null; } }
Uses onCreate() 27.

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

14

Computa c ao M ovel

5. Classe BroadcastReceiver: aplica c oes que pretendem reagir a um evento global (como por exemplo a recep c ao de uma mensagem ou o telefone tocar). O registo pode ser feito de 2 maneiras diferentes [Ableson et al. 2011, p ag. 19]: (a) Colocando uma tag <receiver> no AndroidManifest.xml que descreve o nome da classe BroadcastReceiver e enumera os seus <IntentFilter>s. Neste caso a aplica c ao n ao necessita de estar a executar para ser chamada a lidar com um evento global e quando estiver a correr) usando o m etodo registerReceiver (b) Registando-se em run-time (isto Os BroadcastReceiver n ao possuem um GUI e devem demorar um tempo muito curto a executar (semelhante ao tempo associado a um interrupt handler ). Se a tarefa pode demorar bastante tempo a executar ent ao o BroadcastReceiver deve e iniciar um Service para executar a funcionalidade pretendida 6. Classe ContentProvider: para a aplica c ao gerir dados (ver Sec c ao ??) 7. Ficheiro AndroidManifest.xml: existe uma rela ca o de um para um entre um destes cheiros e a aplica c ao correspondente. Eis um exemplo: Android: exemplo de um AndroidManifest.xml 14
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.msi.manning.unlockingandroid"> <uses-permission android:name="android.permission.RECEIVE_SMS" /> <application android:icon="@drawable/icon"> <activity android:name=".Activity1" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <receiver android:name=".MySMSMailBox" > <intent-filter> <action android:name="android.provider.Telephony.SMS_RECEIVED" /> </intent-filter> </receiver> </application> </manifest>

14

8. Ficheiro main.xml: usado para descrever o layout dos componentes do interface gr aco. No Eclipse pode ser editado directamente em modo texto ou manipulado gracamente, podendo misturar-se os dois tipos de edi c ao 9. Ficheiro strings.xml: usado para se colocar strings (texto, etiquetas, mensagens, etc) que possam necessitar de ser localizadas ou adaptadas consoante o idioma da aplica c ao. No Eclipse pode ser editado directamente em modo texto ou manipulado recorrendo a um interface gr aco, podendo misturar-se os dois tipos de edi c ao

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

15

1.5.1

Hello World em Android

Apresentam-se aqui duas vers oes da aplica c ao Hello World em Android, a primeira baseada somente em dois cheiros (HelloAndroid1.java e AndroidManifest.xml) mas utilizando um estilo de programa c ao que se bem que mais simples n ao e o aconselh avel j a que lhe falta exibilidade, e a segunda baseada em quatro cheiros (HelloAndroid2.java, strings.xml, main.xml, AndroidManifest.xml) recorrendo ` a t ecnica normal que deve ser utilizada para criar aplica c oes Android. 1.5.1.1
15a

Hello World em Android mas sem usar o estilo do Android

version/androidDevel/helloWorld1/HelloAndroid1.java 15a package pt.ismai.cm2011; import android.app.Activity; import android.os.Bundle; import android.widget.TextView; public class HelloAndroid1 extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); TextView tv = new TextView(this); tv.setText("Hello World em Android, vers~ ao simplificada"); setContentView(tv); } }
This code is written to le version/androidDevel/helloWorld1/HelloAndroid1.java. Denes: HelloAndroid1, used in chunk 15b. Uses onCreate() 27.

15b

importante a coloca E c ao do . no in cio do <activity android:name. version/androidDevel/helloWorld1/AndroidManifest.xml 15b


<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="pt.ismai.cm2011" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="3" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name"> <activity android:name=".HelloAndroid1" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
This code is written to le version/androidDevel/helloWorld1/AndroidManifest.xml. Uses HelloAndroid1 15a.

Para criar este projecto em Eclipse fazer: 1. File / New / Android Project 2. Colocar HelloAndroid1 no Project Name

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

16

Computa c ao M ovel

3. Seleccionar Android 1.5 como Build Target 4. Colocar pt.ismai.cm2011 no Package Name 5. Desseleccionar Create Activity (j a que neste caso n ao estamos a fazer um projecto Android usando o estilo de desenvolvimento para Android) 6. No Package Explorer (janela ` a esquerda do Eclipse) seleccionar e expandir HelloAndroid1 / src / pt.ismai.cm2011, seguidamente premir o bot ao da direita do rato e seleccionar New / Class e colocar no Name HelloAndroid1 7. Adicionar a este cheiro o conte udo acima apresentado para HelloAndroid1.java 8. Nota: uma forma simples para se adicionar os import que um cheiro necessite e premir Ctrl-Shift-O (ou Cmd-Shift-O em Mac OS X). Isto e um atalho do eclipse que identica os pacotes que est ao em falta e adiciona-os automaticamente udo do AndroidManifest.xml pelo acima apresentado 9. Substituir o conte 10. Seleccionar Run, escolher run as Android Application, e se necess ario criar um new Android Virtual Device, a aplica c ao deve arrancar no emulador 1.5.1.2
16a

Hello World no estilo Android

version/androidDevel/helloWorld2/res/layout/main.xml 16a <?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/textview" android:layout_width="fill_parent" android:layout_height="fill_parent" android:text="@string/hello"/>
This code is written to le version/androidDevel/helloWorld2/res/layout/main.xml.

16b

version/androidDevel/helloWorld2/res/values/strings.xml 16b <?xml version="1.0" encoding="utf-8"?> <resources> <string name="hello">Hello World em Android, vers~ ao correcta</string> <string name="app_name">Hello Android2</string> </resources>
This code is written to le version/androidDevel/helloWorld2/res/values/strings.xml.

16c

A liga c ao entre estes dois cheiros XML e o c odigo Java e efectuada recorrendo ao cheiro R.java que e gerado automaticamente. version/androidDevel/helloWorld2/src/pt/ismai/cm2011/HelloAndroid2.java 16c
package pt.ismai.cm2011; import android.app.Activity; import android.os.Bundle; public class HelloAndroid2 extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } }
This code is written to le version/androidDevel/helloWorld2/src/pt/ismai/cm2011/HelloAndroid2.java. Denes: HelloAndroid2, used in chunk 17. Uses onCreate() 27.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

17

17

version/androidDevel/helloWorld2/AndroidManifest.xml 17 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="pt.ismai.cm2011" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="3" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name"> <activity android:name=".HelloAndroid2" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
This code is written to le version/androidDevel/helloWorld2/AndroidManifest.xml. Uses HelloAndroid2 16c.

Para criar este projecto em Eclipse fazer: 1. File / New / Android Project 2. Colocar HelloAndroid2 no Project Name 3. Seleccionar Android 1.5 como Build Target 4. Colocar pt.ismai.cm2011 no Package Name 5. Manter seleccionado Create Activity 6. No Package Explorer (janela ` a esquerda do Eclipse) seleccionar e expandir HelloAndroid2 / src / pt.ismai.cm2011 / HelloAndroid2Activity, mantendo o nome do cheiro seleccionado fazer File / Rename..., e alterar o nome de HelloAndroid2Activity para HelloAndroid2 7. Substituir o conte udo deste cheiro pelo conte udo acima apresentado para HelloAndroid2.java 8. Substituir o conte udo de res/layout/main.xml pelo conte udo acima apresentado 9. Substituir o conte udo de res/values/strings.xml pelo conte udo acima apresentado 10. Substituir o conte udo do AndroidManifest.xml pelo acima apresentado 11. Seleccionar Run, escolher run as Android Application, e se necess ario criar um new Android Virtual Device, a aplica c ao deve arrancar no emulador A raz ao porque este programa funciona como esperado e que o Android arranca o HelloAndroid2, este no onCreate usando setContentView utiliza a informa c ao existente no main.xml (recorrendo ao R.java gerado automaticamente e que liga o cheiro xml ao c odigo java), no main.xml no android:text faz-se refer encia a @string/hello e em strings.xml o string name="hello" tem como conte udo "Hello World em Android, vers~ ao correcta" e por isso esse texto e visualizado.

1.5.2

Utiliza c ao do Log do Android

No Android sempre que queremos escrever uma mensagem que que registada, por exemplo informa c ao sobre um erro que ocorreu no programa ou se pretendemos guardar outro tipo de informa c ao, deve-se escrever para o Android log. A classe Log cont em m etodos que permitem adicionar entradas de log com uma classica c ao de Verbose, Debug, Information, Warning, e Error. Pode-se ltrar as entradas no log de acordo

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

18

Computa c ao M ovel

18

com a classica c ao, o ID do processo que originou a entrada de log, e/ou o valor de uma tag que se adiciona a cada entrada de log. Por exemplo para se criar uma entrada de log com a classica c ao de Information, uma tag de calcular, e a mensagem valor demasiado alto faz-se Log.i("calcular", "valor demasiado alto");. Para se ver as entradas no log com capacidade para ltrar e s o ver as que nos interessam usa-se a LogCat view da perspectiva DDMS (Window / Open Perspective / DDMS) [Ableson et al. 2011, p ag. 40]. Android: escrever para o log 18
import android.util.Log; public class Exemplo { public static final String tag = "oMeuLog"; // ... final Button button = (Button)findViewById(R.id.calculate); button.setOnClickListener(new Button.OnClickListener() { public void onClick(View v) { try { Log.i(tag, "chamada a onClick"); // ... } catch (Exception e) { Log.e(tag, "excep c~ ao ..."); // ... } } }); }
Uses setOnClickListener().

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

19

1.5.3

GUIs em Android

19

Pretende-se agora criar uma aplica c ao exemplo em Android que utilize TextView (etiqueta de texto), EditText (campo de texto edit avel que pode ser congurado para s o ler n umeros), e Button para permitir escrever um valor base e a percentagem de desconto a aplicar, e que quando se prime o bot ao obt em-se o valor a pagar. No caso de haver um erro a ler o n umero (por exemplo por o campo estar vazio) escreve-se para o log uma mensagem de erro e mostra-se uma janela de di alogo a avisar que o valor e inv alido. version/androidDevel/descontos/src/pt/ismai/cm2011/DescontosActivity.java 19
package pt.ismai.cm2011; import import import import import import android.app.Activity; android.os.Bundle; android.view.View; android.widget.Button; android.widget.EditText; android.widget.TextView;

public class DescontosActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); final String tag = "descontos"; final AlertDialog.Builder adb = new AlertDialog.Builder(this); final Button button = (Button)findViewById(R.id.button1); final EditText valor = (EditText)findViewById(R.id.editValor); final EditText desconto = (EditText)findViewById(R.id.oDesconto); final TextView resultado = (TextView)findViewById(R.id.oResultado); button.setOnClickListener(new Button.OnClickListener() { public void onClick(View v) { String umValor = valor.getText().toString(); String umDesconto = desconto.getText().toString(); try { double dv = Double.parseDouble(umValor); double dd = Double.parseDouble(umDesconto) / 100; resultado.setText(new Double(dv * (1 - dd)).toString()); } catch (NumberFormatException e) { String errMsg = "Valor inv alido"; Log.e(tag, errMsg); AlertDialog ad = adb.create(); ad.setMessage(errMsg); ad.show(); } } }); } }
This code is written to le version/androidDevel/descontos/src/pt/ismai/cm2011/DescontosActivity.java. Denes: DescontosActivity, used in chunk 20b. Uses onCreate() 27 and setOnClickListener().

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

20

Computa c ao M ovel

20a

Aqui explicita-se o texto a aparecer nas etiquetas (TextView, Button) e o nome da aplica c ao. version/androidDevel/descontos/res/values/strings.xml 20a
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">Descontos</string> <string name="base">Valor base</string> <string name="desconto">Desconto</string> <string name="calcular">Calcular</string> <string name="vazio"></string> </resources>
This code is written to le version/androidDevel/descontos/res/values/strings.xml.

20b

version/androidDevel/descontos/AndroidManifest.xml 20b <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="pt.ismai.cm2011" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="3" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:label="@string/app_name" android:name=".DescontosActivity" > <intent-filter > <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
This code is written to le version/androidDevel/descontos/AndroidManifest.xml. Uses DescontosActivity 19.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

21

21

Usando o interface para criar layouts gr acos disponibilizado pelo Eclipse cria-se um TableLayout, e coloca-se na primeira linha um TextView seguido de um EditText congurado para number (onde o utilizador coloca o valor propriamente dito), na segunda linha coloca-se um TextView seguido de um EditText congurado para number (onde o utilizador coloca a percentagem de desconto, um n umero entre 0 e 100), na terceira linha coloca-se um Button (para mandar calcular o valor a pagar ap os se aplicar o desconto), e na quarta linha coloca-se um TextView para apresentar o resultado. Mudando para a visualiza c ao em modo textual (main.xml) obt em-se algo de semelhante ao aqui apresentado. De modo a que no cheiro R.java, gerado automaticamente, existam as entradas apropriadas que nos permitam referenciar os widgets que nos interessam (alguns widgets, por exemplo TextView, podem n ao ser necess arios referenciar a partir do c odigo da aplica c ao e por isso escusam de ter um id), coloca-se em cada um deles android:id="@+id/nomeEscolhidoParaID ", isto vai fazer com que em R.java apare ca algo do tipo public final class R { ... public static final class id { public static final int nomeEscolhidoParaID=0x7f050000; ... } ... }. No Eclipse em vez de se editar o main.xml directamente pode-se controlar os Id e os Text associados aos widgets clicando-se com o bot ao da direita sobre o componente e seleccionando-se, respectivamente, Edit ID... e Edit Text.... version/androidDevel/descontos/res/layout/main.xml 21
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="horizontal" > <TableLayout android:id="@+id/tableLayout1" android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_weight="0.02" > <TableRow android:id="@+id/tableRow1" android:layout_width="wrap_content" android:layout_height="wrap_content" > <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/base" /> <EditText android:id="@+id/editValor" android:layout_width="84dp" android:layout_height="wrap_content" android:inputType="number" /> </TableRow> <TableRow android:id="@+id/tableRow2" android:layout_width="wrap_content" android:layout_height="wrap_content" > <TextView android:id="@+id/textView2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/desconto" /> <EditText android:id="@+id/oDesconto" android:layout_width="wrap_content" android:layout_height="wrap_content" android:inputType="numberDecimal" />

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

22

Computa c ao M ovel

</TableRow> <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/calcular" /> <TextView android:id="@+id/oResultado" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/vazio" android:textAppearance="?android:attr/textAppearanceMedium" /> </TableLayout> </LinearLayout>
This code is written to le version/androidDevel/descontos/res/layout/main.xml.

1.5.4

O ciclo de vida das aplica c oes Android

FIXME onCreate(), onStart(), onResume(), onPause(), onStop(), onRestart(), onDestroy().

1.5.5

Tarefas Ass ncronas em Android

Quando se desenvolve software em Android e importante notar o seguinte [Mednieks et al. 2011, p ags. 142156]: A framework do interface gr aco do Android, da mesma forma do que acontece noutras frameworks de interfaces gr acos equivalentes, e event-driven, baseia-se no uso de uma biblioteca com uma hierarquia de componentes gr acos, e e single-threaded . Se fosse multithreaded seria quase imposs vel evitar a possibilidade de aparecimento de um deadlock ao reagir a eventos de input (teclado ou ecr a t actil), output (display). Os pedidos de input/output s ao processados sequencialmente pela thread associada ao GUI, por ordem de chegada Tarefas realizadas pelo programa do utilizador que demorem algum tempo a executar (mais do que alguns milisegundos) devem ser executadas numa thread separada necess E ario sincronizar o funcionamento dessa thread com o funcionamento da thread do GUI A classe android.os.AsyncTask<T,S,U> simplica o processo de execu c ao numa thread separada e a respetiva sincroniza c ao. Baseia-se no uso de tipos gen ericos e disponibiliza os m etodos onPreExecute(), U doInBackground(T... args), onProgressUpdate(S... progress), onPostExecute(U result). Os argumentos T, S, U s ao os seguintes: T eo tipo do argumento a dar ` a worker thread que vai fazer o trabalho propriamente dito, S e o tipo das unidades em que se vai reportar o progresso, e U e o tipo de retorno da worker thread. Se n ao for necess ario utilizar onProgressUpdate(S... progress) ent ao no lugar do S deve-se colocar Void (querendo dizer uma refer encia para uma classe que representa a keyword java void) ao sincronizados de tal forma que estas A classe AsyncTask garante que todos os callbacks s opera c oes s ao seguras sem terem a necessidade de serem explicitamente sincronizadas: Alterar o valor de atributos da classe no construtor ou em onPreExecute(), e ler os seus valores em doInBackground(args...).

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

23

Alterar o valor de atributos da classe em doInBackground(args...), e referir-se a eles em onProgressUpdate(progress...) e onPostExecute(result). Cada inst ancia de uma especializa c ao da classe android.os.AsyncTask<T,S,U> s o pode ser executada uma vez, tentar execut a-la mais do que uma vez origina a excep c ao IllegalStateException, deve-se criar uma nova inst ancia da classe cada vez que seja necess ario
23a

version/androidDevel/lixo.txt 23a exemplo: uso de AsyncTaskDemo mas sem indicador de progresso 23b
This code is written to le version/androidDevel/lixo.txt.

23b

exemplo: uso de AsyncTaskDemo mas sem indicador de progresso 23b (23a) package ... import android.app.Activity; import android.os.AsyncTask; ... public class AsyncTaskDemo extends Activity { private final class AsyncInitGame extends AsyncTask<String, Void, String> { private final View dots; private final Game game; private final TextView message; private final Drawable bg; AsyncTask: public AsyncInitGame(View dots, Drawable bg, Game game, TextView msg) 23c AsyncTask: protected void onPreExecute() 23d AsyncTask: protected void onPostExecute(String msg) 24a AsyncTask: protected String doInBackground(String... args) 24b } int mInFlight; @Override AsyncTask: public void onCreate(Bundle state) 24c }
Denes: AsyncTaskDemo, used in chunk 25.

23c

AsyncTask: public AsyncInitGame(View dots, Drawable bg, Game game, TextView msg) 23c public AsyncInitGame(View dots, Drawable bg, Game game, TextView msg) { this.dots = dots; this.bg = bg; this.game = game; this.message = msg; }
Denes: AsyncInitGame(), used in chunk 24c.

(23b 25)

23d

AsyncTask: protected void onPreExecute() 23d @Override // runs on the GUI thread protected void onPreExecute() { if (mInFlight++ < 0) { dots.setBackgroundResource(R.anim.dots); ((AnimationDrawable)dots.getBackground()).start(); } }
Denes: onPreExecute(), never used.

(23b 25)

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

24

Computa c ao M ovel

24a

AsyncTask: protected void onPostExecute(String msg) 24a @Override // runs on the GUI thread protected void onPostExecute(String msg) { if (--mInFlight < 0) { ((AnimationDrawable)dots.getBackground()).stop(); dots.setBackgroundDrawable(bg); } message.setText(msg); }
Denes: onPostExecute(), never used.

(23b 25)

24b

AsyncTask: protected String doInBackground(String... args) 24b @Override // runs on its own thread protected String doInBackground(String... args) { return ((1 != args.length) || (null == args[0])) ? null : game.initialize(args[0]); }
Denes: doInBackground(), used in chunk 25.

(23b)

24c

AsyncTask: public void onCreate(Bundle state) 24c (23b) public void onCreate(Bundle state) { super.onCreate(state); setContentView(R.layout.asyncdemo); final View dots = findViewById(R.id.dots); final Drawable bg = dots.getBackground(); final TextView msg = ((TextView)findViewById(R.id.msg)); final Game game = Game.newGame(); ((Button) findViewById(R.id.start)).setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { new AsyncInitGame(dots, bg, game, msg).execute("basic"); } }); }
Uses AsyncInitGame() 23c, onCreate() 27, and setOnClickListener().

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

25

25

exemplo: uso de AsyncTaskDemo com indicador de progresso 25 package ... import android.app.Activity; import android.os.AsyncTask; ... /** AsyncTaskDemo */ public class AsyncTaskDemoWithProgressIndicator extends Activity { private final class AsyncInitGame extends AsyncTask<String, Integer, String> implements Game.InitProgressListener { AsyncTask: public AsyncInitGame(View dots, Drawable bg, Game game, TextView msg) 23c AsyncTask: protected void onPreExecute() 23d AsyncTask: protected void onPostExecute(String msg) 24a // runs on its own thread @Override protected String doInBackground(String... args) { return ((1 != args.length) || (null == args[0])) ? null : game.initialize(args[0], this); } @Override // runs on the UI thread protected void onProgressUpdate(Integer... vals) { updateProgressBar(vals[0].intValue()); } @Override // runs on its own thread public void onInitProgress(int pctComplete) { publishProgress(Integer.valueOf(pctComplete)); } } int mInFlight; int mComplete; @Override public void onCreate(Bundle state) { super.onCreate(state); setContentView(R.layout.asyncdemoprogress); final View dots = findViewById(R.id.dots); final Drawable bg = dots.getBackground(); final TextView msg = ((TextView)findViewById(R.id.msg)); final Game game = Game.newGame(); ((Button) findViewById(R.id.start)).setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { mComplete = 0; new AsyncInit(dots, bg, game, msg).execute("basic"); } }); } void updateProgressBar(int progress) { int p = progress; if (mComplete < p) { mComplete = p; ((ProgressBar)findViewById(R.id.progress)).setProgress(p); } } }
Denes: onInitProgress(), never used. onProgressUpdate(), used in chunk 33b. updateProgressBar(), never used. Uses AsyncTaskDemo 23b, doInBackground() 24b 33a, onCreate() 27, and setOnClickListener().

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

26

Computa c ao M ovel

1.5.6

Thread que executa c alculos em background com indicador de progresso

26

Apresenta-se aqui a implementa c ao completa (isto e, MainActivity.java, AndroidManifest.xml, activity_main.xml, strings.xml) de uma aplica c ao que possui 2 campos de texto onde se podem os inteiros, um campo de texto onde n colocar n ao se pode escrever e que serve para apresentar a m edia, e um bot ao. Cada vez que se prime o bot ao e lan cada uma thread em background que os aleat vai gerar 200 000 n orios pertencentes ao intervalo [min..max], seguidamente o campo com os gerados at e ao momento. a m edia e actualizado de modo a representar a m edia de todos os n Note-se que entre cada vez que se prime o bot ao se pode alterar os valores do m nimo ou do os aleat orios e executada numa thread separada que m aximo. A opera c ao de gerar os 200 000 n os corre em background, de modo a dar feedback do andamento da opera c ao cada vez que 10 000 n s ao gerados o texto do bot ao e atualizado de modo a apresentar a percentagem do trabalho que j a foi realizado (isto e, a informa c ao de progresso avan ca em m ultiplos de 5%). Para al em disso este m etodo implementa diversos m etodos do ciclo de vida das aplica c oes Android, embora na maior parte dos casos a u nica coisa que faz e mostrar um curto alerta recorrendo a um Toast. version/androidDevel/MainActivity.java 26
package pt.ismai.cm2012; import java.util.Random; import android.app.Activity; import android.os.AsyncTask; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends Activity { protected EditText campoInputMin, campoInputMax; protected TextView campoOutputMed; protected int inputMin, inputMax; protected double outputMed; protected Random r; protected Button botaoGerar; protected int contador; protected double percentage; AsyncGenerator backgroundTask; CicloDeVida: public void onCreate(Bundle savedInstanceState) 27 CicloDeVida: protected void restoreVarsFromBundle(Bundle savedInstanceState) 28b CicloDeVida: onStart(), onResume(), onPause(), onStop(), onRestart(), onDestroy() 29a CicloDeVida: public void onSaveInstanceState(Bundle outputState) 28a CicloDeVida: public void onRestoreInstanceState(Bundle savedInstanceState) 29b CicloDeVida: public void gerarNumeros(int n) 30a CicloDeVida: public void atualizarNumeros() 30b CicloDeVida: MyAsyncInit() 31a }
This code is written to le version/androidDevel/MainActivity.java. Denes: MainActivity, used in chunks 34, 4547, 50b, 52a, and 59. Uses AsyncGenerator 31a.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

27

27

No m etodo onCreate() para al em de se obter acesso aos elementos do interface gr aco (botaoGerar, campoInputMin, campoInputMax, e campoOutputMed), inicializa-se o contador com zero (usado os aleat os aleat no processo de calcular as m edias dos n orios), e inicializa-se o gerador de n orios. Para al em disso usando uma classe an onima adiciona-se ao bot ao um setOnClickListener() que, cada vez que o bot ao e premido, cria uma nova inst ancia da classe AsyncGenerator que e uma subclasse de AsyncTask, e executa essa inst ancia chamando o m etodo execute(), isto despoleta os aleat a cria c ao da thread em backgound e a gera c ao de 200 000 n orios. Note-se que o argumento fornecido a execute() e o argumento recebido por doInBackground(). CicloDeVida: public void onCreate(Bundle savedInstanceState) 27 (26)
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); campoInputMin = (EditText)findViewById(R.id.inputMinimo); campoInputMax = (EditText)findViewById(R.id.inputMaximo); campoOutputMed = (TextView)findViewById(R.id.outputMedia); botaoGerar = (Button)findViewById(R.id.botaoGerar); botaoGerar.setOnClickListener(new Button.OnClickListener() { public void onClick(View v) { backgroundTask = new AsyncGenerator(botaoGerar, campoInputMin, campoInputMax, campoOutputMed); backgroundTask.execute(200000); } }); contador = 0; r = new Random(999); // s o para debug e que se usa o 999 if (savedInstanceState != null) { restoreVarsFromBundle(savedInstanceState); Toast.makeText(this, "onCreate(with bundle)", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "onCreate(null)", Toast.LENGTH_SHORT).show(); } }
Denes: onCreate(), used in chunks 12b, 13, 15a, 16c, 19, 24c, 25, 39, 4345, 47, 49, 50a, and 61. Uses AsyncGenerator 31a, AsyncGenerator() 31b, restoreVarsFromBundle() 28b, and setOnClickListener().

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

28

Computa c ao M ovel

28a

No caso da aplica c ao ser interrompida e ter de guardar o seu estado o m etodo onSaveInstanceState armazena no Bundle os valores que se encontram nos campos campoInputMin, campoInputMax, e campoOutputMed, e o valor do contador, usando tags apropriadas. CicloDeVida: public void onSaveInstanceState(Bundle outputState) 28a (26)
@Override public void onSaveInstanceState(Bundle outputState) { String s = campoInputMin.getText().toString(); if (!s.equals("")) { inputMin = Integer.parseInt(s); outputState.putInt("m nimo", inputMin); } s = campoInputMax.getText().toString(); if (!s.equals("")) { inputMax = Integer.parseInt(s); outputState.putInt("m aximo", inputMax); } s = campoOutputMed.getText().toString(); if (!s.equals("")) { outputMed = Double.parseDouble(s); outputState.putDouble("m edia", outputMed); } outputState.putInt("contador", contador); Toast.makeText(this, "onSaveInstanceState()", Toast.LENGTH_SHORT).show(); super.onSaveInstanceState(outputState); }
Denes: onSaveInstanceState(), never used.

28b

O m etodo restoreVarsFromBundle() faz a opera c ao inversa de onSaveInstanceState(). Optou-se por recorrer a um m etodo separado de modo a permitir que ele seja chamado a partir de dois locais, quer a partir do onCreate() quer a partir do onRestoreInstanceState(). CicloDeVida: protected void restoreVarsFromBundle(Bundle savedInstanceState) 28b (26)
protected void restoreVarsFromBundle(Bundle savedInstanceState) { inputMin = savedInstanceState.getInt("m nimo"); if (inputMin != 0) campoInputMin.setText("" + inputMin); inputMax = savedInstanceState.getInt("m aximo"); if (inputMax != 0) campoInputMax.setText("" + inputMax); outputMed = savedInstanceState.getInt("m edia"); if (outputMed != 0) campoOutputMed.setText("" + outputMed); contador = savedInstanceState.getInt("contador"); }
Denes: restoreVarsFromBundle(), used in chunks 27 and 29b.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

29

29a

CicloDeVida: onStart(), onResume(), onPause(), onStop(), onRestart(), onDestroy() 29a @Override public void onStart() { super.onStart(); Toast.makeText(this, "onStart()", Toast.LENGTH_SHORT).show(); } @Override public void onResume() { super.onResume(); Toast.makeText(this, "onResume()", Toast.LENGTH_SHORT).show(); } @Override public void onPause() { Toast.makeText(this, "onPause()", Toast.LENGTH_SHORT).show(); super.onPause(); } @Override public void onStop() { Toast.makeText(this, "onStop()", Toast.LENGTH_SHORT).show(); super.onStop(); } @Override public void onRestart() { super.onRestart(); Toast.makeText(this, "onRestart()", Toast.LENGTH_SHORT).show(); } @Override public void onDestroy() { Toast.makeText(this, "onDestroy()", Toast.LENGTH_SHORT).show(); super.onDestroy(); } CicloDeVida: public void onRestoreInstanceState(Bundle savedInstanceState) 29b (26) @Override public void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); if (savedInstanceState != null) restoreVarsFromBundle(savedInstanceState); Toast.makeText(this, "onRestoreInstanceState()", Toast.LENGTH_SHORT).show(); }
Denes: onRestoreInstanceState(), never used. Uses restoreVarsFromBundle() 28b.

(26)

29b

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

30

Computa c ao M ovel

30a

Cada chamada ` a fun c ao gerarNumeros(n, ...) origina n chamadas a atualizarNumeros(), sendo que o valor retornado por atualizarNumeros() e a m edia dos valores obtidos at e ao momento, pelo que o que interessa que a fun c ao gerarNumeros() retorne e o u ltimo valor obtido para a m edia. CicloDeVida: public void gerarNumeros(int n) 30a (26)
public double gerarNumeros(int n, int min, int max, double media) { double d = media; for (int k = 0; k < n; ++k) d = atualizarNumeros(min, max, d); return d; }
Denes: gerarNumeros(), used in chunk 33a. Uses atualizarNumeros() 30b.

30b

o no intervalo de min (inclusive) Cada chamada a atualizarNumeros() origina que um novo n os aleat a max (inclusive) seja gerado, o que faz com que o contador (de n orios gerados at e ao momento) seja incrementado e que a media seja atualizada. o aleat O novo valor, oNovo, j a que e um n orio entre min e max, tem um valor que e calculado o aleat da seguinte forma: min + n (max min + 1), em que n e um n orio entre 0 (inclusive) e 1 (exclusive). Aceitou-se que o max pudesse ser menor do que o min, nesse caso troca-se o valor das 2 vari aveis. CicloDeVida: public void atualizarNumeros() 30b (26) public double atualizarNumeros(int min, int max, double media) { int largura = max - min; int base = min; if (largura < 0) { largura = -largura; base = max; } ++largura; int oNovo = (int)(r.nextDouble() * largura) + base; media = (media * contador + oNovo) / (contador + 1); ++contador; return media; }
Denes: atualizarNumeros(), used in chunk 30a.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

31

31a

A classe android.os.AsyncTask<T,S,U>, simplica o processo de execu c ao numa thread separada e a respetiva sincroniza c ao. Baseia-se no uso de tipos gen ericos e disponibiliza os m etodos onPreExecute(), U doInBackground(T... args), onProgressUpdate(S... progress), onPostExecute(U result). Os argumentos T, S, U s ao os seguintes: T e o tipo do argumento a dar ` a worker thread (par ametros) que vai fazer o trabalho propriamente dito (esse argumento e para ser fornecido ao m etodo execute() da inst ancia da classe, que depois de uma forma autom atica o faz chegar ao doInBackground()), S e o tipo das unidades em que se vai reportar o progresso, eU e o tipo de retorno da worker thread que por sua vez e passado ao m etodo onPostExecute(). Por outras palavras trata-se de android.os.AsyncTask<args,progress,result>. CicloDeVida: MyAsyncInit() 31a (26)
private final class AsyncGenerator extends AsyncTask<Integer, Double, Double> { protected Button b; protected String savedLabel; protected TextView tMin, tMax, tMedia; protected int min, max; protected double media; protected boolean ignorar; AsyncGenerator: public AsyncGenerator(View botao) 31b AsyncGenerator: protected void onPreExecute() 32 AsyncGenerator: protected String doInBackground(Integer... args) 33a AsyncGenerator: protected void onProgressUpdate(Double... percentComplete) 33b AsyncGenerator: protected void onPostExecute(String msg) 34a }
Denes: AsyncGenerator, used in chunks 26, 27, and 31b.

31b

Os argumentos fornecidos ao construtor s ao as refer encias para os 4 elementos do interface gr aco. Desta forma a classe AsyncGenerator consegue estar isolada do ambiente que a rodeia, n ao tendo de aceder a nenhum dos atributos da classe envolvente (n ao era obrigat oorio fazer desta maneira mas assim ca melhor). AsyncGenerator: public AsyncGenerator(View botao) 31b (31a)
public AsyncGenerator(Button botao, TextView min, TextView max, TextView media) { b = botao; tMedia = media; tMin = min; tMax = max; }
Denes: AsyncGenerator(), used in chunk 27. Uses AsyncGenerator 31a.

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

32

Computa c ao M ovel

32

A fun c ao onPreExecute() e chamada antes de se come car o trabalho em background. Trata-se do u ltimo momento em que ainda podemos manipular elementos do interface gr aco, por isso o que esta fun c ao faz e colocar em savedLabel o texto original que estava no bot ao (de modo a permitir mais tarde repor esse texto), coloca 0% como texto do bot ao para indicar que o trabalho ainda est a no in cio, desativa o bot ao de modo a que ningu em o possa premir, seguidamente l e os valores dos 3 campos de texto, tMin tMax tMedia, que tem refer encias para os campos campoInputMin campoInputMax campoOutputMed, e coloca-os em vari aveis chamadas, respectivamente, min, max, media. A vari avel ignorar ter a o valor de true se n ao for poss vel dar seguimento ao trabalho a executar em background por um dos valores, o m aximo ou o m nimo ou ambos, n ao tiver sido fornecido. Nesse caso uma ou mais das vari aveis max e min podem n ao car inicializadas. AsyncGenerator: protected void onPreExecute() 32 (31a)
@Override // runs on the GUI thread protected void onPreExecute() { ignorar = false; savedLabel = b.getText().toString(); b.setText("0%"); b.setEnabled(false); String s = tMedia.getText().toString(); if (s.equals("")) media = 0.0; else media = Double.parseDouble(s); s = tMin.getText().toString(); if (s.equals("")) { ignorar = true; return; } min = Integer.parseInt(s); s = tMax.getText().toString(); if (s.equals("")) { ignorar = true; return; } max = Integer.parseInt(s); }
Denes: onPreExecute(), never used.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

33

33a

Note-se que a fun c ao doInBackground(), que corre numa thread que est a proibida de manipular elementos do interface gr aco (e portanto proibida de chamar fun c oes que manipulem o interface gr aco), chama a fun c ao publishProgress(args ) que por sua vez, como que por milagre, faz automaticamente o espelho dessa chamada para a fun c ao onProgressUpdate(args ), sendo que os args s ao os mesmos, e esta fun c ao j a corre na thread do interface gr aco pelo que pode manipular elementos do interface gr aco. os aleat Cada vez que se prime o bot ao esta thread vai gerar 200 000 n orios no intervalo o [min..max] e calcular a m edia desses 200 000 ns. Note-se que antes de se premir o bot ao pode-se alterar o valor do m aximo e/ou do m nimo e s ao esses os valores que s ao utilizados nos c alculos. os aleat J a se se alterar um dos valores dos campos enquanto a gera c ao de n orios est a a ocorrer o novo valor n ao e usado, s o sendo usado na pr oxima vez que se premir o bot ao (que n ao pode ser premido enquanto os c alculos est ao a ser feitos j a que propositadamente por esta raz ao zemos o disable do bot ao no onPreExecute()). O progresso e apresentado em incrementos de 5%, isto e, 5%, 10%, 15%, ..., 90%, 95%, 100%. AsyncGenerator: protected String doInBackground(Integer... args) 33a (31a)
@Override // runs on its own thread protected Double doInBackground(Integer... args) { if (!ignorar) { int n = args[0] / 20; if (n == 0) n = 1; for (int k = 0; k < 20; ++k) { media = gerarNumeros(n, min, max, media); publishProgress(k / 20.0); } } return media; }
Denes: doInBackground(), used in chunk 25. Uses gerarNumeros() 30a.

33b

o que representa a ` medida que vai havendo progresso o texto do bot A ao vai mostrando um n percentagem do trabalho que j a se encontra terminado. Note-se que s o vale a pena atualizar o texto que aparece no bot ao enquanto o trabalho ainda n ao estiver todo completo, quando o trabalho termina a etiqueta de imediato volta a ter o texto original (que tinha sido guardado na vari avel savedLabel e que vai ser reposto pela fun c ao onPostExecute()), portanto ` a partida ningu em tem tempo de ver o texto a dizer 100% antes dele desaparecer. AsyncGenerator: protected void onProgressUpdate(Double... percentComplete) 33b (31a) @Override // runs on the GUI thread protected void onProgressUpdate(Double... percentComplete) { String theText; percentage = percentComplete[0]; theText = "" + (int)(percentage * 100) + "%"; b.setText(theText); }
Denes: onInitProgress(), never used. Uses onProgressUpdate() 25.

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

34

Computa c ao M ovel

34a

O trabalho que estava a ser feito em background foi dado por terminado pelo que s o falta repor o texto original da etiqueta do bot ao e colocar o valor da m edia no campo tMedia (que e o mesmo que o campo campoOutputMed da classe envolvente). AsyncGenerator: protected void onPostExecute(String msg) 34a (31a)
@Override // runs on the GUI thread protected void onPostExecute(Double d) { b.setText(savedLabel); b.setEnabled(true); tMedia.setText("" + d); }
Denes: onPostExecute(), never used.

34b

version/androidDevel/CicloDeVida-AndroidManifest.xml 34b <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="pt.ismai.cm2012" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="15" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:label="@string/title_activity_main" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
This code is written to le version/androidDevel/CicloDeVida-AndroidManifest.xml. Uses MainActivity 26.

34c

version/androidDevel/CicloDeVida-strings.xml 34c <resources> <string name="app_name">CicloDeVida</string> <string name="menu_settings">Settings</string> <string name="title_activity_main">MainActivity</string> <string name="etiquetaMedia">M edia</string> <string name="etiquetaMin">M nimo</string> <string name="etiquetaMax">M aximo</string> <string name="botaoGerar">Gerar</string> </resources>
This code is written to le version/androidDevel/CicloDeVida-strings.xml. Uses MainActivity 26.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

35

35

Usou-se um AbsoluteLayout de modo a facilitar a cria c ao do interface gr aco, no entanto um layout absoluto tem o defeito de que est a amarrado ao tamanho do ecr a. De qualquer forma como o objectivo desta aplica c ao era ver como trabalhar com threads que executavam em background, o layout utilizado pelo interface gr aco e pouco relevante. version/androidDevel/CicloDeVida-activity main.xml 35
<AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/AbsoluteLayout1" android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:id="@+id/textView2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_x="7dp" android:layout_y="12dp" android:text="@string/etiquetaMin" android:textAppearance="?android:attr/textAppearanceMedium" /> <EditText android:id="@+id/inputMinimo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_x="85dp" android:layout_y="13dp" android:ems="10" android:inputType="number" /> <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_x="12dp" android:layout_y="88dp" android:text="@string/etiquetaMax" /> <EditText android:id="@+id/inputMaximo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_x="77dp" android:layout_y="83dp" android:ems="10" android:inputType="number" /> <TextView android:id="@+id/outputMedia" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_x="92dp" android:layout_y="148dp" android:text="0.00" /> <TextView android:id="@+id/textView3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_x="12dp" android:layout_y="154dp" android:text="@string/etiquetaMedia" /> <Button android:id="@+id/botaoGerar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_x="106dp" android:layout_y="222dp" android:text="@string/botaoGerar" /> </AbsoluteLayout>
This code is written to le version/androidDevel/CicloDeVida-activity main.xml.

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

36

Computa c ao M ovel

1.6

Bases de Dados

36

O programa adb (Android Debug Bridge) faz parte do SDK do Android. Em Windows esse programa encontra-se em c:\Program Files (x86)\Android\android-sdk\platform-tools. Pode ser usado para executar remotamente uma shell na variante do sistema operativo Linux utilizada pelo Android. As bases de dados Sqlite3 s ao mantidas em subdirect orios de /data/data. Somente a partir de SQlite 3.6.19 e que o sqlite3 passou a suportar chaves estrangeiras (isto e a fazer verica c oes de integridade de chaves estrangeiras), no entanto por omiss ao essa funcionalidade est a desactivada podendo ser activada usando um pragma (isto e, uma instru c ao SQL que representa uma extens ao ao SQL standard suportado por SQLite3). Portanto s o a partir do Android 2.2 froyo, que actualiza o Sqlite 3.5.9 para o SQLite 3.6.22, e que a vers ao do SQLite3 que vem com a plataforma suporta chaves estrangeiras. Para activar essa funcionalidade usar pragma FOREIGN_KEYS = 1. Para funcionar com o uso de um cursor para obter os dados de uma query do tipo select, a chave prim aria das tabelas tem de obrigatoriamente ter o nome _id [Mednieks et al. 2011, p ag. 257]. sqlite3: criar a base de dados manualmente 36 (37a)
adb shell cd /data/data mkdir pt.ismai.cm2011.app1 cd pt.ismai.cm2011.app1 mkdir databases cd databases sqlite3 theScores.db pragma FOREIGN_KEYS; /* valor actual da configura c~ ao de chaves estrangeiras */ pragma FOREIGN_KEYS = 1; /* necessita de Android 2.2 ou superior */ pragma FOREIGN_KEYS; CREATE TABLE scores(_id INTEGER PRIMARY KEY AUTOINCREMENT, nPretas INTEGER, nBrancas INTEGER); CREATE TABLE players(_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, fk_score_id INTEGER NOT NULL CONSTRAINT score_id REFERENCES scores(_id) ON DELETE CASCADE ); /* CREATE TRIGGER fk_insert_player BEFORE INSERT ON players FOR EACH ROW BEGIN SELECT RAISE(ROLLBACK, insert na tabela "players" violou chave estrangeira "fk_score_id") WHERE (select _id from scores where _id = NEW.fk_score_id) IS NULL; END; */ /* falta trigger para update e trigger para delete */ insert into scores(_id, nPretas, nBrancas) values(1, 2, 0); insert into scores(_id, nPretas, nBrancas) values(2, 3, 1); insert into scores(_id, nPretas, nBrancas) values(3, 2, 2); insert into players(_id, name, fk_score_id) values(1, "avs", 1); insert into players(_id, name, fk_score_id) values(2, "xyz", 2); insert into players(_id, name, fk_score_id) values(3, "avs", 3); insert into players(_id, name, fk_score_id) values(4, "avs", 7); /* erro! */ select * from scores; select name, nPretas, nBrancas from scores, players where players.fk_score_id = scores._id and nBrancas > 0;

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

37

37a

version/androidDevel/criaBaseDadosManualmente.txt 37a sqlite3: criar a base de dados manualmente 36


This code is written to le version/androidDevel/criaBaseDadosManualmente.txt.

37b

Sqlite3: strings com os comandos SQL para criar as tabelas 37b (37c 39) String pragmaForeignKeys = "pragma FOREIGN_KEYS = 1"; String recreateTableScores = "DROP TABLE if exists scores"; String createScoresTable = "CREATE TABLE scores(" + "_id INTEGER PRIMARY KEY AUTOINCREMENT, " + "nPretas INTEGER, nBrancas INTEGER)"; String recreateTablePlayers = "DROP TABLE if exists players"; String createPlayersTable = "CREATE TABLE players(" + "_id INTEGER PRIMARY KEY AUTOINCREMENT, " + "name TEXT, " + "fk_score_id INTEGER NOT NULL CONSTRAINT score_id REFERENCES scores(_id) ON"+ " DELETE CASCADE)"; String trigger1 = "CREATE TRIGGER fk_insert_player BEFORE INSERT ON players " + "FOR EACH ROW " + "BEGIN " + "SELECT RAISE(ROLLBACK, " + "insert na tabela \"players\" violou chave estrangeira \"fk_score_id\") " + "WHERE (select _id from scores where _id = NEW.fk_score_id) IS NULL; " + "END"; Sqlite3: strings com os comandos SQL 37c (38a) Sqlite3: strings com os comandos SQL para criar as tabelas 37b String insert1 = "insert into scores(_id, nPretas, nBrancas) values(1, 2, 0)"; String insert2 = "insert into scores(_id, nPretas, nBrancas) values(2, 3, 1)"; String insert3 = "insert into scores(_id, nPretas, nBrancas) values(3, 2, 2)"; String insert4 = "insert into players(_id, name, fk_score_id) values(1, \"avs\", String insert5 = "insert into players(_id, name, fk_score_id) values(2, \"xyz\", String insert6 = "insert into players(_id, name, fk_score_id) values(3, \"avs\", String insert7 = "insert into players(_id, name, fk_score_id) values(4, \"avs\",

37c

1)"; 2)"; 3)"; 7)"; // erro!

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

38

Computa c ao M ovel

38a

Pode-se obter o mesmo resultado a partir de um programa no Android. sqlite3: criar a base de dados programaticamente 38a (38b)
import android.database.sqlite.SQLiteDatabase; // ... protected SQLiteDatabase db; // ... db = openOrCreateDatabase("theScores.db", SQLiteDatabase.CREATE_IF_NECESSARY, null); db.setLocale(Locale.getDefault()); db.setLockingEnabled(true); db.setVersion(1); Sqlite3: strings com os comandos SQL 37c db.execSQL(pragmaForeignKeys); db.execSQL(recreateTableScores); db.execSQL(createScoresTable); db.execSQL(recreateTablePlayers); db.execSQL(createPlayersTable); db.execSQL(insert1); db.execSQL(insert2); db.execSQL(insert3); db.execSQL(insert4); db.execSQL(insert5); db.execSQL(insert6); db.execSQL(insert7); // tem de dar erro // db.query("players", null, null, null, null, null, null); // db.query(tableName, projection, where, whereArgs, null, null, sortOrder); version/androidDevel/criaBaseDadosProgramaticamente.txt 38b sqlite3: criar a base de dados programaticamente 38a
This code is written to le version/androidDevel/criaBaseDadosProgramaticamente.txt.

38b

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

39

39

version/androidDevel/ScoresDatabase.java 39 private static int DATABASE_VERSION = 1; public class ScoresDatabase extends SQLiteOpenHelper { Sqlite3: strings com os comandos SQL para criar as tabelas 37b private ScoresDatabase(Context context, String nome, SQLiteDatabase.CursorFactory factory) { super(context, nome, factory, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase sqLiteDatabase) { createTable(sqLiteDatabase); } private void createTable(SQLiteDatabase db) { db.execSQL(pragmaForeignKeys); db.execSQL(createScoresTable); db.execSQL(createPlayersTable); } @override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL(pragmaForeignKeys); db.execSQL(recreateTableScores); db.execSQL(createScoresTable); db.execSQL(recreateTablePlayers); db.execSQL(createPlayersTable); } public ScoresCursor obterScores(ScoresCursor.SortBy ordenarPor) { String sql = ScoresCursor.QUERY + ordenarPor.toString(); SQLiteDatabase d = getReadableDatabase(); ScoresCursor c = (ScoresCursor)d.rawQueryWithFactory( new ScoresCursor.Factory(), sql, null, null); c.moveToFirst(); return c; } public static class ScoresCursor extends SQLiteCursor { public static enum SortBy { nPretas, nBrancas } private static final String Query = "select scores._id, nPretas, nBrancas" + "from scores" "order by "; private ScoresCursor(SQLiteDatabase db, SQLiteCursorDriver driver, String editTable, SQLiteQuery query) { super(db, driver, editTable, query); } private static class Factory implements SQLiteDatabase.CursorFactory { @Override public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver driver, String editTable, SQLiteQuery query) { return new ScoresCursor(db, driver, editTable, query); } } public long getColScoresId() { return getLong(getColumnIndexOrThrow("scores._id")); } }
This code is written to le version/androidDevel/ScoresDatabase.java. Uses onCreate() 27.

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

40

Computa c ao M ovel

40

O objecto cursor associado ao resultado de uma query tem de ser manipulado com cautela de modo a evitar que cursores mal manipulados comecem a consumir um excesso de recursos que acabam por dar cabo da aplica c ao ou afectar o sistema operativo. Quando a aplica c ao entra em pausa ou termina, o cursor tem de ser desactivado com uma chamada a deactivate(), quando a aplica c ao volta a estar activa o cursor deve ser actualizado usando requery(), quando j a n ao e necess ario tem de se chamar close() para libertar os recursos. Isto e, devem ser efectuadas estas chamadas no ambito do cilco de vida da aplica c ao: onPause(), onResume(), onDestroy(). Outra hip otese bastante mais simples e usar startManagingCursor(c) para que seja a framework do Android a tratar destes detalhes (em casos especiais mais tarde pode-se sempre chamar stopManagingCursor(c) para obter de volta a gest ao do cursor) [Darcey e Conder 2011, p ag. 245]. SQLite3: query usando QueryBuilder 40 (42c)
// query simples // Cursor c; // c = db.query(tableName, projection, where, whereArgs, null, null, sortOrder); // query usando QueryBuilder import android.database.sqlite.SQLiteQueryBuilder; // ... SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder(); queryBuilder.setTables("contactos, locais"); queryBuilder.appendWhere("contactos.local_id = locais._id"); String devolverColunas[] = { "nome", "telefone", "localidade" }; String ordenarPor = "nome ASC"; Cursor c = queryBuilder.query(db, devolverColunas, null, null, null, null, ordenarPor); c.close(); // o cursor tem de ser fechado quando n~ ao for mais necess ario

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

41

41a

Esta classe mostra uma maneira simples de lidar com um cursor, obviamente que o cursor possui bastantes outros m etodos. SQLite3: classe MeuCursor 41a (42c)
// classe auxiliar para trabalhar com o cursor public class MeuCursor { protected Cursor c; int nResultados; int nColunas; public MeuCursor(Cursor oCursor) { c = oCursor; nResultados = c.getCount(); nColunas = c.getColumnCount(); c.moveToFirst(); } public String[] obterNomesCampos() { String[] nomesCampos = new String[nColunas]; for (int k = 0; k < nColunas; ++k) nomesCampos[k] = c.getColumnName(k); return nomesCampos; } public String[] obterProximoRegisto() { if (c.isAfterLast()) return null; String[] campos = new String[nColunas]; for (int k = 0; k < nColunas; ++k) campos[k] = c.getString(k); //funciona mesmo que campo n~ ao seja string c.moveToNext(); return campos; } }
Denes: MeuCursor, never used.

41b

SQLite3: strings base de dados nome, telefone, localidade 41b (42c) // strings com esquema da base de dados String pragmaForeignKeys = "pragma FOREIGN_KEYS = 1"; String recreateTableLocais = "DROP TABLE if exists locais"; String createLocaisTable = "CREATE TABLE locais(" + "_id INTEGER PRIMARY KEY AUTOINCREMENT, " + "localidade TEXT)"; String recreateTableContactos = "DROP TABLE if exists contactos"; String createContactosTable = "CREATE TABLE contactos(" + "_id INTEGER PRIMARY KEY AUTOINCREMENT, " + "nome TEXT, telefone TEXT, " + "fk_local_id INTEGER NOT NULL CONSTRAINT locais_id REFERENCES locais(_id) ON"+ " DELETE CASCADE)"; String trigger1 = "CREATE TRIGGER fk_insert_contacto BEFORE INSERT ON contactos " + "FOR EACH ROW " + "BEGIN " + "SELECT RAISE(ROLLBACK, " + "insert na tabela \"contactos\" violou chave estrangeira \"fk_local_id\") " + "WHERE (select _id from locais where _id = NEW.fk_locais_id) IS NULL; " + "END";

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

42

Computa c ao M ovel

42a

SQLite3: criar base de dados nome, telefone, localidade 42a

(42c)

// abrir base de dados import android.database.sqlite.SQLiteDatabase; // ... protected SQLiteDatabase db; // ... db = openOrCreateDatabase("theScores.db", SQLiteDatabase.CREATE_IF_NECESSARY, null); db.setLocale(Locale.getDefault()); db.setLockingEnabled(true); db.setVersion(1); // criar base de dados db.execSQL(pragmaForeignKeys); db.execSQL(recreateTableLocais); db.execSQL(createLocaisTable); db.execSQL(recreateTableContactos); db.execSQL(createContactosTable); SQLite3: inserir nome, telefone, localidade 42b

42b

(42c)

// inserir dados ++contador; db.execSQL("insert into locais(_id, localidade) values(" + contador + ", " + aLocalidade + ")"); db.execSQL("insert into contactos(nome, telefone, fk_local_id) values(" + oNome + ", " + oTelefone + ", " + contador + ")"); version/androidDevel/exemploQueryComCursor.txt 42c SQLite3: strings base de dados nome, telefone, localidade 41b SQLite3: criar base de dados nome, telefone, localidade 42a SQLite3: inserir nome, telefone, localidade 42b SQLite3: query usando QueryBuilder 40 SQLite3: classe MeuCursor 41a
This code is written to le version/androidDevel/exemploQueryComCursor.txt.

42c

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

43

1. Usar ListView widget (Composite / ListView)

1.7

Ler e Escrever Ficheiros

Estes exemplos mostram como escrever dados para a mem oria interna do telem ovel (internal storage ). Note-se que cheiros na mem oria interna s o podem ser acedidos pela aplica c ao, n ao existe acesso directo pelo utilizador ou acesso por outras aplica c oes, e os cheiros s ao automaticamente apagados quando a aplica c ao e desinstalada. 1. No que se segue assume-se que o package da aplica c ao e pt.ismai.cm2013 e que o nome da aplica c ao e Test1 2. Para criar um cheiro chamado osMeusDados.txt e colocar l a a mensagem Hello World. usar escrever para o cheiro osMeusDados.txt 43
import java.io.FileOutputStream; import java.io.IOException; public class ExemploActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); String filename = "osMeusDados.txt"; String msg = "Hello World."; try { FileOutputStream f = openFileOutput(filename, Context.MODE_PRIVATE); f.write(msg.getBytes()); f.close(); } catch (IOException e) { e.printStackTrace(); } } }
Uses onCreate() 27.

43

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

44

Computa c ao M ovel

3. Pode-se tamb em usar openFileOutput(filename, Context.MODE_PRIVATE | Context.MODE_APPEND) para evitar que ao abrir o cheiro para output o seu conte udo (se j a existir) seja apagado 4. Compilar e executar o programa 5. Seleccionar Window / Open Perspective / DDMS, de modo a obter o Dalvik Debug Monitor Server, e no topo ` a direita seleccionar o tab File Explorer. Seguidamente ver o conte udo de /data/data/pt.ismai.cm2013/files, deve ter o cheiro osMeusDados.txt 6. Para copiar o cheiro para o PC usar adb pull /data/data/pt.ismai.cm2013/files/osMeusDados.txt osMeusDados-remotos.txt 7. Para ler o conte udo do cheiro usar algo de semelhante a: ler do cheiro osMeusDados.txt 44
public class InternalStorageExample2 extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); String fileName = "osMeusDados.txt"; try { FileInputStream f = openFileInput(fileName); InputStreamReader isr = new InputStreamReader(f); StringBuilder sb = new StringBuilder(); char[] inputBuffer = new char[2048]; int k; while ((k = isr.read(inputBuffer)) != -1) { sb.append(inputBuffer, 0, k); } String readString = sb.toString(); Log.i("LOG_TAG", "String lido: " + readString); // deleteFile(fileName); } catch (IOException e) { e.printStackTrace(); } } }
Uses onCreate() 27.

44

1.8
1.8.1

GUI widgets
ListView, Spinner, GridView

1. O widget ListView29 representa uma list box, enquanto que o Spinner30 representa uma drop-down list. O GridView31 permite fazer uma selec c ao numa matriz bidimensional [Murphy 2012, p ag. 167] 2. Todas estas classes (e a ExpandableListView apresentada na Sec c ao 1.8.2) herdam de uma classe comum, o AdapterView 3. Um adapter fornece um interface comum para um conjunto diverso de APIs ou de origens de dados (adapta os dados ao componente gr aco em quest ao), recorrendo ` a implementa c ao de interfaces (class x implements y )
29 Que 30 Que

se encontra na palete Composite do editor gr aco do Eclipse se encontra na palete Forms do editor gr aco do Eclipse 31 Que se encontra na palete Forms do editor gr aco do Eclipse

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

45

4. O exemplo mais simples e o ArrayAdapter que pode ser colocado ` a volta de um array ou de um java.util.List. Dado que um ListView representa um conjunto de linhas que aparecem todas ao mesmo tempo no ecr a faz sentido que seja usado no contexto de uma Activity que s o contenha essa ListView. Para esse efeito existe o ListActivity, que e uma activity que por omiss ao inclui um ListView como sendo o u nico elemento que aparece no layout. O simple_list_item_1 serve para controlar o aspecto das linhas, neste caso representa usar o formato standard do Android, com um tipo de letra grande, muito espa co em branco, texto em branco. criar um ListView 45
package pt.ismai.cm2012; import import import import import import android.os.Bundle; android.widget.ListView; android.widget.ArrayAdapter; android.view.View; android.app.ListActivity; android.widget.Toast;

45

public class MainActivity extends ListActivity { private String[] osItensDaLista = {"um", "dois", "tr^ es", "quatro", "cinco", "seis", "sete", "oito", "nove", "dez"}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, osItensDaLista); setListAdapter(adapter); } @Override public void onListItemClick(ListView parent, View v, int position, long id) { Toast.makeText(this, osItensDaLista[position], Toast.LENGTH_SHORT).show(); } }
Uses MainActivity 26 and onCreate() 27.

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

46

Computa c ao M ovel

46

Quando se usa o ListActivity e obrigat orio colocar como id no XML android:id="@android:id/list" (por exemplo android:id="@+id/listView1" n ao pode ser usado), se isso n ao for feito o programa ao executar origina uma excep c ao dizendo java.lang.RuntimeException: Unable to start activity ...: java.lang.RuntimeException: Your content must have a ListView whose id attribute is android.R.id.list. layout XML do ListView 46
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" > <ListView android:id="@android:id/list" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="100dp" android:layout_marginLeft="51dp" > </ListView> </RelativeLayout>
Uses MainActivity 26.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

47

47

No caso do Spinner j a faz sentido que ele seja somente um entre v arios componentes gr acos, por isso recorremos a um Activity e adicionamos esse widget. criar um spinner 47
package pt.ismai.cm2012; import import import import import import import import android.os.Bundle; android.app.Activity; android.widget.ArrayAdapter; android.widget.Spinner; android.widget.TextView; android.widget.AdapterView; android.widget.AdapterView.OnItemSelectedListener; android.view.View;

public class MainActivity extends Activity { private Spinner oSpin; // assume-se que o ID do componente e spinner1 private TextView seleccionado; // s o para mostrar o que foi seleccionado private String[] osItensDaLista ={"um", "dois", "tr^ es", "quatro", "cinco"}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); oSpin = (Spinner)findViewById(R.id.spinner1); seleccionado = (TextView)findViewById(R.id.textView1); ArrayAdapter<String> adapter1 = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, osItensDaLista); oSpin.setAdapter(adapter1); oSpin.setOnItemSelectedListener(new OnItemSelectedListener() { // @Override FIXME bug fix,poder usar ou n~ ao depende da vers~ ao do SDK? public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) { seleccionado.setText(osItensDaLista[position]); } // @Override FIXME bug fix,poder usar ou n~ ao depende da vers~ ao do SDK? public void onNothingSelected(AdapterView<?> parentView) { seleccionado.setText("?"); } }); } }
Uses MainActivity 26 and onCreate() 27.

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

48

Computa c ao M ovel

FIXME GridView

1.8.2
FIXME

ExpandableListView ( arvore com 2 n veis)

1.8.3
FIXME
48

ActionBar (tabs)

XML layout inater 48 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View result; if (getArguments().getInt(ARG_SECTION_NUMBER) == 2) result=inflater.inflate(R.layout.tab2, container, false); else if (getArguments().getInt(ARG_SECTION_NUMBER) == 1) result=inflater.inflate(R.layout.tab1, container, false); else result=inflater.inflate(R.layout.tab3, container, false); return result; }

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

49

1.8.4

Criar activities com sub-activities

49

Exemplo de uma activity simples chamada MulMainActivity que cont em um bot ao que lhe permite chamar o sub-activity existente no cheiro SubActivity1.java, sendo que esse SubActivity1 quando termina devolve o controlo ao MulMainActivity, podendo-se portanto saltar de um activity para outro e voltar ao ponto inicial tantas vezes quantas se quiser. Para evitar problemas com o contexto do this por o on click listener ser uma classe an onima (logo o this iria referir-se ` a classe an onima e n ao ao MulMainActivity), criou-se uma fun c ao separada chamada executarOutraActivity() que e chamada tomando como argumento o nome da classe a executar (note-se que o nome n ao e dado como uma String, tem de se dar o nome da classe sem se colocar aspas, isto e, o argumento e do tipo classe). version/androidDevel/MulMainActivity.java 49
package pt.ismai.cm2012; import android.os.Bundle; import android.app.Activity; import android.view.View; import android.content.Intent; import android.widget.Button; public class MulMainActivity extends Activity { protected Button button1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button1 =(Button)findViewById(R.id.button1); button1.setOnClickListener(new Button.OnClickListener() { public void onClick(View v) { executarOutraActivity(SubActivity1.class); } }); } private void executarOutraActivity(Class<?> subActividade) { Intent x = new Intent(this, subActividade); startActivity(x); } }
This code is written to le version/androidDevel/MulMainActivity.java. Uses onCreate() 27 and setOnClickListener().

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

50

Computa c ao M ovel

50a

Note-se que ao se executar finish() se fecha o activity e se devolve o controle ao activity que o chamou. version/androidDevel/SubActivity1.java 50a
package pt.ismai.cm2012; import import import import import import android.os.Bundle; android.app.Activity; android.view.View; android.content.Intent; android.widget.Button; android.widget.EditText;

public class SubActivity1 extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.subactivity_main); final EditText campo1 = (EditText)findViewById(R.id.editText1); final EditText campo2 = (EditText)findViewById(R.id.editText2); final Button finished = (Button)findViewById(R.id.button2); finished.setOnClickListener(new Button.OnClickListener() { public void onClick(View v) { finish(); } }); } }
This code is written to le version/androidDevel/SubActivity1.java. Uses onCreate() 27 and setOnClickListener().

50b

version/androidDevel/mul actvity main.xml 50b <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" > <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:layout_marginTop="124dp" android:text="Button" /> </RelativeLayout>
This code is written to le version/androidDevel/mul actvity main.xml. Uses MainActivity 26.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

51

51

version/androidDevel/mul subactity main.xml 51 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <EditText android:id="@+id/editText1" android:layout_width="match_parent" android:layout_height="wrap_content" android:ems="10" > <requestFocus /> </EditText> <EditText android:id="@+id/editText2" android:layout_width="match_parent" android:layout_height="wrap_content" android:ems="10" android:inputType="number" /> <Button android:id="@+id/button2" style="?android:attr/buttonStyleSmall" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Terminate" /> </LinearLayout>
This code is written to le version/androidDevel/mul subactity main.xml.

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

52

Computa c ao M ovel

52a

necess Por m eis o AndroidManifest.xml deste programa. E ario registar uma ac c ao por omiss ao (a <action>) para esta activity, sendo importante que no activity android:name apare ca ".SubActivity1" [Steele e To 2011, p ag. 37]. version/androidDevel/GaleriaWidgets-AndroidManifest.xml 52a
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="pt.ismai.cm2012" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="16" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="pt.ismai.cm2012.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".SubActivity1" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.VIEW"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity> </application> </manifest>
This code is written to le version/androidDevel/GaleriaWidgets-AndroidManifest.xml. Uses MainActivity 26.

1.9

Escrever e ler de um Ficheiro de Texto

52b

Se no Android se pretender escrever ou ler algo de um cheiro de texto muito provavelmente pretende-se escrever para a placa de mem oria de armazenamento externo. Para que o programa tenha permiss ao para escrever na mem oria externa e necess ario adicionar ao m do AndroidManifest.xml o seguinte: no AndroidManifest.xml dar permiss oes para escrever na mem oria externa 52b
... </application> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> </manifest>

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

53

53

A classe FicheiroDeTexto implementa a funcionalidade b asica para manipular um cheiro de texto. Suporta diversos modos de abertura do cheiro (no construtor), escrever texto (uma ou mais linhas), ler uma linha de texto, ler desde o ponto onde se encontre at e ao m do cheiro, e fechar o cheiro. Os erros se existirem s ao escritos para o LogCat e s ao igualmente mantidos internamente pelo que podem ser obtidos por uma chamada a obterMensagemErro(). version/androidDevel/FicheiroDeTexto.java 53
package pt.ismai.cm2012; import import import import import import import import java.io.BufferedReader; java.io.BufferedWriter; java.io.File; java.io.FileReader; java.io.FileWriter; java.io.IOException; android.util.Log; android.os.Environment;

public class FicheiroDeTexto { boolean append; int modo; String filename; String errorMsg; String errorTag; BufferedReader br; BufferedWriter bw; FicheiroDeTexto: public FicheiroDeTexto(String errorTag, String lename, int modo) 54a FicheiroDeTexto: public boolean escreverTexto(String osDados) 55b FicheiroDeTexto: public String lerUmaLinha() 56a FicheiroDeTexto: public String lerAteAoFim() 56b FicheiroDeTexto: public boolean fechar() 56c FicheiroDeTexto: public void escreverErroNoLog() 57b FicheiroDeTexto: public String obterMensagemErro() 57a }
This code is written to le version/androidDevel/FicheiroDeTexto.java. Denes: FicheiroDeTexto, used in chunks 54a and 58.

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

54

Computa c ao M ovel

54a

No construtor explicita-se a tag a usar nas mensagens de erro, o nome do cheiro, e o modo de abertura do cheiro. A abertura do cheiro suporta 3 modos de opera c ao: 0) readonly; 1) s o para escrita, escrevendo por cima do que j a existia, isto e, truncando o cheiro antes de come car a escrever; 2) escrita em modo append. Falta implementar o suporte para abrir um cheiro no modo leitura e escrita (n ao foi feito por ser menos interessante no contexto do Android). Note-se que foi propositado o uso de um switch em que um dos case n ao tem break. FicheiroDeTexto: public FicheiroDeTexto(String errorTag, String lename, int modo) 54a (53)
public FicheiroDeTexto(String errorTag, String filename, int modo) { this.errorTag = errorTag; this.filename = filename; this.modo = modo; br = null; bw = null; File rootDir = Environment.getExternalStorageDirectory(); File f = new File(rootDir, filename); append = false; switch (modo) { case 0: // READONLY FicheiroDeTexto: abrir s o para leitura 54b break; case 2: // APPEND, tem de estar aqui antes do WRITEONLY append = true; // break; // de prop osito n~ ao tem break de modo a continuar pelo // "case 1:" abaixo case 1: // WRITEONLY FicheiroDeTexto: abrir para escrita (quer seja append quer n ao) 55a break; default: errorMsg = "Erro ao abrir " + filename + " no modo " + modo + ", tem de ser 0, 1 ou 2"; escreverErroNoLog(); break; } }
Denes: FicheiroDeTexto(), used in chunk 58b. Uses escreverErroNoLog() 57b and FicheiroDeTexto 53.

54b

FicheiroDeTexto: abrir s o para leitura 54b try { if (rootDir.canRead()) { FileReader filereader = new FileReader(f); br = new BufferedReader(filereader, 8 * 1024); } else { errorMsg = "Erro de permiss~ oes ao abrir para leitura " + filename + ""; escreverErroNoLog(); } } catch (IOException e) { errorMsg = "Erro ao abrir p/ leitura " + filename + ": " + e.getMessage(); escreverErroNoLog(); }
Uses escreverErroNoLog() 57b.

(54a)

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

55

55a

FicheiroDeTexto: abrir para escrita (quer seja append quer n ao) 55a try { if (rootDir.canWrite()) { FileWriter filewriter = new FileWriter(f, append); bw = new BufferedWriter(filewriter, 8 * 1024); } else { errorMsg = "Erro de permiss~ oes ao abrir para escrita " + filename + ""; escreverErroNoLog(); } } catch (IOException e) { errorMsg = "Erro ao abrir p/ escrita " + filename + ": " + e.getMessage(); escreverErroNoLog(); }
Uses escreverErroNoLog() 57b.

(54a)

55b

Escreve verbatim o texto que lhe for fornecido, qualquer que ele seja (isto e, pode consistir numa ou mais linhas). Em caso de erro devolve false. FicheiroDeTexto: public boolean escreverTexto(String osDados) 55b (53)
public boolean escreverTexto(String osDados) { try { if (bw != null) bw.write(osDados); else { errorMsg = "Erro, o ficheiro " + filename + " n~ ao estava aberto para escrita"; escreverErroNoLog(); return false; } } catch (IOException e) { errorMsg = "Erro ao escrever em " + filename + ": " + e.getMessage(); escreverErroNoLog(); return false; } return true; }
Denes: escreverTexto(), used in chunk 58b. Uses escreverErroNoLog() 57b.

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

56

Computa c ao M ovel

56a

L e uma linha de texto, isto e, uma linha terminada por \n (line feed), ou por \r (carriage return) ou por \r\n (carriage return seguido de line feed). O texto devolvido inclui um \n line feed. Em caso de erro devolve um string nulo. FicheiroDeTexto: public String lerUmaLinha() 56a (53)
public String lerUmaLinha() { String s = null; try { if (br != null) s = br.readLine(); else { errorMsg = "Erro, o ficheiro " + filename + " n~ ao estava aberto para leitura"; escreverErroNoLog(); return null; } } catch (IOException e) { errorMsg = "Erro ao ler de " + filename + ": " + e.getMessage(); escreverErroNoLog(); return null; } return s + "\n"; }
Denes: lerUmaLinha(), never used. Uses escreverErroNoLog() 57b.

56b

L e o cheiro na sua totalidade desde o ponto onde se encontrava at e ao m. Utiliza um StringBuffer em vez de um String de modo a ser mais eciente a ler o cheiro j a que evita estar sempre a concatenar Strings. Em caso de erro devolve um string nulo. FicheiroDeTexto: public String lerAteAoFim() 56b (53)
public String lerAteAoFim() { StringBuffer sb = new StringBuffer(4096); try { if (br != null) { String s; while ((s = br.readLine()) != null) { sb.append(s); sb.append("\n"); } return sb.substring(0); } else { errorMsg = "Erro, o ficheiro " + filename + " n~ ao estava aberto para leitura"; escreverErroNoLog(); return null; } } catch (IOException e) { errorMsg = "Erro ao ler de " + filename + ": " + e.getMessage(); escreverErroNoLog(); return null; } }
Denes: lerAteAoFim(), used in chunk 58b. Uses escreverErroNoLog() 57b.

Fecha o cheiro, esvaziando os buers se for caso disso. Em caso de erro devolve false.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

57

56c

FicheiroDeTexto: public boolean fechar() 56c (53) public boolean fechar() { try { if (bw != null) bw.close(); else if (br != null) br.close(); } catch (IOException e) { errorMsg = "Erro ao fechar " + filename + ": " + e.getMessage(); escreverErroNoLog(); return false; } return true; }
Denes: fechar(), never used. Uses escreverErroNoLog() 57b.

57a

Uma chamada a esta fun c ao permite obter o texto da u ltima mensagem de erro (para permitir o seu uso na pr opria aplica c ao, por exemplo num Toast, j a que esse mesmo texto j a foi escrito para o LogCat). FicheiroDeTexto: public String obterMensagemErro() 57a (53)
public String obterMensagemErro() { return errorMsg; }
Denes: obterMensagemErro(), never used.

57b

Dado que a escrita de todas as mensagens de erro para o log est a colocada aqui, quem pretender usar outra forma de log (ou at e nenhuma) pode simplesmente redenir esta fun c ao (fazer uma subclasse que redene esta fun c ao, idealmente marcando-a como @Override de modo a garantir que est a mesmo a substituir esta fun c ao). FicheiroDeTexto: public void escreverErroNoLog() 57b (53)
public void escreverErroNoLog() { Log.e(errorTag, errorMsg); }
Denes: escreverErroNoLog(), used in chunks 5456 and 58a.

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

58

Computa c ao M ovel

58a

Eis um exemplo de como se podia criar uma subclasse desta classe que em vez de escrever para o LogCat usa um Toast para fazer aparecer a mensagem de erro no ecr a. Basta adicionar um novo atributo para armazenar o contexto do Toast, criar um construtor sem o par ametro do errorTag (por n ao ser necess ario) e com o par ametro extra que d a o contexto para o Toast, depois no construtor chama-se o construtor da superclasse usando super(...) e inicializa-se o atributo extra. Para al em disso a u nica coisa que se faz e redenir a fun c ao escreverErroNoLog() de modo a usar um Toast. version/androidDevel/FicheiroDeTextoSemLogComToast.java 58a
import android.widget.Toast; import android.content.Context; public class FicheiroDeTextoSemLogComToast extends FicheiroDeTexto { Context toastContext; public FicheiroDeTextoSemLogComToast(String filename, int modo, Context c) { super(null, filename, modo); toastContext = c; } @Override public void escreverErroNoLog() { Toast.makeText(toastContext, errorMsg, Toast.LENGTH_SHORT).show(); } }
This code is written to le version/androidDevel/FicheiroDeTextoSemLogComToast.java. Denes: FicheiroDeTextoSemLogComToast, used in chunk 58b. Uses escreverErroNoLog() 57b and FicheiroDeTexto 53.

58b

Isto signica que agora se propositadamente usarmos mal as duas classes, na primeira abrindo para leitura e tentando escrever, e na segunda abrindo para escrita e tentando ler, a mensagem de erro da primeira vai para o LogCat e a mensagem de erro da segunda aparece num Toast. Note-se que devido ao polimorsmo, embora a vari avel f seja do tipo FicheiroDeTexto ela pode ser inicializada com um FicheiroDeTexto ou com um FicheiroDeTextoSemLogComToast (por ser uma subclasse), e devido ao suporte para polimorsmo o m etodo chamado quando d a erro e o correcto. exemplos do uso do FicheiroDeTexto e FicheiroDeTextoSemLogComToast 58b
... // por exemplo no contexto dum Activity FicheiroDeTexto f = new FicheiroDeTexto("FICHEIRO", "oMeuFicheiro.txt", 0); f = new FicheiroDeTexto("FICHEIRO", "oMeuFicheiro.txt", 0); f.escreverTexto("o meu texto 2\no meu texto 3"); f = new FicheiroDeTextoSemLogComToast("oMeuFicheiro.txt", 1, this); String s = f.lerAteAoFim(); ...
Uses escreverTexto() 55b, FicheiroDeTexto 53, FicheiroDeTexto() 54a, FicheiroDeTextoSemLogComToast 58a, and lerAteAoFim() 56b.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

59

1.10

Aplica c ao de Manuten c ao

Pretende-se criar uma aplica c ao Android de manuten c ao de avarias que possua as seguintes funcionalidades: 1. Permita a introdu c ao de dados via um interface gr aco 2. Tenha mais do que um ecr a (isto e, necessite de usar mais do que um activity) e possua um Help About que despoleta um Toast 3. Armazene os dados numa base de dados, executando opera c oes de insert, update, select da base de dados 4. Necessite de executar tarefas em background 5. Via HTTP fale com um servidor remoto e sincronize a base de dados local com a base de dados do servidor 6. Utilize tipos gen ericos e excep c oes 7. Eis o formato pretendido para a base de dados: tipoAvaria(id, nome) onde nome e escolhido pelo utilizador podendo ser por exemplo: Menor, Grave, Urgente, Cr tica cliente(id, nome, morada) statusAvaria(id, nome), onde o nome e um dos 4 valores xos: Registado, Sincronizado, A ser tratado, Resolvido avaria(id, cliente, tipo, status, observacao), onde somente observacao e texto livre, os restantes campos permitem escolher valores pr e-denidos pertencentes ` as restantes tabelas 8. O ecr a de topo deve ter 4 bot oes que permitem escolher entre: Avarias (registar uma nova avaria ou actualizar os dados de uma avaria j a existente), Clientes (adicionar um novo cliente ou actualizar os dados de um cliente j a existente), Tipos (ver os tipos existentes ou adicionar novos tipos de avarias), Sincronizar (ligar-se ao servidor remoto usando uma tarefa ass ncrona), a aplica c ao pode continuar a ser usada, quando acabar avisa que terminou 9. Minimum API 8 (Android 2.2 Froyo), target SDK (API 18, Android 4.3), compile with API 19 (Android 4.4 KitKat)
59

version/androidDevel/activity main.xml 59 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity" > <Button android:id="@+id/botaoTipos" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/botaoAvarias" android:layout_alignParentTop="true" android:layout_alignRight="@+id/botaoClientes"

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

60

Computa c ao M ovel

android:layout_marginTop="14dp" android:text="@string/textoTipos" /> <Button android:id="@+id/botaoSincronizar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="62dp" android:text="@string/textoSincronizar" /> <Button android:id="@+id/botaoAvarias" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@+id/botaoSincronizar" android:layout_alignLeft="@+id/botaoSincronizar" android:layout_alignRight="@+id/botaoSincronizar" android:layout_marginBottom="58dp" android:text="@string/textoAvarias" /> <Button android:id="@+id/botaoClientes" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/botaoSincronizar" android:layout_alignRight="@+id/botaoAvarias" android:layout_below="@+id/botaoTipos" android:layout_marginTop="35dp" android:text="@string/textoClientes" /> </RelativeLayout>
This code is written to le version/androidDevel/activity main.xml. Uses MainActivity 26.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

Computa c ao M ovel

61

61

database stu 61 public class DatabaseHelper extends SQLiteOpenHelper { private static final String DATABASE_NAME = "myDatabase.db"; private static final int SCHEMA = 1; static final String TITLE = "titulo"; static final String VALUE = "valor"; static final String TABLE = "constantes"; public DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, SCHEMA); } public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE constants (_id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, value REAL);"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { throw new RuntimeException("N~ ao devia acontecer: base de dados obsoleta"); } public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { throw new RuntimeException("N~ ao devia acontecer: base de dados demasiado nova"); } } db = new DatabaseHelper(getActivity().getApplicationContext()); db.getWriteableDatabase().rawQuery(...); Cursor c = db.getReadableDatabase().rawQuery("SELECT _id, name, value from dados", null); ... c.close()
Uses onCreate() 27.

1.11

Indice de Fragmentos de C odigo

Android: escrever para o log 18 18 Android: exemplo de um AndroidManifest.xml 14 14 Android: exemplo de um Service 13 13 Android: exemplo de uma Activity 12b 12b Android: ver os registos da base de dados de contactos 12a 12a AsyncGenerator: protected String doInBackground(Integer... args) 33a 31a, 33a AsyncGenerator: protected void onPostExecute(String msg) 34a 31a, 34a AsyncGenerator: protected void onPreExecute() 32 31a, 32 AsyncGenerator: protected void onProgressUpdate(Double... percentComplete) 33b 31a, 33b AsyncGenerator: public AsyncGenerator(View botao) 31b 31a, 31b AsyncTask: protected String doInBackground(String... args) 24b 23b, 24b AsyncTask: protected void onPostExecute(String msg) 24a 23b, 24a, 25 AsyncTask: protected void onPreExecute() 23d 23b, 23d, 25 AsyncTask: public AsyncInitGame(View dots, Drawable bg, Game game, TextView msg) 23c 23b, 23c, 25 AsyncTask: public void onCreate(Bundle state) 24c 23b, 24c CicloDeVida: MyAsyncInit() 31a 26, 31a CicloDeVida: onStart(), onResume(), onPause(), onStop(), onRestart(), onDestroy() 29a 26, 29a

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

62

Computa c ao M ovel

CicloDeVida: protected void restoreVarsFromBundle(Bundle savedInstanceState) 28b 26, 28b CicloDeVida: public void atualizarNumeros() 30b 26, 30b CicloDeVida: public void gerarNumeros(int n) 30a 26, 30a CicloDeVida: public void onCreate(Bundle savedInstanceState) 27 26, 27 CicloDeVida: public void onRestoreInstanceState(Bundle savedInstanceState) 29b 26, 29b CicloDeVida: public void onSaveInstanceState(Bundle outputState) 28a 26, 28a criar um ListView 45 45 criar um spinner 47 47 database stu 61 61 erros: erro se o Android SDK foi instalado num local com espa cos no nome 10 10 escrever para o cheiro osMeusDados.txt 43 43 exemplo: uso de AsyncTaskDemo com indicador de progresso 25 25 exemplo: uso de AsyncTaskDemo mas sem indicador de progresso 23b 23a, 23b exemplos do uso do FicheiroDeTexto e FicheiroDeTextoSemLogComToast 58b 58b FicheiroDeTexto: abrir para escrita (quer seja append quer n ao) 55a 54a, 55a FicheiroDeTexto: abrir s o para leitura 54b 54a, 54b FicheiroDeTexto: public boolean escreverTexto(String osDados) 55b 53, 55b FicheiroDeTexto: public boolean fechar() 56c 53, 56c FicheiroDeTexto: public FicheiroDeTexto(String errorTag, String lename, int modo) 54a 53, 54a FicheiroDeTexto: public String lerAteAoFim() 56b 53, 56b FicheiroDeTexto: public String lerUmaLinha() 56a 53, 56a FicheiroDeTexto: public String obterMensagemErro() 57a 53, 57a FicheiroDeTexto: public void escreverErroNoLog() 57b 53, 57b layout XML do ListView 46 46 ler do cheiro osMeusDados.txt 44 44 Mac OS X: instalar o Android SDK 7 7 no AndroidManifest.xml dar permiss oes para escrever na mem oria externa 52b 52b SQLite3: classe MeuCursor 41a 41a, 42c sqlite3: criar a base de dados manualmente 36 36, 37a sqlite3: criar a base de dados programaticamente 38a 38a, 38b SQLite3: criar base de dados nome, telefone, localidade 42a 42a, 42c SQLite3: inserir nome, telefone, localidade 42b 42b, 42c SQLite3: query usando QueryBuilder 40 40, 42c SQLite3: strings base de dados nome, telefone, localidade 41b 41b, 42c Sqlite3: strings com os comandos SQL 37c 37c, 38a Sqlite3: strings com os comandos SQL para criar as tabelas 37b 37b, 37c, 39 version/androidDevel/activity main.xml 59 59 version/androidDevel/CicloDeVida-activity main.xml 35 35 version/androidDevel/CicloDeVida-AndroidManifest.xml 34b 34b version/androidDevel/CicloDeVida-strings.xml 34c 34c version/androidDevel/criaBaseDadosManualmente.txt 37a 37a version/androidDevel/criaBaseDadosProgramaticamente.txt 38b 38b version/androidDevel/descontos/AndroidManifest.xml 20b 20b version/androidDevel/descontos/res/layout/main.xml 21 21 version/androidDevel/descontos/res/values/strings.xml 20a 20a version/androidDevel/descontos/src/pt/ismai/cm2011/DescontosActivity.java 19 19 version/androidDevel/exemploQueryComCursor.txt 42c 42c version/androidDevel/FicheiroDeTexto.java 53 53 version/androidDevel/FicheiroDeTextoSemLogComToast.java 58a 58a version/androidDevel/GaleriaWidgets-AndroidManifest.xml 52a 52a version/androidDevel/HelloNumaThread.java 11 11 version/androidDevel/helloWorld1/AndroidManifest.xml 15b 15b version/androidDevel/helloWorld2/AndroidManifest.xml 17 17
$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $ ISMAI

Computa c ao M ovel

63

version/androidDevel/helloWorld1/HelloAndroid1.java 15a 15a version/androidDevel/helloWorld2/res/layout/main.xml 16a 16a version/androidDevel/helloWorld2/res/values/strings.xml 16b 16b version/androidDevel/helloWorld2/src/pt/ismai/cm2011/HelloAndroid2.java version/androidDevel/lixo.txt 23a 23a version/androidDevel/MainActivity.java 26 26 version/androidDevel/mul actvity main.xml 50b 50b version/androidDevel/MulMainActivity.java 49 49 version/androidDevel/mul subactity main.xml 51 51 version/androidDevel/ScoresDatabase.java 39 39 version/androidDevel/SubActivity1.java 50a 50a XML layout inater 48 48

16c

16c

ISMAI

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

64

Computa c ao M ovel

1.12

Bibliograa

Ableson, W. Frank, Robi Sen, Chris King, e C. Enrique Ortiz (2011). Android in Action (3rd ed.). Manning. ISBN 978-1-61729-050-3. Collins, Charlie, Michael Galpin, e Matthias K appler (2012). Android in Practice. Manning. ISBN 978-1-935182-92-4. Darcey, Lauren e Shane Conder (2011). Android Wireless Application Development (2nd ed.). Addison Wesley. ISBN 978-0-321-74301-5. Mednieks, Zigurd, Laird Dornin, G. Blake Meike, e Masumi Nakamura (2011). Programming Android. OReilly. ISBN 978-1-449-38969-7. Murphy, Mark L. (2012). The Busy Coders Guide to Android Development, Version 4.3, supports Android 2.x-4.1 and the R20 tools. CommonsWare LLC. ISBN 978-0-9816780-0-9. Neil, Theresa (2012, Mar co). Mobile Design Pattern Gallery. OReilly. ISBN 978-1-449-31432-3. Steele, James e Nelson To (2011). The Android Developers Cookbook. Developers Library. Addison Wesley. ISBN 978-0-321-74123-3.

$Id: androidDevel.nw,v 1.89 2013/11/13 20:46:31 avs current $

ISMAI

1.13 Indice de Ficheiros

activity main.xml, 59 CicloDeVida-activity main.xml, 35 CicloDeVida-AndroidManifest.xml, 34 CicloDeVida-strings.xml, 34 criaBaseDadosManualmente.txt, 37 criaBaseDadosProgramaticamente.txt, 38 descontos/AndroidManifest.xml, 20 descontos/res/layout/main.xml, 21 descontos/res/values/strings.xml, 20 descontos/src/pt/ismai/cm2011/DescontosActivity.java, 19 exemploQueryComCursor.txt, 42 FicheiroDeTexto.java, 53 FicheiroDeTextoSemLogComToast.java, 58 GaleriaWidgets-AndroidManifest.xml, 52 HelloNumaThread.java, 11 helloWorld1/AndroidManifest.xml, 15 helloWorld1/HelloAndroid1.java, 15 helloWorld2/AndroidManifest.xml, 17 helloWorld2/res/layout/main.xml, 16 helloWorld2/res/values/strings.xml, 16 helloWorld2/src/pt/ismai/cm2011/HelloAndroid2.java, 16 lixo.txt, 23 MainActivity.java, 26 mul actvity main.xml, 50 mul subactity main.xml, 51 MulMainActivity.java, 49 ScoresDatabase.java, 39 SubActivity1.java, 50

65

1.14 Indice Remissivo

adb, 36 android Activity, 12 ambiente de desenvolvimento congurar o eclipse, 9 AndroidManifest.xml, 14 arquitectura das aplica c oes, 12 bases de dados, 36 BroadcastReceiver, 14 classes principais Activity, 12 BroadcastReceiver, 14 ContentProvider, 14 Intent, 12 IntentFilter, 12 Service, 13 ContentProvider, 14 exemplos GUI (aplicar um desconto), 19 Hello World (estilo correcto), 16 Hello World (estilo incorrecto), 15 instala c ao ambiente de desenvolvimento, 2 Mac OS X 64 bits, 7 software necess ario, 3 Windows 7 Pro 64 bits, 4 Intent, 12 IntentFilter, 12 log (uso do), 17 main.xml, 14 minSdkVersion, 8 Service, 13 strings.xml, 14 suporte para chaves estrangeiras, 36 vers oes das APIs e do SDK, 8 xml AndroidManifest.xml, 14 main.xml, 14 strings.xml, 14 avalia c ao, 2 bases de dados, 36 C/C++, 2 chave prim aria necessidade de uso do nome _id, 36 FIXME, 1 iPhone, 2 java threads (concorr encia), 10 mensagens de erro

invalid command-line parameter: Files, 10 MoSync, 4 objectivos, 2 polimorsmo, 58 problemas potenciais instalar SDK do Android num local com espa cos embebidos no nome, 10 Symbian, 2

66