EPx professional blog and repository for braindumps
2007/11/27
Palestras em Joinville e Manaus
Na última semana ministrei a mesma palestra em Joinville/SC (na SOCIESC, mais conhecida como "Escola Técnica Tupy") e em Manaus/AM (na UFAM), no contexto dos "Tech Days" promovidos pelo Instituto Nokia de Tecnologia (INdT).
Na falta de título melhor, a apresentação denomina-se "Desenvolvimento rápido para dispositivos móveis Nokia". O "powerpoint" no formato OpenOffice pode ser obtido aqui.
A idéia é mostrar os atuais problemas do desenvolvimento para mobile, e também o que a Nokia e o INdT têm feito para melhorar a vida do desenvolvedor. Tudo numa visão de 30 mil pés de altura, já que o tempo é curto e a platéia poderia ter perfil gerencial. Os Tech Days também ofereceram mini-cursos de desenvolvimento para Maemo em seguida às palestras, que sobejamente satisfez quem estava curioso em ver sangue e bits.
Do ponto de vista pessoal, Joinville transcorreu sem novidades, afinal é onde eu moro e o evento foi onde eu leciono. Já Manaus tem sido muito mais interessante, afinal eu nunca tinha vindo sequer ao Norte do Brasil.
Em primeiro lugar: é quente, e o calor é amplificado pela umidade. Embora eu já tenha pegado dias iguais ou piores em Joinville no verão, de modo que ainda estava dentro das minhas especificações. A dica é levar uma garrafa de água consigo, sempre.
Em segundo, deixa-se muitos espaços arborizados ao longo da cidade, floresta mesmo. Ajuda a diminuir a temperatura e embeleza muito. A vida selvagem faz parte do consciente coletivo. Quando estava chegando na UFAM, um bicho-preguiça simplesmente parou no meio da rua. Os motoristas de ambas as mãos *pararam* e um deles deu-se ao trabalho de tirar ela de lá.
De maneira geral, a cidade é bonita, não há aquelas std::favelas comuns nos grandes centros, e há um esforço nítido em "não deixar a peteca cair" em termos de aparência urbana: as casas estão pintadas, as calçadas estão em ordem...
Terceiro, foi interessante conhecer as indústrias da Zona Franca, bem como o Rio Negro, encontro das águas, o Teatro Amazonas (teto na foto ao lado), enfim, aquelas coisas que aparecem na TV ou vêm impressas nas caixas de inúmeros produtos. Independente de discussões sobre guerra fiscal, dá uma sensação de completeza saber "de onde vêm as caixas".
O pessoal do INdT/MAO foi muito gentil conosco em nos mostrar a cidade. Fiquei com consciência pesada por não ter feito o mesmo quando eles estiveram em Joinville (eu realmente precisava colocar o trabalho em dia para ter tempo de vir para cá). Mas haverá outras oportunidades para saldar este débito.
Uma reclamação comum de quem já veio para cá é a demora em serviços em geral. Mas não tive nenhum problema desse tipo. Juro. Com uma exceção: ontem pedi um bule de café no quarto (eu bebo café antes de dormir, e é *para* dormir bem, coisa de viciado mesmo. Como diz o meu amigo Rudá Moura, café é uma droga como qualquer outra) e demorou 2 horas e meia para chegar.
Vou pedir mais um bule hoje, agora. Veremos o que se sucede.
Desenvolvimento Série 60 para quem desenvolve em Linux - parte 3
"Quer moleza, vai mastigar água de cabeça pra baixo!" -- Desenvolvedor Symbian respondendo à dúvida de um novato.
OpenC -- a luz no fim do túnel
O principal sintoma de que a API do Symbian é mais complicada do que seria razoável, é a baixa dinâmica da comunidade de desenvolvedores. É difícil encontrar software livre para Symbian, e é difícil encontrar respostas para dúvidas no Google. Isso para um sistema operacional que roda em 118 milhões de dispositivos.
Enxerga-se melhor o problema comparando com a comunidade Palm OS. O Palm é uma plataforma moribunda, muita gente diz que o conceito de PDA é obsoleto, mas apesar de tudo isso há uma comunidade muito atuante. Isso se conseguiu com decisões corretas lá no início do Palm: uma API com complexidade dentro do razoável, "parecida" com o que já existia (comunicação via sockets, por exemplo, é exatamente igual ao POSIX), e ferramentas de boa qualidade. O emulador do Palm já era bom desde a época que ele usava processador 68000. E sempre teve emulador e compilador Palm para Linux. De graça.
Enfim, parece que pelo menos a plataforma Série 60 enxergou o óbvio ululante, e lançou o OpenC - uma biblioteca que implementa a API POSIX, threads inclusive, mais o porte de algumas bibliotecas que são "padrão de fato" no mundo do software livre: libz, OpenSSL e GLib/GObject.
O OpenC é para Série 60, não para Symbian. Isso exclui dispositivos Série 80, Série 90 e UIQ. Mas como a Série 60 é a variante mais forte comercialmente, beneficia a maior fatia de usuários.
Posso dizer por experiência própria que o OpenC é um bom software. Tem demonstrado estabilidade e tenta oferecer o máximo possível de recursos do POSIX. Desta forma, o porte de uma aplicação ou biblioteca para OpenC é basicamente tranqüila. Tenho inclusive usando muitas threads, que em Symbian normal é considerado "heresia".
Alguns "issues" a serem considerados, ao menos os que me afetaram:
* O suporte a sockets do OpenC tem algumas peculiaridades. Não é possível obter a lista de interfaces da máquina, porque celular não tem interface de rede, tem "access point", que só é ativado na hora que realmente precisa, para economizar energia. O OpenC oferece chamadas ioctl() adicionais (fora do padrão POSIX) para lidar com access points.
* Ainda na parte de rede, há algumas diferenças na API. É preciso fazer bind() em todo soquete, seja conexão TCP ou UDP, do contrário não funciona. É na hora do bind() que aparece a janelinha perguntando que access point você quer usar (salvo você ter selecionado via ioctl() antes dentro do aplicativo).
* Ao criar um soquete UDP, é necessário passar socket(..., IPPROTO_UDP) no terceiro parâmetro. Se passar zero, não funciona (o comportamento POSIX correto é usar UDP como default). No caso de soquete TCP, o OpenC usa corretamente TCP como default, não é necessário passar IPPROTO_TCP.
* A versão atual do OpenC, que é um add-on para 3rd Edition FP1, não suporta multicast. O programa compila mas vai retornar erro no setsockopt(IP_ADD_MEMBERSHIP). O SDK 3rd Edition Feature Pack 2 beta já tem OpenC embutido e suporta multicast, mas não há nenhum celular com FP2 na praça ainda.
* Quando o select() retorna dizendo que há arquivos ou soquetes a serem lidos/gravados, o programa *tem* de tratar todos nessa oportunidade. Se o programa "esquecer" algum soquete, o próximo select() NÃO vai retornar indicando que aquele soquete ainda precisa ser tratado. Muito código POSIX por aí trata apenas um soquete de cada vez a cada retorno do select(); essa estratégia falhará no OpenC, então é bom ficar esperto.
* Não há como implementar sinais POSIX em Symbian, portanto o OpenC não oferece esta funcionalidade. Aplicativos que dependem de sinais para funcionar são os mais trabalhosos para portar. Em particular quando usam sinais para comunicação entre uma thread e outra. Também não há como criar subprocessos via fork().
* O OpenC não oferece nada em termos de interface gráfica, ainda. É possível fazer um aplicativo OpenC completamente livre da API Symbian, com ponto de entrada main() e tudo, mas ele não vai ter rosto. Para um aplicativo "normal", a solução é usar API Symbian para interface de usuário (naquele esquema cheio de classes do post anterior), e usar OpenC apenas na lógica.
Imagina-se que OpenC vá oferecer UI no futuro. Considerando que o OpenC já possui o GLib portado, que o Maemo é baseado no GTK+, e que o GTK+ é o "padrão de fato" no mundo do software livre, as apostas são que o OpenC ofereça GTK+ um dia. Tomara :)
* Não só UI, mas muita outra coisa cai fora da API OpenC. Para usar multimídia, câmera, GPS, discar um número, etc. você ainda terá de usar a API Symbian. Em comparação, o Python para Série 60 dá acesso a todos estes recursos.
Pior: o Python para Série 60 é assinado pela Nokia, e tem capabilities bastante extensas. Já um aplicativo OpenC tem de ser assinado por você, e isso implica em ter capabilities restritas, ou então ter de cair com o cobre para homologar seu aplicativo no Symbian Signed.
(Imagino que, um dia, o OpenC vá dar acesso a estes recursos de forma "organizada", não dando acesso a /dev/dsp, mas sim usando os padrões do freedesktop.org tal qual o Maemo. No caso de áudio, vídeo ou fotos, a API padrão de fato seria o GStreamer. Mas não tenho a menor idéia se é possível portar o GStreamer para Symbian.)
* A saída padrão (STDIO) ocorre dentro de um aplicativo separado (STDIO server), e não num console separado. Tem a vantagem de funcionar mesmo no celular (para o usuário final não ver as mensagens, basta remover o STDIO server do celular). E a desvantagem da tela ter pequeno tamanho, portanto é incômodo usá-lo para depuração (seria melhor poder jogar a saída num terminal do PC).
E finalmente, sempre temos de lembrar que o desenvolvimento em OpenC acontece dentro do contexto das ferramentas Symbian, o que implica em Windows e um emulador problemático. Assim, embora o OpenC seja vendido como um convite à comunidade de software livre a experimentar o Symbian, eu acho exatamente o contrário: o OpenC veio é para facilitar a vida dos desenvolvedores Symbian!
Resumindo, reconheço que há interesse da Nokia em construir a ponte Symbian-software livre. E aprecio a iniciativa. Mas, diferente do anunciado, eles começaram a construir a ponte pelo lado "de lá" do rio.
Desenvolvimento Série 60 para quem desenvolve em Linux - parte 2
"O senhor é um fanfarrão!" -- Capitão Nascimento falando ao arquiteto do Symbian
A API do Symbian é um produto legítimo dos anos 90: baseia-se fortemente em design patterns C++. No caso de uma aplicação estilo 'Hello World' gerada pelo SDK, temos as seguintes classes (para uma aplicação chamada EPx):
CEPxApplication: representa a aplicação como um todo
CEPxAppUi: controla a UI, representa o "controller" do modelo MVC. Na prática, o grosso da aplicação vai ser codificada nesta classe, ou pelo menos é a partir desta classe que o resto do seu código é invocado.
CEPxAppView: representa uma "visão" da UI, pode haver várias visões. É obviamente o "view" do MVC.
CEPxDocument: representa o "Model" do modelo MVC.
O ponto de entrada da aplicação é a função E32Main() no código-fonte EPx.cpp. Neste mesmo arquivo há outra função chamada NewApplication(). A aplicação é carregada da seguinte forma:
* E32Main() chama EikStart::RunApplication(NewApplication). * tendo recebido o endereço via RunApplication, o framework chama NewApplication(). * NewApplication() instancia um objeto CEpxApplication. * O framework invoca CEpxApplication::CreateDocumentL(), que instancia um objeto CEPxDocument. * O framework invoca CEPxDocument::CreateAppUiL() que cria um objeto CEPxAppUI. * O framework invoca CEPxAppUi::ConstructL() que contém e instancia um objeto CEpxAppView.
Enfim, barroco ao extremo.
O próximo choque cultural do desenvolvedor não-Symbian é o "tratamento de exceções". Como as exceçòes e templates do C++ costumavam ter problemas de implementação em todos os compiladores, o Symbian criou um tratamento de exceção sui generis.
Funciona assim: se um método qualquer quiser lançar uma exceção, chama o método estático User::Leave(código_do_erro). Normalmente isto faz o programa fechar. Porém, se o chamador do método quiser capturar esta exceção, ele chama o método através da macro TRAPD(erro, método). Em caso de exceção, a variável erro conterá o código, e o chamador pode agir conforme.
Internamente, este mecanismo parece ser implementado usando longjmp(). CORREÇÃO: segundo o amigo Adenilson, o Symbian 9 utiliza exceções C++ padrão internamente.
Como convenção, todo método que pode causar uma exceção termina com L (de Leave, não de Loser :). Métodos como NewL, ConstructL, RunL têm esse nome pois podem "sair", e os chamadores têm de lidar com isso.
Nas exceções C++ nativas, se qualquer construtor lançar uma exceção, a memória eventualmente comprometida é automaticamente liberada. Mas a exceção Symbian é muito pobre para garantir isso. A solução foi dividir o processo de construção em duas fases: NewL e ConstrucL. NewL() cuida principalmente de alocar o objeto, lidando com a possibilidade da falta de memória. ConstructL() acaba de construir um objeto já alocado (por exemplo abrindo arquivos, conexões de rede que o objeto precise etc.).
Quem apenas usa uma classe, simplesmente chama classe::NewL() para obter um objeto. Quem implementa a classe precisa prover NewL() e ConstructL(), e invocar ConstructL() de dentro de NewL().
Um problema no tratamento de exceções de construção é a destruição de objetos em heap. Para facilitar isto, foi criada outra convenção: classes feitas para serem alocadas em heap têm o nome começando em C maiúsculo (daí os nomes CEPxApplication, CEPxAppUi...). Em tais classes, o construtor C++ deve ser privado (ou seja, proibido de ser usado diretamente) e o usuário da classe sempre chama a fábrica classe::NewLC() para obter novos objetos.
A propósito, esta é outra convenção: métodos que retornem um novo objeto em heap devem terminar com o nome em C, por isso NewL() vira NewLC(). Mas normalmente pode-se usar NewL() mesmo ppara tais classes (NewL simplesmente chama NewLC).
A outra avis-rara do Symbian é o "active object". Quase todas as operações não triviais do Symbian são assíncronas, ou seja, você chama e ela retorna imediatamente, sem o resultado que você quer. Por outro lado, não é recomendado usar threads para lidar com tais operações. A saída é usar um active object, que tenta ser uma espécie de thread "leve" (na verdade é multitarefa colaborativa).
Se você tem uma tarefa assíncrona, deve criar uma classe para ela, e esta classe deve ser descendente de CActive.Todos os active objects são controlados por um escalonador, que escolhe o active object de maior prioridade e chama RunL() para sua classe, quando nada mais importante há a ser feito.
Dentro de RunL() você faz a sua tarefa assíncrona, de preferência em pequenos pedaços e retornando para o escalonador assim que possível. Enquanto você não retornar de RunL(), o escalonador não terá chance de alocar tempo para outros active objects.
No caso de solicitar tarefas assíncronas a um "servidor", ou seja, a algum subsistema do Symbian, você terá de passar um objeto Observador, um design pattern que faz papel de callback. Normalmente isso implica que você torne uma classe do seu programa descendente do observador adequado àquele subsistema.
Se por exemplo você quer tirar uma foto, poderia fazer CEpxAppUi ser uma subclasse de MCameraObserver, e implementar o método CEpxAppUi::ImageReady(). Solicite a foto chamando CCamera::CaptureImage(), e quando ela estiver pronta, o subsistema câmera chamará seu método ImageReady().
Outro uso do observador é ser notificado quando uma tarefa delegada a um active object foi terminada. Supondo que deleguemos a gravação da foto a um active object. Essa gravação pode demorar a acontecer pois outros eventos mais prioritários acontecem (e.g. o usuário pode estar tirando mais fotos). Quando a gravação finalmente acaba, o active object chama o observador.
Parece interessante mas deixa o código extremamente engessado, devido ao observador ter de descender de uma classe. No caso de uma linguagem interpretada como Python, bastaria que o observador implementasse o método de nome FooBar(), não haveria necessidade de descender da class FooBarObserver.
Eu mencionei que o Symbian tem "servidores". Tendo seguido as modas dos anos 90, Symbian é microkernel, e cada serviço como: sistema de arquivos, rede, câmera, tela etc. é controlado por um processo separado. O kernel apenas cuida da memória virtual e coordena a troca de mensagens entre clientes e servidores. É um dos motivos pelo qual quase todo serviço é atendido assincronamente.
Parece bacana, e na verdade tem suas vantagens, mas implica que cada chamada de sistema implique em 2 trocas de contexto no mínimo (cliente para servidor e servidor para cliente) fora a "burocracia" na comunicação. Conforme os celulares ficam mais rápidos, a lentidão inerente ao Symbian fica mais aparente, em particular na interface de usuário.
Desenvolvimento Série 60 para quem desenvolve em Linux - parte 1
Tendo enfrentado neste ano o desafio de desenvolver para Symbian e Série 60, vindo de longos anos de experiência anterior exclusiva em Linux, achei que seria interessante escrever algumas impressões, para quem pretende desenvolver algo em Série 60.
Em primeiro lugar, é uma filosofia completamente diferente. Ainda mais para quem está acostumado a desenvolver em terminal, usando make e vi. No Symbian, você vai usar um ambiente pronto, e o jeito é confiar nele.
Para começar, é preciso usar o Windows, pois as ferramentas são apenas para Windows. No site http://forum.nokia.com, obtenha os pacotes Carbide.c++ (versão 1.2 funciona bem, as anteriores não), e o SDK S60 C++ apropriada para seu celular. Presumindo que você tem um celular Série 60 3a edição, então seria o SDK S60 C++ for 3rd Edition Feature Pack 1, ou Feature Pack 2. Estas versões têm emuladores melhores que o 3rd Edition "original".
Além disto, você vai precisar do Active Perl e do Java Runtime Environment. As versões exatas recomendadas para o SDK que você está instalando serão informadas durante o processo de instalação.
Recomendo também obter o Open C, e instalar tanto no SDK quanto no seu celular. Infelizmente, OpenC existe apenas para S60 3a edição.
Para testar o ambiente e gerar rapidamente algo que possa ser instalável em seu celular, inicie o Carbide e gere um novo projeto usando o Wizard. Rodá-lo no emulador, gerar o pacote e instalá-lo no celular já vai ser um exercício, em particular para quem não está acostumado a Windows, Carbide, Symbian ou a lidar com seu celular...
Um projeto Symbian depende de inúmeras coisas estarem corretas para ele funcionar no celular. Por exemplo, cada programa tem três códigos UID (UID, UID2 e UID3). Um identifica a plataforma, outro identifica univocamente o programa, e o outro eu não sei até hoje :) Se o UID especificado no projeto não "bate" com o especificado nos arquivos de inclusão, o programa funciona no emulador porém não funciona no celular. Começar um projeto a partir de um template do Wizard evita esse tipo de dissabor.
Gerar um projeto a partir do Wizard também permite explorar logo o esqueleto de um projeto Symbian, e a forma de construir projetos.
A (única) parte que eu realmente gostei do Symbian foi o esquema de construção de programas. Pelo fato da estrutura do código ser mais rígida, fica mais fácil tratar a questão da construção. Todo projeto Symbian tem uma estrutura de pastas semelhante a esta:
\group: contém os arquivoss específicos do Symbian, como BLD.INF e PROJETO.MMP
\src: fontes .cpp
\inc: arquivos de inclusão .h
\data: arquivos de inclusão com mensagens internacionalizadas e menus
\gfx: parte gráfica, como por exemplo o ícone da aplicação
\sis: onde o pacote final é jogado, geralmente contém o arquivo PROJETO.PKG
É possível mudar o nome destas pastas, mas usar os nomes padrão facilita a compreensã o do seu projeto para terceiros.
Todo o processo de compilação e empacotamento é gerenciado por apenas 3 arquivos-texto, dois da pasta \group e um da pasta \sis:
PROJETO.MMP: este é o principal deles. É neste arquivo que se especifica os UIDs (códigos numéricos) do programa, quais as pastas que contém arquivos de inclusão, quais os programas a serem compilados etc. Ele é análogo ao "Makefile.in" do software livre. Baseado neste arquivo, todo o esquema de construção e tratamento de dependências é levado a cabo. Exemplo:
BLD.INF: Define quais arquivos MMP serão invocados para construção, e também define, no caso de bibliotecas DLL, quais arquivos de inclusão devem esr copiados para Epoc32\include, para serem vistos por outros projetos. Exemplo:
PRJ_PLATFORMS WINSCW ARMV5 GCCE
PRJ_MMPFILES
gnumakefile icons_scalable_dc.mk
EPx.mmp
PROJETO.PKG: define o empacotamento do programa para distribuição aos celulares. Essencialmente lista os arquivos que serão incluídos no pacote. Equivalente ao "debian/control" do Debian, ou ao "programa.rpm" do RedHat. Exemplo:
;Language - standard language definitions &EN
; standard SIS file header #{"EPx"},(0xE1BFA2A2),1,0,0
;Localised Vendor name %{"Vendor-EN"}
;Unique Vendor name :"Vendor"
;Supports Series 60 v 3.0 [0x101F7961], 0, 0, 0, {"Series60ProductID"}
;Files to install ;You should change the source paths to match that of your environment ; "$(EPOCROOT)Epoc32\release\$(PLATFORM)\$(TARGET)\EPx.exe" -"!:\sys\bin\EPx.exe" "$(EPOCROOT)Epoc32\data\z\resource\apps\EPx.rsc" -"!:\resource\apps\EPx.rsc" "$(EPOCROOT)Epoc32\data\z\private\10003a3f\apps\EPx_reg.rsc" -"!:\private\10003a3f\import\apps\EPx_reg.rsc" "$(EPOCROOT)Epoc32\data\z\resource\apps\EPx.mif" -"!:\resource\apps\EPx.mif"
; Add any installation notes if applicable ;"EPx.txt" -"!:\private\E1BFA2A2\EPx.txt"
;required for application to be covered by backup/restore facility "..\sis\backup_registration.xml" -"!:\private\E1BFA2A2\backup_registration.xml"
E isso é tudo. Esses 3 arquivos gerados 99% automaticamente, mais os fontes .cpp e .h, é tudo que se precisa para construir um programa Symbian, seja na linha de comando ou no IDE Carbide. É muito, mas muito mais simples do que o esquema automake/autoconf comum em software livre.
No Carbide, para importar um projeto, basta mandá-lo importar o BLD.INF, pois a partir deste arquivo ele acha os MMPs e por conseqüência todo o resto dos arquivos. O arquivo .PKG tem de ser escolhido manualmente no Carbide, mas isso é simples.
Na linha de comando, preparar um novo projeto para construção envolveria ir para a pasta group\, rodar
BLDMAKE BLDFILES
o que gera um script chamado ABLD.BAT. Dali para frente, basta chamar ABLD.BAT para reconstruir o projeto a cada mudança.