Como criar um aplicativo Android com câmera (Android 6+)

google-camera-interface-nexus-6p-android-n-840x473

Atualizado em 19/07/17 com suporte a Android 6+

No post de hoje vou ensinar como criar um app Android onde você pode “invocar” o recurso de câmera do smartphone e depois ter acesso à foto retirada para fazer o que quiser com ela. Tenho certeza que lhe pode ser útil para algum projeto que esteja fazendo ou que vá fazer um dia.

Se você nunca fez um app antes, sugiro dar uma olhada primeiro neste tutorial que cobre todo o básico!

Há alguns anos atrás eu desenvolvi um app para uma campanha publicitária da LG, para o lançamento de um novo modelo de smartphone, acho que era o L2 ou algo assim. Esse app se chamava “Bem na Foto” e foi solicitado pela LG para ser usado nos pontos de venda visando mostrar como a câmera desse aparelho novo era boa. Ele era bem simples: o usuário abria o app, tirava uma foto, escolhia uma moldura para colocar a foto dentro e publicava nas redes sociais. Mesmo simples, me rendeu bastante dinheiro para um projeto que levei poucos dias para deixar 100%.

Vamos ver nesse post:

A tela inicial

Na tela inicial não teremos nada de especial, exceto um botão para tirar uma nova foto, sendo assim, quando você for criar seu projeto, escolha o template Basic Activity (o mesmo que usamos neste tutorial de CRUD) que já vem com um FloatingActionButton e apenas adicione uma ImageView que exibirá a foto tirada, dentro do content_main.xml:

Note que eu não escrevi todos esses código aí em cima, apenas arrastei o ImageView pro layout e usei o recurso Infer Constraints do ContraintLayout. Já no activity_main.xml, apenas mude o ícone do FloatingActionButton para ser uma câmera ao invés do envelope tradicional, como fiz no código abaixo:

O seu layout deve se parecer com esse (a ImageView está transparente pois não tiramos foto ainda):

Layout do app
Layout do app

Na classe Java que vai responder por esta tela, chamada de MainActivity, existe um código pronto para o clique do FloatingActionButton, que por padrão apenas exibe uma SnackBar, mude-o para que chame o método correto, conforme abaixo:

Note que neste código chamamos um método getPermissions(); que ainda não existe e que teremos de criar no próximo passo.

Configurando permissões

Neste tutorial usaremos os recursos de câmera e de armazenamento do smartphone, logo, precisamos que o Android nos forneça tais permissões. Além disso, precisamos exigir que este app somente possa ser instalado em dispositivos com câmera. O trecho de código abaixo deve ser colocado no arquivo AndroidManifest.xml, logo acima da tag application:

Até a versão 5 do Android isso é o suficiente para garantir que teremos as permissões necessárias. No entanto, a partir da versão 6 o controle de permissões mudou e agora devemos também solicitar via código, no momento em que vamos usar o recurso em questão.

Para tanto, vamos criar o método getPermissions() como abaixo (bem parecido com o que usamos no tutorial de app com GPS) e o método onRequestPermissionsResult() que será disparado automaticamente quando o usuário tiver interagido com o dialog de sistema que pedirá a permissão para ele:

Após o usuário fornecer ou não permissão ao aplicativo, ele irá disparar o evento onRequestPermissionsResult, também exibido no código anterior, que dispara o método dispatchTakePictureIntent() se tiver as permissões de uso da câmera e do SD ou um alerta, caso contrário. Da mesma forma, em usos futuros do app, já com a permissão concedida, esse passo não acontece mais, como mostrado no else do método getPermissions().

Note que este método dispatchTakePictureIntent() não existe ainda, criaremos no próximo passo.

Chamando a câmera

Para invocar a câmera do dispositivo nós temos que criar um método com a intenção de usar a câmera que, quando a foto for tirada, irá retornar à activity de origem, salvar a foto e exibir a mesma.

Ufa, é um trabalho e tanto!

Temos dois métodos no exemplo de código acima, o dispatchTakePictureIntent que inicializa a câmera, e salva a foto tirada no cartão SD e o evento onActivityResult que é disparado automaticamente quando a foto termina de ser tirada para exibi-la no ImageView.

Quando for mandar rodar no simulador, lembre-se de se certificar se ele possui hardware de câmera na tela de configurações dele (caso não possua, edite-o). Na imagem abaixo você confere a foto tirada, usando o modo “emulated” de câmera no simulador do Android.

Foto tirada
Foto tirada

Na sequência você pode colocar botões nessa tela para editar a imagem, por exemplo, ou para exclui-la. Com alguns códigos Java você pode publicá-la em redes sociais ou mesmo envia-la por email.

Mas isso fica para a sua criatividade ou para posts futuros, não é mesmo?! 😉

* OBS: curtiu o post? Então dá uma olhada no meu livro de Android clicando no banner abaixo pra aprender a criar outros tantos apps incríveis!

Criando apps para empresas com Android

Por que Stanford trocou Java por JavaScript?

Antes que você comece a ler este artigo, eu gostaria de dizer que este é um artigo de perguntas, não de respostas. Que este é um artigo sobre educação, não sobre por que a linguagem X é melhor que a Y. Sei que talvez você queira desistir de ler ele agora, afinal, não estamos acostumados a nos preocupar com educação neste país, e nossa geração não deseja perguntas complicadas, mas sim respostas prontas.

Sem julgamentos. Pode ir.

Não estou escrevendo este artigo para ganhar cliques ou vender meus livros. Estou escrevendo o mesmo por algo muito maior do que interesses financeiros, pelo futuro que estamos construindo hoje. Lendo você vai entender o que quero dizer.

Palestrando no JS Experience
Palestrando no JS Experience

Eu + Tecnologia + Educação

Trabalho com TI desde 2006, quase sempre como desenvolvedor de software em linguagens de programação bem tradicionais e “corporativas” como Java e C#. Tenho graduação e pós-graduação na área e tive a oportunidade de trabalhar em várias empresas e dezenas de projetos de todos tamanhos e plataformas, sempre com Java e C#.

Em 2010 passei a lecionar cursos livres e mais tarde disciplinas de graduação em faculdades da Grande Porto Alegre. Oficialmente, com carteira assinada, sou professor do ensino superior há quatro anos em uma grande rede de educação do RS, a QI. Como a maioria das faculdades, ensinamos a linguagem Java aos nossos alunos e, ocasionalmente, a linguagem C# a pedido deles.

Posso dizer tranquilamente que estas duas tecnologias, Java e C#, moldaram toda minha carreira até hoje, sendo que durante muito tempo foquei todos os meus esforços em ser o melhor desenvolvedor possível nelas. Não ao mesmo tempo, mas em momentos distintos da minha vida profissional.

Então por que venho falar hoje de JavaScript ao invés de Java? E por que cito Stanford no título deste artigo?

Uma coisa de cada vez. Primeiro vamos começar por Stanford.

Stanford
Stanford

Stanford University

Certamente você já deve ter ouvido falar de Stanford, mas vou frisar alguns pontos aqui antes de continuarmos.

Segundo a Wikipedia, Stanford é uma das instituições de ensino mais prestigiadas do mundo, com a maior seletividade de graduação e a posição de primeira colocada em várias pesquisas e medições nos Estados Unidos. Mesmo que você não acredite na veracidade de alguma dessas pesquisas, em TODAS elas Stanford se encontra entre as Top 5 dos EUA.

Ela foi fundada em 1891 e fica em Palo Alto, na Califórnia, na região conhecida como Vale do Silício, um dos maiores pólos de empreendedorismo e inovação tecnológica mundialmente falando. Na verdade sessenta anos antes de surgir o Vale do Silício já existia Stanford então certamente ela teve uma importância muito grande na região, principalmente considerando que grandes nomes como Google, HP, Yahoo!, Sun e Nike nasceram dentro das dependências dessa faculdade. Estima-se que, somando, essas empresas tenham mais de U$2.7 trilhões de faturamento hoje.

Anualmente mais de 19.000 estudantes se inscrevem para o processo seletivo mas apenas 12% conseguem entrar, sendo que destes 12% apenas 5% são estrangeiros, ou 0.6% do total.

Mas por que estou falando de Stanford? E que relação ela tem com JavaScript?

Calma, já vou chegar lá!

Stanford e o JavaScript

O estopim deste artigo foi uma notícia que li em abril desse ano em um site britânico que dizia em seu título (em tradução livre): “A disciplina de Introdução à Ciência da Computação da Universidade Stanford adota JavaScript, ao invés de Java”. O artigo original pode ser lido neste link, para que você tire suas próprias conclusões.

Eu fiz faculdade de Ciência da Computação e em um primeiro momento fiquei ultrajado com a notícia. Fui atrás de mais informações pra confirmar isso. E era verdade, como confirma este artigo do jornal estudantil Stanford Daily e no próprio portal que permite a matrícula nas disciplinas.

Como a “fase de negação” demora a terminar quando somos pegos de surpresa por uma informação que nos tira da zona de conforto, eu queria saber quem que autorizou isso. Eu jurava que era algum professor novato que havia entrado “ontem” em Stanford e estava querendo bagunçar tudo. Mas não era.

PhD Eric Roberts
PhD Eric Roberts

O doutor Eric Roberts (que recebeu seu PhD em 1980, quando eu nem era nascido ainda) é um professor emérito de Stanford que atua na universidade desde 1990, foi ele quem tomou esta decisão. E não, ele nunca foi um fã de JavaScript (assim como eu não sou), tendo inclusive publicado livros sobre C, C++ e Java ao longo de suas décadas lecionando, muitos dos quais são usados como referência nas disciplinas de Ciência da Computação de Stanford.

Como que um professor de Ciência da Computação aprovaria a troca de Java como linguagem de introdução por JavaScript? Uma linguagem de altíssimo nível que abstrai boa parte da complexidade inerente de estudar programação? JavaScript nem mesmo é Orientado à Objetos!!! Note que esses foram exatamente os meus pensamentos à ocasião, mas como ser pensante que sou, decidi investigar quais seriam os motivos por trás dessa escolha para entender.

Afinal, universidades centenárias não trocam de currículo da noite para o dia ou por causa de alguma “modinha”.

E, de fato, depois de estudar mas a fundo, não está parecendo que esse boom do JavaScript é uma “modinha”.

O fenômeno JavaScript

Tentando entender a escolha do dr. Roberts, fui refletir sobre o JavaScript na minha carreira e no mercado como um todo.

Eu programo um pouco de JavaScript desde 2007, quando comecei a mexer com tecnologias web. Mas só um pouco. Quase nada, apenas coisas auxiliares no front-end, que era praticamente todo renderizado e manipulado no back-end.

Ali por 2010 eu comecei a mexer mais a fundo quando descobri o poder e a praticidade do JQuery. Comecei a adicionar cada vez mais JavaScript aos meus projetos ao ponto de começar a escrever front-ends completamente em JavaScript, que consumiam micro-serviços escritos em uma linguagem de back-end por volta de 2012.

No entanto, ainda considerava JavaScript uma linguagem de “segunda-classe”, inferior se comparada a Java e C#, minhas favoritas. Mas isso começou a mudar em 2016 para mim.

Segundo o StackOverflow (2017), 62.5% da audiência deles programa em JavaScript, em algum grau. Ou dois a cada 3 programadores, aproximadamente.

Segundo o GitHub (2013), 21% dos repositórios criados na plataforma são de projetos escritos em JavaScript como principal tecnologia. Ou um em cada cinco projetos, a linguagem mais popular dentre todas na plataforma.

Nos últimos cinco anos aproximadamente, a linguagem JavaScript tem se tornado a escolha obrigatória para construção de front-ends fluidos, performáticos, dinâmicos, etc e, em muitas ocasiões graças ao Node.js, a escolha para o back-end também, criando o termo “full-stack JS”.

A programação back-end mundial, até poucos anos dominada pelo Java, aos poucos vem mostrando sinais de cansaço e JavaScript vem ganhando, aos poucos, cada vez mais força, enquanto o Java decai em todas os rankings possíveis (incluindo o Tiobe), como mostra a tendência de buscas por estas tecnologias no Google Trends:

Java vs JavaScript
Java vs JavaScript

Claro, boa parte da popularidade do JavaScript é como todos sabem: no front-end, como mostra a crescente (e recente) popularidade de frameworks como React e Angular nas pesquisas, mas também com a adoção em larga escala de Node.js (a plataforma de back-end que mais utilizo atualmente):

Angular, React e Node
Angular, React e Node

E se você acha que o volume de pesquisas do Google não nos permite tirar conclusões alguma, dê uma olhada na timeline de linguagens de programação utilizadas por Stanford na cadeira introdutória de Ciência da Computação desde 1990, quando o dr. Roberts entrou na instituição:

Programação em Stanford
Programação em Stanford

Agora compare com o Google Trends de 2004 para cá, buscando pelas mesmas linguagens (infelizmente o Google não disponibiliza dados antes de 2004, o que impossibilita de entender o quão populares eram C e Pascal na década de 90):

Linguagens ao longo do tempo
Linguagens ao longo do tempo

Note que Pascal já não é nada popular desde o início dos anos 2000, que C estagnou em popularidade ali por 2009 e que a diferença de popularidade entre Java e JavaScript nunca foi tão pequena quanto nos anos recentes.

As mudanças de linguagens geralmente acompanham tendências de ensino e de mercado/indústria e até mesmo feedbacks dos alunos e professores. E não são feitas da noite para o dia: em 1995 com o boom do Java, já cogitava-se usar a linguagem em Stanford, que começou a preparar terreno para fazer a troca, que só ocorreu em 2002, sete anos depois. No caso de JavaScript, faz cinco anos que estão reescrevendo todos os materiais das disciplinas e estudando a melhor forma de fazer essa transição, embora atualmente os alunos estão podendo optar entre Java e JavaScript (disciplinas CS106A e CS106J, respectivamente).

E quando falo de feedbacks, números como os abaixo preocupam as faculdades de computação no mundo inteiro (dados da faculdade onde trabalho, que pude confirmar com coordenadores de outras instituições gaúchas):

  • aproximadamente 40% dos calouros desistem do curso de computação após a primeira disciplina de programação da faculdade (geralmente chamada de Algoritmos e geralmente com essa taxa de reprovação também);
  • no semestre seguinte, na próxima cadeira de programação (Algoritmos II, Linguagens e Técnicas de Programação, etc), cerca de 25% desistem;
  • somente a partir do semestre seguinte (no segundo ano de faculdade) que as turmas costumam estabilizar e as perdas de alunos são reduzidas a níveis aceitáveis como 10%;

De longe posso perceber enquanto professor que programação é o assunto que mais assusta em cursos de computação e o que mais impede dos estudantes de avançarem no curso, como o feedbacks abaixo (do jornal de Stanford) demonstra (traduzido livremente):

“Essa cadeira destrói a sua vida. Mas você aprende muito com essa destruição.”

Já uma professora de Stanford diz que:

“Os estudantes acreditam que essa cadeira tem o intuito de ‘limpar’ a faculdade, mas isso não é verdade.”

Acredito que estes comentários estão associados à complexidade da disciplina introdutória para quem vem “cru” do ensino médio (ou high school americano) aliado à complexidade das linguagens usadas pelas faculdades para introduzir algoritmos para os alunos, como C e Java sendo as escolhas mais populares aqui no Brasil e no exterior.

Fazendo uma comparação bem boba, mas reveladora, você sabe como é um “olá mundo”, a primeira instrução que se aprende em qualquer linguagem, em Java?

E em C?

Agora compare com um Hello World em JavaScript, que faz exatamente a mesma coisa e me diga qual você acha que é mais fácil para um calouro iniciar no mundo da programação?

E não é apenas Stanford, o famoso MIT usa Python nas disciplinas introdutórias, uma linguagem tão simples e tão poderosa quanto JS (ou talvez até mais!).

Mas por que eu deveria me preocupar com isso?

Afinal, azar é de quem não consegue avançar no curso de computação, não é mesmo?

Mais ou menos. E já vou explicar o porquê.

Por que se preocupar?

Você consegue adivinhar o que é a foto abaixo?

Fila de...
Fila de…

Uma fila de pessoas desempregadas. Em tempos de crise não são nenhuma novidade não é?

Mas você sabe em que país foi tirada essa foto?

Não, não foi no Brasil. Foi na Espanha.

Segundo a Wikipedia, o índice de desemprego na Espanha está pior que que aqui no Brasil: 17% dos espanhóis estão sem emprego contra 14% dos brasileiros. Na Grécia o índice é ainda pior: 23%. E esse problema é crônico em quase toda Europa com Itália (12%), França (10%) e Portugal (10%) figurando no topo dessa lista também.

Ou seja: não são apenas países ferrados e subdesenvolvidos da América Latina como nós que estão com grandes problemas nessa área.

Ah, mas isso é culpa da crise global, logo, logo, tudo vai melhorar…

Só que não.

Uma pesquisa da Universidade Oxford em 2013 constatou que até 2038, 47% dos empregos americanos deixarão de existir por causa da “computadorização” ou substituição do trabalhador humano por computadores.

47% dos empregos vão sumir
47% dos empregos vão sumir

E não precisa ser nenhum gênio para entender que estamos rumo ao mesmo futuro, talvez não na mesma velocidade, mas vai acontecer por aqui também. E em um mundo computadorizado, quem é que vai sempre ter emprego garantido?

Os programadores? (Viva pra nós!)

Quase. Já explico.

Uma outra pesquisa, dessa vez da OCDE em 2016, diz que 65% das crianças de hoje vão trabalhar em carreiras que ainda não existem.

O que isso quer dizer?

Eu não sei. Mas tenho algumas ideias.

Em um mundo “computadorizado”, o artista plástico não vai criar esculturas com a mão, mas programando uma impressora 3D. O motorista (se ele ainda existir) vai programar a sua rota no caminhão e estará na boleia apenas para emergências e situações não previstas, provavelmente analisando relatórios de suas viagens e cargas. O médico vai programar braços robóticos da melhor maneira para aquela cirurgia em questão, sem a necessidade dele ficar 100% do tempo na sala de cirurgia.

Sei lá, estou viajando aqui, é possível que você tenha as suas próprias ideias de profissões do futuro.

Você já ouviu falar na frase “Programe ou seja programado?”. Ela é de 2010, do professor e autor Douglas Rushkoff e dá pra ser entendida pelo vídeo abaixo:

Rushkoff entendeu que quando uma ferramenta é tão poderosa a ponto de mudar o mundo, como os computadores são, ou você aprende a programá-los para participar da criação dessa nova realidade, ou viverá na realidade criada por outras pessoas.

E isso pode ser considerado a marginalização do futuro. Viver sem saber operar um computador (não da forma como conhecemos hoje, mas de maneira ubíqua e onipresente como está se transformando) de maneira eficiente pode não ser apenas uma questão de escolher trabalhar como um programador, mas de escolher trabalhar como qualquer coisa que tenha sido “computadorizada”.

Camila Achutti, uma mestra em computação, defende inclusive que deveríamos ensinar programação nas escolas, como sendo o analfabeto digital aquele que não sabe criar programas de computador, apenas usa, em analogia a quem não sabe ler e compreender um livro, apenas olha as figuras. Você pode entender mais do trabalho de Camila no vídeo abaixo de 2016:

Como era a vida das pessoas antes de saber ler e escrever se tornar algo comum? Quem dominava tais habilidades tinha uma vantagem enorme frente aos iletrados. O mesmo acontece nos dias de hoje e acontecerá ainda mais no futuro.

Camila defende inclusive que, no pior dos casos, entender como os programas funcionam, lhe dará mais domínio de como você os usa. Um exemplo muito simples é entender a diferença entre um site com HTTP e com HTTPS por exemplo. É entender a política de privacidade de uma rede social e o que acontece quando você compartilha uma postagem.

O caso é: quanto mais fechamos a programação em uma caixa-preta blindada e inacessível para os “não iniciados”, mais o mundo inteiro perde. Quanto mais exclusivos os programadores forem, pode-se até pensar em ganho pessoal a curto prazo, mas no longo prazo, só há o caos.

Eu como o pai de um menino de 2 anos, não posso deixar que isso aconteça, ao menos não na vida dele.

O que podemos fazer?

O movimento recente de Stanford é um sinal de que não devemos apenas manter os currículos dos nossos cursos atualizados, mas sim melhores.

Se eu acho que JavaScript melhor que Java? Depende do projeto. Depende do contexto.

Para introduzir a programação para crianças e adolescentes, ou até mesmo para qualquer pessoa que está tendo seu primeiro contato com programação em toda sua vida?

Possivelmente um JavaScript ou um Python podem ser melhores mesmo.

Note que Stanford não abandonou as demais linguagens de programação que são vistas ao longo do curso de Ciência da Computação (ao longo de cinco anos eu devo ter visto mais de cinco linguagens diferentes). Ela apenas trocou Java por JavaScript na cadeira introdutória, para fazer com que mais pessoas consigam pegar o “gosto pela coisa”, que algoritmos seja algo mais “digerível” para pessoas normais.

O objetivo aqui não é formar programadores mais “fracos” e jogá-los no mercado de trabalho, até porque o intuito de nenhum bacharelado é formar programadores, mas sim cientistas.

Outra prestigiada faculdade, que eu já mencionei nesse artigo, o MIT, tem um projeto muito interessante também chamado Scratch, que já possui mais de uma década de vida. É uma ferramenta visual que lhe permite criar algoritmos usando blocos, incluindo jogos completos e interessantes de maneira muito fácil. Esta ferramenta inclusive tem sido usada em escolas ao redor do mundo nos eventos chamados Hora do Código.

Outro projeto, novamente do MIT (eu já mencionei como admiro eles?) é o APP Inventor, uma ferramenta na nuvem para construção de apps de maneira muito simples e visual!

Mas e eu? E você? O que podemos fazer?

Eu só conheço uma forma de realmente mudar a vida das pessoas: ensinando-as.

Só a educação te dá o poder para mudar a tua própria vida.

Eu sei que muitos não querem virar professores para ensinar programação, e que muitos não querem virar programadores também. Minha sugestão não é essa.

A minha sugestão é que você, nós, que somos não apenas adeptos de tecnologia, mas entendedores da mesma, ajudemos o próximo. Ajude aquela sua tia que quer um site, a criar o próprio site dela no Wix. Ajude aquele seu primo que só sabe fazer site no Wix, a criar sites usando WordPress, que é mais poderoso. Explique para sua mãe porque ela não pode publicar dados pessoais em sites sem HTTPS. Mostre para o seu pai o risco de colocar a foto da placa do carro dele na Internet, cruzando essa informação com as do Detran e tendo acesso a todos os dados pessoais dele como CPF e endereço. Mostre pro pessoal do seu escritório como as tarefas repetitivas podem ser eliminadas, e até otimizadas, usando o Zapier.

Ajude as pessoas que você se importa a não serem meros usuários, mas que entendam o que estão fazendo. Caçoe menos das “tosquices” deles e ajude mais.

Agora, se a pessoa tiver um mínimo de inclinação para algoritmos, ajude-a mostrando uma plataforma como Node.js (que usa JavaScript) ou Python e as maravilhas que são possíveis de fazer com elas. Ensine-a como usar o Google para encontrar mais informações das mesmas, como organizar seus projetos usando um Trello, como ganhar dinheiro com isso fazendo freelas.

Não guarde esses conhecimentos só pra si. Não finja que os “outros não saberem não é problema seu”. Lembra o tio Ben do Peter Parker? Ele dizia que com “grandes poderes vem grandes responsabilidades”. Ele foi assassinado por alguém que o homem-aranha não quis deter com seus poderes.

Ok, é uma história em quadrinhos e talvez agora você comece a duvidar da minha sanidade, mas não é difícil traçar um paralelo com a vida real e todas as pessoas marginalizadas que entram para o crime pois não tiveram oportunidades. Não, eu não estou defendendo bandidos aqui, estou querendo evitar que novos surjam, simples assim.

Continuando e encerrando, o dr. Werner Vogels (CTO da Amazon) tem uma célebre frase que diz: “A tecnologia é inútil se não for para o bem maior de servir os clientes.”. Eu vou mais longe e digo que “a inteligência é inútil se não for para o bem maior de ajudar as pessoas.”.

Ou você acha que guardar todo o conhecimento que você tem, apenas para si, vai lhe trazer algum outro benefício exceto ser o cadáver mais inteligente do cemitério?

Entendendo o Node.js Event Loop

Continuando os estudos de Node.js me deparei com um elemento chave que não temos como ignorar quando o assunto é essa tecnologia. Estou falando do Event Loop.

Grande parte das características e principalmente das vantagens do Node.js se devem ao funcionamento do seu loop single-thread principal e como ele se relaciona com as demais partes do Node, como a biblioteca C++ libuv.

Assim, a ideia deste artigo é ajudar você a entender como o Event Loop do Node.js funciona, o que deve lhe ajudar a entender como tirar o máximo de proveito dele.

Vamos ver neste artigo:

O problema

Antes de entrar no Event Loop em si, vamos primeiro entender porque o Node.js possui um e qual o problema que ele propõe resolver.

A maioria dos backends por trás dos websites mais famosos não fazem computações complicadas. Nossos programas passam a maior parte do tempo lendo ou escrevendo no disco, ou melhor, esperando a sua vez de ler e escrever, uma vez que é um recurso lento e concorrido. Quando não estamos nesse processo de ir ao disco, estamos enviando ou recebendo bytes da rede, que é outro processo igualmente demorado. Ambos processos podemos resumir como operações de I/O (Input & Output) ou E/S (Entrada & Saída).

Processar dados, ou seja executar algoritmos, é estupidamente mais rápido do que qualquer operação de IO que você possa querer fazer. Mesmo se tivermos um SSD em nossa máquina com velocidades de leitura de 200-730 MB/s fará com que a leitura de 1KB de dados leve 1.4 micro-segundos. Parece rápido? Saiba que nesse tempo uma CPU de 2GHz consegue executar 28.000 instruções.

Isso mesmo. Ler um arquivo de 1KB demora tanto tempo quanto executar 28.000 instruções no processador. É muito lento.

Quando falamos de IO de rede é ainda pior. Faça um teste, abra o CMD e execute um ping no site do google.com, um dos mais rápidos do planeta:

A latência média nesse teste é de 44 milisegundos. Ou seja, enviar um ping para o Google demora o mesmo tempo que uma CPU necessita para executar 88 milhões de operações.

Ou seja, quando estamos fazendo uma chamada a um recurso na Internet, poderíamos estar fazendo cerca de 88 milhões de coisas diferentes na CPU.

É muita diferença!

A solução

A maioria dos sistemas operacionais lhe fornece mecanismos de programação assíncrona, o que permite que você mande executar tarefas concorrentes que não ficam esperando uma pela outra, desde que uma não precise do resultado da outra, é claro.

Esse tipo de comportamento pode ser alcançado de diversas maneiras. Atualmente a forma mais comum de fazer isso é através do uso de threads o que geralmente torna nosso código muito mais complexo. Por exemplo, ler um arquivo em Java é uma operação bloqueante, ou seja, seu programa não pode fazer mais exceto esperar a comunicação com a rede ou disco terminar. O que você pode fazer é iniciar uma thread diferente para fazer essa leitura e mandar ela avisar sua thread principal quando a leitura terminar.

Novas formas e programação assíncrona tem surgido com o uso de interfaces async como em Java e C#, mas isso ainda está evoluindo. Por ora isso é entediante, complicado, mas funciona. Mas e o Node? A característica de single-thread dele obviamente deveria representar um problema uma vez que ele só consegue executar uma tarefa de um usuário por vez, certo? Quase.

O Node usa um princípio semelhante ao da função setTimeout(func, x) do Javascript, onde a função passada como primeiro parâmetro é delegada para outra thread executar após x milisegundos, liberando a thread principal para continuar seu fluxo de execução. Mesmo que você defina x como 0, o que pode parecer algo inútil, isso é extremamente útil pois força a função a ser realizada em outra thread imediatamente.

Vamos falar melhor dessa solução na sequência.

Node.js Event Loop

Sempre que você chama uma função síncrona (i.e. “normal”) ela vai para uma “call stack” ou pilha de chamadas de funções com o seu endereço em memória, parâmetros e variáveis locais. Se a partir dessa função você chamar outra, esta nova função é empilhada em cima da anterior (não literalmente, mas a ideia é essa). Quando essa nova função termina, ela é removida da call stack e voltamos o fluxo da função anterior. Caso a nova função tenha retornado um valor, o mesmo é adicionado à função anterior na call stack.

Mas o que acontece quando chamamos algo como setTimeout, http.get, process.nextTick, ou fs.readFile? Estes não são recursos nativos do V8, mas estão disponíveis no Chrome WebApi e na C++ API no caso do Node.js.

Vamos dar uma olhada em uma aplicação Node.js comum – um servidor escutando em localhost:3000. Após receber a requisição, o servidor vai chamar wttr.in/ para obter informações do tempo e imprimir algumas mensagens no console e depois retorna a resposta HTTP.

O que será impresso quando uma requisição é enviada para localhost:3000?

Se você já mexeu um pouco com Node antes, não ficará surpreso com o resultado, pois mesmo que console.log(‘Obtendo a previsão do tempo, aguarde.’) tenha sido chamado depois de console.log(‘Previsão confirmada!’) no código, o resultado da requisição será como abaixo:

O que aconteceu? Mesmo o V8 sendo single-thread, a API C++ do Node não é. Isso significa que toda vez que o Node for solicitado para fazer uma operação bloqueante, Node irá chamar a libuv que executará concorrentemente com o Javascript em background. Uma vez que esta thread concorrente terminar ou jogar um erro, o callback fornecido será chamado com os parâmetros necessários.

A libuv é um biblioteca C++ open-source usada pelo Node em conjunto com o V8 para gerenciar o pool de threads que executa as operações concorrentes ao Event Loop single-thread do Node. Ela cuida da criação e destruição de threads, semáforos e outras “magias” que são necessárias para que as tarefas assíncronas funcionem corretamente. Essa biblioteca foi originalmente escrita para o Node, mas atualmente outros projetos a usam também.

Task/Event/Message Queue

Javascript é uma linguagem single-thread orientada a eventos. Isto significa que você pode anexar gatilhos ou listeners aos eventos e quando o respectivo evento acontece, o listener executa o callback que foi fornecido.

Toda vez que você chama setTimeout, http.get ou fs.readFile, Node.js envia estas operações para a libuv executá-las em uma thread separada do pool, permitindo que o V8 continue executando o código na thread principal. Quando a tarefa termina e a libuv avisa o Node disso, o Node dispara o callback da referida operação.

No entanto, considerando que só temos uma thread principal e uma call stack principal, onde que os callbacks ficam guardados para serem executados? Na Event/Task/Message Queue, ou o nome que você preferir. O nome ‘event loop’ se dá à esse ciclo de eventos que acontece infinitamente enquanto há callbacks e eventos a serem processados na aplicação.

Em nosso exemplo anterior, de previsão do tempo, nosso event loop ficou assim:

  1. Express registrou um handler para o evento ‘request’ que será chamado quando uma requisição chegar em ‘/’
  2. ele começar a escutar na porta 3000
  3. a stack está vazia, esperando pelo evento ‘request’ disparar
  4. quando a requisição chega, o evento dispara e o Express chama o handler configurado: sendWeatherOfRandomCity
  5. sendWeatherOfRandomCity é empilhado na call stack
  6. getWeatherOfRandomCity é chamado dentro da função anterior e é também empilhado na call stack
  7. Math.floor e Math.random são chamadas, empilhadas e logo desempilhadas, retornando uma cidade à variável city
  8. superagent.get é chamado com o parâmetro ‘wttr.in/${city}’ e definimos o handler/callback para o evento de término da requisição.
  9. a requisição HTTP para http://wttr.in/${city} é enviada para uma thread em background e a execução continua
  10. ‘Obtendo a previsão do tempo, aguarde.’é impresso no console e getWeatherOfRandomCity retorna
  11. sayHi é chamada, ‘Hi’ é impresso no console
  12. sendWeatherOfRandomCity retorna, é retirado da call stack, deixando-a vazia
  13. ficamos esperando pela chamada de http://wttr.in/${city} nos responder
  14. uma vez que a resposta chegue, o evento de ‘end’ é disparado
  15. o handler anônimo que passamos para .end() é chamado, é colocado na call stack com todos as variáveis locais, o que significa que ele pode ver e modificar os valores de express, superagent, app, CITIES, request, response, city e todas funções que definimos
  16. response.send() é chamado com um status code de 200 ou 500, mas isso também é executado em uma thread do pool para a stream de respostas não fique bloqueada e o handler anônimo é retirado da pilha.

E é assim que tudo funciona!

Vale salientar que por padrão o pool de threads da libuv inicia com 4 threads concorrentes e que isso pode ser configurado conforme a sua necessidade.

Microtasks e Macrotasks

Além disso, como se não fosse o bastante, nós temos duas task queues, não apenas uma. Uma task queue para microtasks e outra para macrotasks.

Exemplos de microtasks

  • process.nextTick
  • promises
  • Object.observe

Exemplos de macrotasks

  • setTimeout
  • setInterval
  • setImmediate
  • I/O

Para mostrar isso na prática, vamos dar uma olhada no seguinte código:

a saída no console é:

De acordo com a especificação da WHATWG, uma macrotask deve ser processada da macrotask queue em um ciclo do event loop. Depois que essa macrotask terminar, todas as microtasks existentes são processadas dentro do mesmo ciclo. Se durante este processamento de microtasks novas microtasks surgirem, elas são processadas também, até a microtask queue ficar vazia.

Este diagrama do site Rising Stack ilustra bem o event loop completo:

Em nosso caso:

Ciclo 1:

  1. setInterval é agendado como (macro)task
  2. setTimeout 1 é agendado como task
  3. em Promise.resolve 1 ambos thens são agendados como microtasks
  4. a call stack está vazia e as microtasks executam

Task queue: setInterval, setTimeout 1

Ciclo 2:

  1. a microtask queue está vazia, logo o handler setInterval pode executar;
  2. outro setInterval é agendado como task, logo atrás de setTimeout 1

Task queue: setTimeout 1, setInterval

Ciclo 3:

  1. a microtask queue está vazia, logo o handler setTimeout 1 pode executar;
  2. promise 3promise 4 são agendadas como microtasks;
  3. handlers de promise 3promise 4 são executados
  4. setTimeout 2 é agendado como task

Task queue: setInterval, setTimeout 2

Ciclo 4:

  1. a microtask queue está vazia; logo o handler de setInteval pode executar;
  2. outro setInterval é agendado como task, logo atrás de setTimeout

Task queue: setTimeout 2, setInterval

Ciclo 5:

  1. o handler setTimeout 2 executa;
  2. promise 5promise 6 são agendadas como microtasks;
  3. os handlers de promise 5 e promise 6 executam encerrando o programa.

Nota: isso funciona perfeitamente bem no Google Chrome, mas por questões que fogem da minha compreensão, não é regra em todos ambientes de execução. Existem modificações que podemos fazer no código para que o comportamento seja o mesmo em todos ambientes, mas deixam o código terrivelmente feio (i.e. callback hell).

Conclusões

Como você pôde ver, se quisermos ter total controle de nossas aplicações Node.js devemos prestar atenção nestes detalhes de como as tarefas são executadas dentro do event loop, principalmente para não bloquearmos sua execução sem querer.

O conceito do event loop pode ser um tanto complicado no início mas uma vez que você entender sue funcionamento na prática você não conseguirá mais imaginar sua vida sem ele. Obviamente o uso inicial intenso de callbacks é muito chato de gerenciar mas já é possível usar Promises em nossos códigos Javascript que permitem deixar as tarefas assíncronas mais inteligíveis e em breve devemos ter acesso ao recurso async-await com o ES7.

Uma última dica é que você também pode enviar seus processamentos longos (mas que não são operações de IO), que normalmente seriam executados na thread principal para as threads em background usando bibliotecas como async.js.

Recomendo agora colocar a mão na massa com esse tutorial de Node.js com MongoDB que preparei pra você!