A experiência de fazer um cliente para twitter em JavaFX
Saturday, 01 August 2009 21:53
administrator
Ainda em JavaFx, minha segunda aplicação esta se saindo bem, estou gostando do resultado. Este texto não é só propaganda(mas também é :), vou analisar a criação, negativos e positivos. Mas primeiro, a apresentação: O TwitterFlow é um cliente para twitter que conta com alguns dos principais recursos que deve-se ter já funcionando: - Ele tem múltiplas contas
- Escreve em qualquer uma das contas (doh)
- Atualiza automaticamente (ou não, claro)
- Faz buscas
- Retweet, Reply, links...
Para oferecer algo um pouco além do "basicão", que os clients legais costumam ter: - Envia fotos
- Encurtar URLs
- Salva buscas
- Busca por menções(ret e rep) de qualquer usuário
- Segue usuários sem precisar "follow" de verdade (não lembro mais por que)
- Atualiza qualquer coisa automaticamente
- Manter mais de uma janela, cada uma aconpanhando um fluxo
- Paginação
- Se "esconde" na bandeja do sistema
- Exibe alertas na bandeja quando há novos tweets
Acho que é só isso por enquanto... A tarefa de construção de UIs no JavaFx é muito boa, e mesmo um "nada-designer-e-preguiçoso" como eu consegui fazer algo razoável. Os principais dificuldades foram as já esperadas: Quando fora da UI, não consegui bons resultados com as classes fornecidas pelo SDK. Fiz o controle de threads e temporização na mão, e em Java. Foi uma cascata nessa parte na verdade, primeiro usei as classes mais "abstratas" do JavaFx, a RssTask. Depois Passei a usar o HttpRequest mesmo junto com Timer e TimerTask. Depois usei o Timer e TimerTask com URLConnection (estas já são do Java), e como já estava aqui larguei o Timer e usei Threads e Runnables mesmo. O resultado esta com Threads bem legais até, sem dores de cabeça e boa performance, sem overhead. Na parte de UI apenas algumas ressalvas, escrevi um campo de password baseado no campo de texto, e um Spinner do zero. Ainda faltam matar os Swings do TextArea e (mais complicado) FileChooser, mas acho que o FileChoose já tem pronto por ai. Quanto às chamadas de serviço, a API do Twitter é uma maravilha. Mesmo. A do twitpic abstraí através do twitipic4j. E os encurtadores de URLs também não podiam ser mais simples. Para cuidar do JSon usei a fornecida no próprio json.org , que sempre me ajuda. Já que estou nos créditos, todas as imagens (exceto os avatares, óbvio) são do Iconset do Oxygen. (Links no final) Mas um pouco na API , tem uma complicação na verdade. Depois de todo o mimimi de segurança e todos os problemas em digitar sua senha em aplicativos, o twitter esta introduzindo autenticação por OAuth (parecido com grandes sites fazem), e com novas apps só tem seus nomes aparecendo no tweet se usarem OAuth (antigas continuam ok). Então aparece apenas API por hora. Uma parte que rendeu uma boa Gambiarra foi para fazer os links no tweet. Bom, como não é simples HTML e não posso simplesmente usar <a>, nem inserir eventos em partes especificas de uma string. A primeira POG foi dividir o conteúdo nos espaços e ver se são possíveis links (http, @ ou #). Se for uso um elemento devido, senão uso o Text padrão. E fazer uma sequence com isso. O primeiro problema dessa abordagem foram parenteses e pontuação, por exemplo o conteúdo "bla bla (http://g.com)" sairia errado. Isso já rendeu bons IFs. O segundo é que cada palavra era na verdade um elemento de UI, o "tweet" em sí era um "float" desses elementos. Imagine a confusão! A solução atual é diferente, eu busco pelos links no conteúdo os guardo em uma sequencia, depois ele são inseridos numa área especifica da UI. Salvei bastante memória assim, até melhorou o layout, mas perdi um pouco em design já que não tenho "cores" no tweet (apenas na área dos links). Falando em memória, essa sempre foi uma preocupação. Chances para Leaks , o fato da tecnologia ainda ser nova e fama de Java como devorador de memória me deixavam bastante preocupado. De fato é muito fácil "torrar" memória na construção UI, principalmente por que estou sempre lidando com sequencias e binds, mas tenho feito um esforço bom para mante-la sobre controle e tem ficado tudo bem. Reduzi a passagem de objetos entre diferentes camadas da aplicação, para evitar de esquecer referencias, e reutilizei os objetos que consegui. O modelo de threads ajudou bastante a controlar a memória, um "pool" para as imagens também reduziu bastante. A lista de tweets é mantida somente em um lugar, e quem a recebe sempre se livra da referência. A gambiarra dos links quando desfeita também liberou bastante coisa. Mas é um trabalho eterno, ainda pode melhorar muito. Não gosto de fazer comparações, pois sei que são muito diferentes e todo um blá blá blá(...), mas sempre achei que os clientes twitter consumissem bem pouco. Estava enganado. Tirando os pertencentes aos browsers (twitterfox, Opera Twitter widget, etc), a maioria consome muita memória. Talvez eu só tenha escolhido os maiores consumidores para testar. Nos clients em Adobe AIR, ( Twhirl, Spaz e TweetDeck que eu vi) é fácil começarem nos 70, 80, subir para 90 e 100MBs (e até continuar), o TweetDeck, um client completíssimo, é assustador nisso. Não posso falar que é culpa do AIR (provavelmente não é), já que o DestroyTwitter (que eu usaria fácil, gostei muito) usa bem menos(visual mais simples, talvez), entre 55~65Mbs. Nos feitos em JavaFx, como o TwitterFx e o TweetBox, o consumo também é muito alto, tanto ou maior que os em AIR. O TwitterFlow (o meu) também não está ideal ainda, mas fica parecido com o DestroyTwitter, durante o uso normal ao longo do dia fica entre uns 60~70Mbs, mas se for abrindo mais janelas e mais fluxos, passa fácil para 70~80MBs. Não testei nenhuma aplicação nativa, nem para Windows, nem Linux e nem Mac (informações são bem vindas!), mas imagino que esses se saiam bem melhor (por favor!). Voltando para a aplicação, usei melhor os recursos de binds, triggers e sequences do JavaFx, facilitando manter a conformidade , normalização e sincronismo dos dados. Salvo as configurações usando a API de Storage local, que é encriptada e "sandboxada" para cada app. A parte de integração com o Desktop (lançar navegador e bandeja) ainda esta na gambiarra, ambas usam a classe de Desktop do Java. O lançar navegador usa o BareBonesBrowserLauncher. Ainda quero implementar pelo menos OAuth, manutenção de cada usuário e , claro, memória e performance. E o que mais der na telha! Enfim, o aplicativo, links e código em http://kenai.com/projects/twitterflow. Opiniões são bem vindas, para mal ou bem :)
Last Updated on Sunday, 02 August 2009 00:10
JavaFx - O bom, o ruim e o feio.
Saturday, 25 July 2009 15:40
administrator
Até agora ficou claro que JavaFx não pegou, não existem muitas aplicação “de verdade” rodando, e só entusiastas mesmo tem construído algo. Nesse batalha de RIA parece que o Adobe AIR realmente largou na frente. No meio de minha segunda aplicação na tecnologia, resolvi fazer essa análise, com vantagens, desvantagens e considerações. Vou começar pela vantagem mais óbvia: É Java. Ou melhor, roda na JVM e pode ser escrito em Java. Isso quer dizer que se o JavaFx não supre algum requisito do aplicativo, você pode “descer” para java nessa parte. Isso implica também que você pode fazer uso de uma comunidade gigantesca que disponibiliza milhares de bibliotecas para completar sua aplicação. Além é claro dos recursos e classes da JRE. Aplicações JavaFx podem ser lançadas como applets ou através do Java WebStart. Lançando como applet você ainda tem a opção de arrastar o applet para fora do browser para te-lo como uma app independente. Ambos os modos permitem criar um icone na área de trabalho para o aplicativo. Isso levanta a questão óbivia de performace: No geral o tempo de carregamento tanto de um applet como do JWS é um pouco lento, o startup da JVM ainda é um pouco lento. Mas após lançado o aplicativo a performace é muito boa, e o consumo de memória não é o monstro que todos esperam do Java, com alguns cuidados é possível manter o usso de memoria baixo, mais até que Apps em Adobe Air, por exemplo. Falando em recursos da JVM, o primeiro grande problema em JavaFx é que é “Single Threaded”, isso quer dizer que toda sua aplicação roda em uma Thread só, na EDT (na interface). E o único meio de usar threads é recorrendo a Java. Algumas classes do JavaFx internamente rodam em threads, como HTTP, mas se você quer criar threads, a classe da thread será feita em Java. O JavaFX tem classes para thread próprias, mas tem quer alidas a classes java. Resumindo, se quer thread use as classes de Java (como Timer) ou crie as classes usando Java e as classes async do JavaFx. E qual a diferença entre escrever em Java e JavaFx? A linguagem usada no JavaFx é o javafx script, é uma linguagem híbrida entre Orientação a Objetos e Funcional (mas bem mais OO que funcional), com uma sintaxe declarativa. Isso quer dizer que: É uma ótima sintaxe. É bem mais flexível, mais enxuta e de leitura fácil, lembra muito JSon(e javascript), mas é bem diferente. Tem funções como classes de primeiro nível e variáveis finais. Você pode receber uma função(definida pela assinatura) como parâmetro. Tem ainda os “bind”, que permitem conectar duas ou mais variáveis, com ou sem reciprocidade. Quando você “bind” uma variável a outra é como se cadastrasse um Listener para quando esta mudar, o outra é atualizada. Você pode ainda disparar eventos quando o conteúdo de uma variável é alterado. Isso tudo torna a linguagem muito boa para criação de interface gráfica, e aí esta a maior vantagem do JavaFx: É focado em interfaces. Não posso nem comparar ao swing ou mesmo mesmo swt, é muito mais fácil e flexível. Você pode usar ferramentas gráficas, pode usar o inkscape por exemplo, é bem fácil fazer os elementos da forma e cor que quiser, e adicionar funções a eles. Além, é claro, de contar com os controles mais comuns. Efeitos, transições, animações e layouts não faltam. E, aceita CSS em vários níveis. Bom, ai esta um grande problema na verdade: Ainda faltam muitos componentes “comuns”, como por exemplo a absurda ausência de campos para password e selects drop down. O workaround (ou gambiarra) é que é possivel fazer o “wrap” de componentes swing, o que é feio. Não são muitos elementos que faltam, mas esses dois são uma grande decepção. Mas isso também demonstra que o JavaFx traz utilitários para várias tarefas comuns à aplicativos conectados, por exemplo: o RssTask (e AtomTask) que atualiza a aplicação a cada atualização dos mesmo, verificando em períodos definidos, tem parsers para Json e Xml bem fáceis de usar, baseados em SAX. O utilitário de HTTP também é muito bom, gráficos tem de montão, além de media, incluindo um conversor de diversos formatos. Além de uma API de persistência para os dados da aplicação. Enfim, na minha opinião, JavaFX é uma tecnologia muito boa mas ainda imatura. Claro que, principalmente por pode recorrer ao Java, pode ser usado em aplicações “reais”, mas ainda tem muito o que implementar e corrigir. Nos aplicativos que fiz costumo deixar tudo sobre fluxos e telas ao JavaFx, e nas próprias classes do JavaFx usar as classes Java para certos trabalhos, como threading e pools. Para terminar, confiram alguns links legais, para conferir algumas aplicações:
Last Updated on Sunday, 09 August 2009 15:55
Testes automáticos para webservices Restful, em PHP
Wednesday, 01 July 2009 00:00
administrator
Testes unitários é uma ferramenta essencial para manter a qualidade do código e garantir que esteja tudo funcionando entre manutenções e upgrades. Se você não aplica, deveria :) O fato é que geralmente testamos a aplicação "por dentro", com testes unitários para as classes e methodos dessas, mas na construção de um webservice pode ser interessante testar a aplicação "por fora", realmente acessando a API com entradas válidas e inválidas para garantir que esteja respondendo bem às chamadas de clientes com os devidos cabeçalhos (Content-Type, Response Code e Message, etc) e o conteúdo em si esta correto. Teste de integração e outros são comumente negligenciados (inclusive por mim...). Com isto em mente preparei um pequena classe em PHP, parte do pacote RestServer: a RestClient. É uma classe de uso geral para acesso HTTP através de cURL, pensando em testes para APIs de webservices. O uso é simples, pode ser ver no exemplo/teste, veja por exemplo com atualizar o twitter: <? $twitter = RestClient::post( "http://twitter.com/statuses/update.json" ,array("status"=>"Working with RestClient from RestServer!") ,"username" ,"password"); var_dump($twitter->getResponse()); var_dump($twitter->getResponseCode()); var_dump($twitter->getResponseMessage()); var_dump($twitter->getResponseContentType()); ?> Você pode então combina-la com sua suite testes favorita (ou escrever na mão, se for chato) para testar sua API, veja partes dos testes do IdeasWall.org : // Authorized access should be forbidden $http = RestClient::get($base."/ideas.json"); if($http->getResponseCode() != 401) { echo "Unauthorized access: Wrong response code\n"; echo $http->getResponseCode()."\n"; return false; }
if($http->getResponse() != "Unauthorized") { echo "Unauthorized access: Wrong response\n"; echo $http->getResponse()."\n"; return false; }
// Will now create an idea , should create $http = RestClient::post($base."/ideas.json",array("idea"=>"I Rock","tags"=>"test","priori"=>"1"),"diogok","123"); if($http->getResponseCode() != 201) { echo "Create idea: not created.n"; echo $http->getResponseCode()."\n"; echo $http->getResponse()."\n"; return false; } $ideaJson = $http->getResponse(); // This is the idea created; $idea = Zend_JSon::decode($ideaJson,Zend_Json::TYPE_OBJECT); if($idea->idea != "I Rock") { echo "Idea not created properly: Wrong idea\n"; var_dump($idea); return false; }
// save map $http = RestClient::post($base."/ideas/".$id."/map.json",array("x"=>100,"y"=>200),"diogok","123"); if($http->getResponseCode() != 200) { echo "Erro at save map\m"; echo $http->getResponseCode()."\n"; echo $http->getResponse()."\n"; return false; }
$ideaJson = $http->getResponse(); // This is the idea updated; $idea = Zend_JSon::decode($ideaJson,Zend_Json::TYPE_OBJECT);
if($idea->map->x != "100" || $idea->map->y != "200") { echo "Error in return idea map\n"; var_dump($idea); return false; } Claro que há muitas linha no arquivo de teste, mas deu para entender.
Extraindo thumbnail de preview de videos no android
Tuesday, 30 June 2009 00:00
administrator
Totalmente aleatório, mas deu um trabalho absurdo achar a solução e colocar para funcionar, então é melhor registrar aqui para não esquecer. Resumindo: SurfaceView é velho e não funciona bem e o VideoView ainda é bugado. Olhando ainda no source code das aplicações(a Gallery2Avtivity) que vem no android elas estão desatualizados no repositório e não funcionam na ultimo SDK, tendo sua ultima atualização a meses atras. Enfim, a solução foi a seguinte, pegar uma classe que consta nos source projeto mas não entrou no SDK, a MediaMetadataRetriever e coloca-la em seu projeto, e TEM que ser no pacote "android.media", então fica assim a class android.media.MediaMetadataRetriever. Para extrair o thumb do video, segue o trexo de código: MediaMetadataRetriever retriever = new MediaMetadataRetriever(); try { retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY); retriever.setDataSource(context,uri); // URI do video Bitmap t = retriever.captureFrame(); thumb.setImageBitmap(t); } catch(IllegalArgumentException ex) { ex.printStackTrace(); } catch (RuntimeException ex) { ex.printStackTrace(); } finally { try { retriever.release(); } catch (RuntimeException ex) { } } É isso ae, partiu.
Last Updated on Monday, 29 June 2009 19:10
O padrão Repository para persistência de dados.
Thursday, 25 June 2009 00:00
administrator
Há muito tempo atrás, em uma terra distante, escrevi um artigo explicando a diferença e a abordagem nos padrões mais comuns de persistência de dados: Active Record e DAO. Que na verdade era parte de uma série sobre boas práticas em programação. Lá no fim, disse que escreveria sobre outro padrão, o Repository. Muito tempo se passou, e eu me esqueci disso. Mas o Frederico me lembrou (obrigado), então aqui estou. O padrão Repository (ou Repositório) tem, em uma abordagem muito semelhante ao DAO, a responsabilidade de representar a camada de dados na aplicação. A semelhança entre DAO e Repository é tanta que em muitas abordagens são programados da mesma forma, ou simplesmente delegam do Repositório para o DAO. Em casos de migração onde se tenta “uma abordagem mais Domain Driven” simplesmente refatoram os DAO para se chamarem Repositório. Porem os padrões atuam em camadas distintas da aplicação, e podem até mesmo coexistir. Os Repositórios pertencem ao modelo da aplicação, são parte de camada de negócios complementando o Model e fornecendo estes objetos as outras camadas (como Controle e Apresentação, se aplicável) e recuperando estes do banco de dados. Porem, sendo parte do modelo, os repositórios não conhecem detalhes de infra-estrutura da aplicação (banco de dados, http, etc), e é na área de infra em que atuam os DAOs. Assim o DAO tem o trabalho de traduzir as chamadas à persistência em chamadas de infra, sejam elas banco de dados, webservices, arquivos em disco ou outra abordagem qualquer. A confusão acontece por que na maioria dos projetos toda persistência e acesso a dados é feita através de um , e apenas um, banco de dados. E , mais ainda, os frameworks atuais abstraem e traduzem os objetos em relacionamento de forma tão simples que o DAO simplesmente passa ao framework o que recebe e retorna o resultado. Dessa forma , em sistema onde temos um modelo Usuário, teríamos um UsuárioRepository atuando no modelo. Este repositório faria a interface de criar, recurar e guardar os Usuários, e pode incluir métodos específicos, como filtros ou pré-preencher os usuários. Mas o repositório não sabe de infra, então ele poderia ter de delegar a um DAO as chamadas. Desta forma os DAO apenas implementam as chamadas mais genéricas como select, insert, update e delete. Em um framework atual, que oferece essa interface de forma simples, o repositório poderia passar a chamada ao framework, se a configuração não passar pelo repositório. Então, ao receber uma chamada o Repositório valida isto de acordo com as regras de negócio que o cabe(já que a maior parte pode estar no próprio modelo), e pede à camada de infra esses dados. A camada de infra trata essa chamada no cabe à infra ( valida, encode, traduz campos...) e acessa a persistência. Ela trata o retorna da persistência no que vale a infra (array para objetos, encoding, joins...) e devolve ao repositório. O repositório então trata este retorno o que diz respeito ao negócio, criando objetos, cálculos e o que for aplicável. Como disse, em muitos projetos isso gera classes desnecessárias e o mal uso dos repositórios. Uma forma diferente de lidar com isso é que os Repositórios são interfaces, implementadas pelos próprios DAO(o contrário não é valido), pois do ponto de vista de modelagem o modelo continua acessando o Repositório. E pode-se criar um repositório implementado aonde preciso, antes do DAO. Esta forma parece um pouco mais complexo, mas é ate mais simples. O que temos afinal é: O Repositório é uma camada de negócio da aplicação, responsável por manter e persistir os objetos de Modelo, enquanto isto não envolver infra. Quando tratar de infra, esse trabalho é delegado aos DAO ou diretamente ao framework, caso este seja bem abstraído.
Last Updated on Wednesday, 24 June 2009 12:48
|
|