speed

Otimizando a performance de aplicativos Cordova/Ionic Framework

Olá pessoal, nessa última semana tive a boa surpresa de ter meu aplicativo Mini Receitas aceito no showcase do Ionic Framework e o melhor é que ele foi considerado pela equipe do Ionic como um dos Top Apps feitos com o framework. Esse bom resultado me fez refletir em como eles escolhem os aplicativos que vão para o Top Apps e a conclusão que eu cheguei é que como eles não conseguem testar a finalidade de todos os aplicativos até por que eles podem estar em idiomar que eles desconhecem, como o português, o que vale mesmo é a performance e o design.

Não sou nenhum especialista em design, mas depois de meia década trabalhando como front-end eu tenho noção do que não fazer, então acho que não passo vergonha como muitos desenvolvedores (principalmente os que vieram do mundo back-end). No entanto, como não sou especialista nesse assunto prefiro falar pra vocês dois cuidados (banco de dados e animações) que tomei para criar um aplicativo híbrido performático.

Banco de dados

A primeira coisa que eu fiz para otimizar a performance do aplicativo foi efetuar o mínimo de operações possíveis no banco de dados pois essas bloqueiam o DOM e iriam travar minha interface. De início segui as dicas do artigo How To Use PouchDB + SQLite For Local Storage In Your Ionic App da Ashteya Biharisingh e embora isso tenha melhorado muito a performance do meu aplicativo eu ainda tinha problemas nas transições de telas. Principalmente nos primeiros acessos às telas, pois o Ionic ainda não tinha colocado-as em cache.

O que acontece é que quando você realiza o primeiro acesso a uma página não cacheada ou uma que possui o cache desabilitado o seu código consulta o banco e isso causa o bloqueio do DOM. Então o objetivo é evitar que ocorra esse bloqueio. E a forma que encontrei de fazer isso foi carregar todo o banco de dados em cache antes mesmo do usuário acessar a tela que irá utilizá-lo.

Para isso eu criei um serviço chamado Loader que possui um método chamado done que me retorna uma promise. Esse método done é quem carrega todos os dados do banco para o cache como descrito pelo artigo da Ashteya.

Logo, o que precisei fazer (usando alguns recursos do ES6) foi:

services/loader.service.js

/**
  * LoaderService
  */
  
(function() {
  'use strict';

  function LoaderService ($q, RecipesService){
    'ngInject';

    this.done = function() {
      return $q.all([
                RecipesService.getAllRecipes()
              ])
              .then((...args) => args)
              .catch((...args) => args)
    }

  }

  module.exports = LoaderService;
}());

index.run.js

/**
  * Run
  */
  
(function() {
  'use strict';

  function runBlock (Loader, $ionicPlatform, $cordovaSplashscreen) {
    'ngInject';

    $ionicPlatform.ready(function(){
      Loader.done()
            .then(function(){
              $cordovaSplashscreen.hide();
            })
    })

  }

  module.exports = runBlock;
}());

index.module.js

/**
  * Definição do módulo
  */
  
import runBlock from './index.run';

import LoaderService from './services/loader.service';
import RecipesService from './services/recipes.service';

angular.module('miniReceitas', ['ionic', 'ngCordova'])
       .run(runBlock)

       .service('RecipesService', RecipesService)
       .service('Loader', LoaderService);

Note que eu também faço o uso do plugin cordova-plugin-splashscreen , faço isso para que o usuário só possa interagir com o aplicativo após todo o banco de dados estar em cache. Dessa forma ele não percebe o bloqueio do DOM pelas consultas ao banco. O único momento o usuário pode vir a notar algum tipo de bloqueio é ao inserir, alterar ou deletar algum registro no banco. No entanto essas operações normalmente são mais leves que uma query de consulta.

Animações

Animações são muito importantes para que aplicativo ofereçam uma boa experiência para o usuário. Quando estamos criando sites nem sempre nos preocupamos com a performance delas já que computadores possuem muito mais poder de processamento, porém nem sempre trabalhamos com mobile-first, o que é um erro em um mundo onde o usuário passa boa parte do seu dia conectado a internet através de dispositivos móveis.

Enfim, para otimizar as animações nós devemos entender como o CSS é renderizado e para isso eu recomendo a leitura do artigo Entendendo os Reflows. Mas em síntese o que precisamos fazer é evitar Repaints e Reflows. E para evitá-los basta projetar seu CSS com propriedades que não disparem esses eventos.

Você pode, por exemplo, utilizar o transform: translateY ao invés do top ou o bottom para uma animação, pois o top e bottom disparam tanto o Repaint quanto o Reflow. Você pode utilizar o CSS TRIGGERS para consultar se a propriedade css que você está utilizando para suas animações dispara algum desses eventos.

(Bônus) DOM

Ainda não terminei a implementação desse terceiro item, por isso ele está presente apenas como bônus. Mas assim como temos que ter cuidado com a árvore de renderização do CSS, nós também temos que ter cuidado com a do HTML. E uma técnica que vem cada vez mais sendo utilizada para otimizar a performance do DOM é o ato de renderizar apenas os nós visíveis. No caso do AngularJS que cada um dos nós pode possuir uma escopo ligado a ele por meio do two-way data binding isso pode otimizar ainda mais a performance pois remove os listeners dos itens que não estão visíveis, já que eles não estão renderizados.

No Ionic uma diretiva que faz uso dessa técnica é a Collection Repeat e estou trabalhando para implementá-la no novo Slider que foi introduzido na versão 1.2 do framework.

Enfim fim

E é isso, além dessas existem outras otimizações que já citei no artigo 4 Dicas para trabalhar com o Ionic Framework e vocês podem encontrar mais algumas dicas na apresentação Top 10 Performance Techniques for PhoneGap and Hybrid Apps do Christophe Coenraets que ainda continua super atual. Sem contar que no fim das contas é tudo Javascript, logo, otimizações a nível da linguagem são sempre bem vindas, nesse sentido recomendo o artigo 10 Javascript Performance Boosting Tips from Nicholas Zakas do Jon Raasch e o artigo do Addy Osmani entitulado Writing Fast, Memory-Efficient JavaScript.


Ultimamente tenho estudado mais questões relacionadas a engajamento do usuário, promoção de aplicativos e melhoria de ranking nas lojas de aplicativos e achei conveniente fazer um post sobre performance enquanto essas informações ainda estão frescas em minha memória. Espero que esse post possa ajudá-los e caso você utilize alguma outra técnica para otimizar a performance dos seus aplicativos deixe seu comentário abaixo para que possamos disctuir e criar aplicativos cada vez melhores.

Cadeados

Desempacotando APK’s da Google Play Store

Olá pessoal, acredito que esse post poderá ser bastante polêmico tendo em vista que algumas pessoas não gostariam que esse conteúdo fosse disseminado. No entanto acredito que para a evolução do ecossistema de desenvolvimento híbrido isso se faz necessário. Particularmente aprendi muito ao inspecionar e realizar engenharia reversa em aplicativos de terceiros baixados da Play Store e espero que com esse artigo mais pessoas sejam capazes de otimizar seus aplicativos através das descobertas proporcionadas pela técnica aqui descrita.

Bom, como todos sabem existe uma categoria de aplicativos híbridos que rodam em um navegador no dispositivo e são códigos HTML, CSS e Javascript. Esses códigos são armazenados no dispositivo sem nenhum tipo de compilação. Portanto, se tivéssemos acesso a esses códigos conseguiríamos ler o mesmos. O fato é que muita gente sabe que esse processo é possível mas nunca o realizou, nem mesmo nos seus próprios aplicativos.

Os APK's são arquivos compactados com as instruções necessárias para que o Android leia e execute uma aplicação. No caso dos aplicativos Cordova/Ionic esses arquivos carregam em si códigos HTML, CSS e Javascript e no caso de aplicativos React Native eles carregam um bundle Javascript.

No React Native o próprio processo de build do framework já realizada a minificação do código, fato que não ocorre em aplicações Cordova/Ionic, ficando por conta do desenvolvedor realizá-lo. As vantagens de se ter um código minificado fica para um outro artigo, mas é importante notar que alguns dos aplicativos desempacotados possuírão o código minificado e outros não. Nos que não tiverem será possível ler todo o código do mesmo sem nenhum esforço de engenharia reversa.

Também é importante ressaltar que essa técnica também é aplicável para aplicativos não híbridos, porém ela irá necessitar de um conhecimento em engenharia reversa de códigos Java.

Desempacotando os Aplicativos

Para desempacotar um aplicativo a primeira coisa que se deve fazer é possuir o arquivo APK do mesmo. Para isso você pode utilizar o site apkpure.com ou alguma extensão para o Google Chrome, em meus experimentos eu utilizei a extensão APK Downloader for Google Play, porém ela não está mais disponível na Chrome Web Store.

Além do APK, nós precisaremos de uma ferramenta chamada Apktool (http://ibotpeaches.github.io/Apktool/) que é quem efetivamente irá desempacotar o aplicativo.

Após instalar o Apktool e possuir o APK do aplicativo basta ir no terminal, navegar até a pasta onde o arquivo está localizado e rodar o comando:

apktool d NOME_DO_ARQUIVO.apk

Logo, para desempacotar o meu aplicativo Mini Receitas eu rodaria o comando:

apktool d Mini\ Receitas_v1.1.0_apkpure.com.apk 

Apktool terminal

O Apktool irá criar uma pasta com o mesmo nome do seu APK, para visualizar o código é só entrar nessa pasta.

Mini Receitas estrutura de pastas

A estrutura de cada aplicação poderá variar, mas aplicações Cordova sempre possuirão as pastas www e dentro dela a pasta plugins. Na pasta www vocês poderão encontrar os códigos do aplicativo e na pasta plugins os plugins Cordova utilizados. Foi através da inspeção da pasta plugins que eu descobri ferramentas como o Branch e o Flurry. Do código do meu aplicativo pode ser interessante que vocês procurem saber um pouco mais sobre o CodePush.

Enfim…

É isso. Na realidade o procedimento é muito fácil, mas acredito que por falta de interesse ou curiosidade muitas pessoas nunca tinham procurado saber como se é feito. A verdade é que esses simples passos podem ajudar, e muito, no seu processo de aprendizado. Saber como outras pessoas fazem em produção pode te ajudar a fazer melhores escolhas e a cada dia criar aplicativos melhores.

tappx

Promova seu aplicativo de graça

Eu sei, o título desse post até parece sensacionalista e com o objetivo de conseguir cliques, mas, pelo menos dessa vez, não é. Nesse ano de 2016 eu me propus um desafio: viver só com a grana proveniente de meus aplicativos para dispositivos móveis e pra isso eu estabeleci que faria um aplicativo por mês até o fim do ano.

Daí você pergunta: "Ok, mas o que isso tem a ver com promoção de aplicativos gratuita?". Bom, a princípio nada, exceto que para conseguir viver disso eu preciso que os aplicativos tenham muitos downloads, porém com o grande número de aplicativos nas lojas hoje a concorrência é muito grande e provavelmente meus aplicativos não vão ganhar usuários se eu não encontrar formas de chegar até eles.

E aí que entram técnicas como ASO, que é o equivalente ao SEO para aplicativos, e os Ads, afinal, eu posso anunciar meus aplicativos. Mas aí entra um outro porém, o custo de aquisição do usuário. Enquanto o ASO é um conjunto de técnicas para melhorar o rankeamento nas lojas, na maioria das vezes não é necessário gastar dinheiro com ele. Já com os Ads a conversa muda.

O custo médio de aquisição de usuários varia bastante de acordo com inúmeros fatores, como: país, tema do aplicativo, idade do público alvo, etc.. E, infelizmente, eu não tenho muita grana pra investir, por exemplo, R$ 1,00 para cada nova instalação do aplicativo, e olha que esse é um valor relativamente baixo.

E foi aí que eu conheci o Tappx.

Tappx

O Tappx é um Ad Network para promoção de anúncios diferente das outras. Nela, a cada cliques em anúncios veiculados por ela em seu aplicativo você ganha 500 tappix, que é a moeda de troca da plataforma, com eles você pode criar suas próprias campanhas onde a cada clique que seu anúncio receber você paga 500 tappix, ou seja, para cada vez que alguém clicar em um anúncio no seu aplicativo você ganha o direito de exibir anúncios nos aplicativos de outras pessoas até que alguém clique no seu anúncio.

O interessante é que a comunidade Tappx hoje é muito forte e existem alguns aplicativos bem populares que a utilizam. Ontem, em meu primeiro teste com a plataforma, eu consegui milhares de impressões do meu anúncio em poucas horas. Infelizmente eu não tinha tappix o sufuciente para conseguir muitos cliques, mas aí entra outro fator interessante sobre a plataforma: a cada pessoa indicada por você que passar a usar a plataforma você ganha 50.000 tappix.

Ionic

O Tappx possui integração tanto para Android quanto para iOS, então se você desenvolve nativo é só baixar a SDK deles e ser feliz. No entanto, também é possível integrar com outras plataformas como Unity, Firefox OS e Cordova.

O meu novo aplicativo (Mini Receitas) foi desenvolvido em Cordova/Ionic e eu realizei a integração através do plugin Cordova Admob que inclusive já possui um módulo AngularJS para integração com aplicativos Ionic. Nesse plugin você pode configurar tanto o AdMob quanto o Tappx e ainda dividir os anúncios exibidos em seu aplicativo entre as duas plataformas.

  1. Para instalar o plugin no Ionic basta rodar o comando:
ionic plugin add cordova-admob
  1. Adicionar o seguinte código em seu index.html (note que você não precisa copiar nenhum arquivo, o plugin copiará os arquivos automaticamente na fase prepare do Cordova):
<script src="lib/angular-admob/angular-admob.js"></script>
  1. Injetar o módulo na definição do seu aplicativo:
var app = angular.module('myApp', ['admobModule']);
  1. Configurar o plugin com as suas informações do AdMob e do Tappx:
app.config(['admobSvcProvider', function (admobSvcProvider) {
    admobSvcProvider.setOptions({
        publisherId:          "ca-app-pub-XXXXXXXXXXXXXXXX/BBBBBBBBBB",  // Obrigatório
        interstitialAdId:     "ca-app-pub-XXXXXXXXXXXXXXXX/IIIIIIIIII",  // Opcional
        tappxIdiOs:           "/XXXXXXXXX/Pub-XXXX-iOS-IIII",            // Opcional
        tappxIdAndroid:       "/XXXXXXXXX/Pub-XXXX-Android-AAAA",        // Opcional
        tappxShare:           0.5                                        // Opcional
    });
}]);
  1. Exibir os anúncios (você pode fazer isso em um controller específico também):
app.run(['admobSvc', function (admobSvc) {
    admobSvc.createBannerView();
}]);

E é isso. Em um próximo post espero poder falar mais sobre ASO (App Store Optimization), ainda estou estudando e aplicando aos poucos os conhecimentos adquiridos. O meu novo aplicativo (Mini Receitas) ainda não tem muitos downloads, mas com uma plataforma de promoção gratuita as chances de eu atingir meus objetivos ao final do ano aumentam e espero que vocês também possam vir a se beneficiar da plataforma.