Um guia excessivamente completo para bibliotecas Android subutilizadas
Publicados: 2022-03-11Qualquer desenvolvedor experiente lhe dirá que seu melhor código não é o código que eles escreveram. É um código que eles tiraram do trabalho de outra pessoa.
Sim, nós desenvolvedores somos solucionadores de problemas inovadores, mas muitos dos problemas que encontramos já foram resolvidos – e os remédios empacotados em bibliotecas estão disponíveis para qualquer pessoa. Por que reinventar a roda quando as rodas livres estão por toda parte?
O Android não é exceção. A fonte definitiva para reutilização de código é o próprio Android SDK, que vem com ótimas construções e serviços que farão muito do seu trabalho para você.
Mas onde o SDK é curto, a comunidade Android criou algumas bibliotecas de primeira linha que podem economizar muito trabalho de codificação, substituindo-as por implementações altamente ajustadas, examinadas e testadas. Não estou falando das bibliotecas óbvias — Biblioteca de Suporte do Android, Biblioteca de Suporte ao Projeto do Android, Gson. Estou me referindo a ferramentas que você pode não conhecer. E mesmo que o faça, provavelmente ainda não os está usando.
Venho desenvolvendo, orientando e liderando equipes do Android há anos, e estudei e usei dezenas de ferramentas e bibliotecas externas. (Eu até sou conhecido por ler seu código de implementação e discutir seus detalhes internos com o desenvolvedor.) Muitos foram altamente eficazes em me ajudar a fazer o trabalho, mas a verdade é que a maioria não foi.
Por isso montei este guia. Conte com minha experiência, bem como a de outros desenvolvedores móveis, para garantir que você esteja usando as melhores bibliotecas. Eu escolhi sete. Eu suspeito que eles serão alguns de seus favoritos muito em breve também.
Selecionando a biblioteca Android correta
Ao escolher uma biblioteca, procuro quatro recursos principais:
- Ele fornece uma solução consistente e de alta qualidade para um problema real e não trivial.
- Ele usa uma API tão simples quanto possível.
- Ele não força nenhuma mudança na minha arquitetura geral.
- Possui uma grande base de usuários e, preferencialmente, uma comunidade ativa de desenvolvedores.
Os três primeiros recursos são disjuntores. Se eles não estiverem presentes, eu sigo em frente ou começo a codificar manualmente.
As bibliotecas que abordo abaixo passam em todos os quatro testes. Eles também resolvem alguns dos aspectos mais desafiadores do desenvolvimento móvel.
- Duas bibliotecas para injeção de dependência, vinculação de layout para Java, objetos simulados.
- Modelo de mensagens de publicação/assinatura no aplicativo.
- Camada de comunicação HTTP segura, eficiente e autorrecuperável.
- Manipulação de imagem: download, armazenamento em cache, redimensionamento e carregamento na RAM.
- Transmissão de vídeo em tempo real.
- Detecção de vazamento de memória.
ButterKnife: a ferramenta definitiva de injeção de dependência
Esta é a melhor biblioteca de injeção de dependência para Android. Simples, robusto, super rápido (sem reflexão!) e capaz de eliminar muito do código clichê do seu aplicativo.
Foi-se a necessidade de vincular diretamente cada uma de suas visualizações por meio de uma chamada para findViewById()
; em vez disso, há uma visualização anotada que fornece acesso direto ao código. O ButterKnife também elimina a necessidade de eventos de interface do usuário padronizados, como onClick
, onTouch
e assim por diante, e os substitui por código auto-injetado.
Mas chega de conversa, vamos ver o código.
Ver vinculação de campo:
class MyButterKnifeActivity extends Activity { @BindView(R.id.name) TextView name; @BindView(R.id.address) TextView address; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_activity); ButterKnife.bind(this); // MUST BE CALLED BEFORE ACCESSING UI FIELDS name.setText(“etc etc”); } }
Vinculação de recursos:
class ExampleActivity extends Activity { @BindString(R.string.username) String username; @BindDrawable(R.drawable.graphic) Drawable graphic; @BindColor(R.color.bg_color) int bgColor; @BindDimen(R.dimen.lower_padding) Float lowerPadding; // and no need for getResources().getString()/getDrawable/getColor() }
Vinculação de evento da interface do usuário:
@OnClick(R.id.my_button) public void clickHandler(View view) { // onClick logic goes here }
AndroidAnnotations: levando a injeção de dependência para o próximo nível
Um segundo próximo ao ButterKnife quando se trata de injeção de dependência, o AndroidAnnotations usa uma abordagem um pouco diferente: classes geradas automaticamente, que, uma vez que você pega o jeito, são extremamente simples. Ainda mais útil é que permite a injeção de dependência “baseada em nome”. Por exemplo, @ViewById ListView myUserList;
instrui a biblioteca a atribuir este campo com um layoutListView
com o mesmo nome.
O AndroidAnnotations também é incrivelmente rápido, mas consegue isso de uma maneira um pouco diferente do ButterKnife. Em vez de injeção de dependência de vinculação em tempo de execução, o AndroidAnnotations cria uma duplicação de tempo de compilação de todas as atividades afetadas e envia sua lógica de conexão para elas, permitindo que você obtenha o mesmo desempenho que obteria com a lógica codificada manualmente.
Mas os recursos de injeção do AndroidAnnotations vão ainda mais longe. Você pode injetar estado e layout em uma atividade.
Implementação do AndroidAnnotations:
@NoTitle @Fullscreen @EActivity(R.layout.my_layout) public class MyActivity extends Activity { @ViewById ListView customerList; // auto-binded to R.id.customerList @App MyApplication app; // auto-binded to app object @AminationRes Animation fadeoutAnimation; @UiThread void updateUI() { // main thread action } }
A última anotação requer um pouco mais de explicação: uma tarefa comum para um aplicativo Android multithread é alternar de threads em segundo plano (ou de trabalho) para o thread de encaminhamento (ou principal ou de interface do usuário), que é o único que permite acesso aos componentes da interface do usuário . Essa tarefa, embora não seja complexa, geralmente é necessária e envolve alguma codificação confusa:
new Handler(Looper.getMainLooper()).post(new Runnable() { logic goes here } ); // NO ANNOTATIONS
No AndroidAnnotations, tudo o que você precisa fazer é anotar sua função com @UiThread, e agora é garantido que sempre será executado:
@UiThread void updateUI() {..} // WITH ANNOTATIONS
Observe que esta anotação se aplica a classes de componentes padrão do Android (atividades, serviços e assim por diante). Mas o que acontece quando eu também quero anotar minhas próprias aulas?
Aqui, o AndroidAnnotations apresenta um novo conceito, o do EBean
. Tudo o que você precisa fazer é marcar sua classe como tal usando @EBean
e pronto:
@EBean public class MyNonComponentClass { @SystemService NotificationManager notifManager; @Bean MyOtherClass dependency; @UiThread void updateUI() { // main thread work goes here } }
EventBus: Comunicação entre componentes facilitada
A biblioteca EventBus transforma um problema que assombra os desenvolvedores do Android há anos em um passeio no parque. A comunicação entre componentes nunca foi tão simples - use um modelo simples de pub/sub para se comunicar entre duas partes do seu sistema.
Seu serviço de pesquisa em segundo plano não precisa mais estar ciente de seus fragmentos para alimentá-los com eventos de alteração.
O uso do EventBus é direto.
uma. Crie classes de eventos. Trabalhar com POJOs aqui é melhor:
class NewUserEvent { String fullname; String address; String role; // add getters and setters }
b. Crie métodos de manipulação de eventos na classe — qualquer classe que você deseja assinar para esses eventos:
class MySubscriber { @Subscribe public void newUserHandler(NewUserEvent event) { // handle NewUserEvent } @Subscribe public void newUserHandler(AnotherEvent event) { // handle AnotherEvent } }
Mas ei, qualquer desenvolvedor Android meio experiente pararia e perguntaria neste ponto: Qual é o modelo de segmentação desses manipuladores? E posso forçar um manipulador a executar o thread principal se, digamos, envolver o acesso ao componente da interface do usuário? Boa pergunta…
Por padrão, todos os métodos do manipulador são executados em um thread de trabalho obtido de um pool de threads alocado e mantido pelo próprio EventBus. Se você precisar que um método de manipulador seja executado no encadeamento principal, expanda suas anotações de assinatura da seguinte maneira:
@Subscribe(threadMode = ThreadMode.MAIN) public void runOnMainThreadHandler(AnotherEvent event) { … }
Aviso: Não abuse desse recurso! Operações de longa duração nunca devem ser executadas no thread principal e, mesmo com operações rápidas, tenha cuidado. Sobrecarregar o tópico principal é a maneira mais segura de tornar seu aplicativo lento, agitado e basicamente menos divertido para seus usuários.
c. Gerencie o ciclo de vida de registro do EventBus de sua classe de assinante, ou seja, quando ele se conecta e quando se desconecta do barramento? Um fluxo de registro razoável para uma atividade seria:
class MySubscriberActivity extends Activity { @Override public void onStart() { super.onStart(); EventBus.getDefault().register(this); // START RECEIVING EVENTS HERE } @Override public void onStop() { EventBus.getDefault().unregister(this); // NO MORE EVENTS super.onStop(); } }
O acima é, obviamente, apenas um exemplo. Você pode realizar o (des)registro em qualquer lugar que você escolher.
d. E, finalmente, realmente dispare um evento:
EventBus.getDefault().post(new MyEvent(“I'm here”));
Há muito mais para saber sobre o uso do EventBus: eventos de multicast (o comportamento padrão), usando eventos fixos, threads de entrega, prioridades e muito mais. Mas o acima é suficiente para você começar com esta tecnologia simples, mas poderosa.
OkHttp: HttpClient do Android em esteróides
É assim que o HttpClient do Android deveria ter sido escrito. Muito simples, muito inteligente. A biblioteca OkHttp cuida internamente do loop de repetição, compactação automática de carga útil, suporte a Http/2, pool de conexão e cache de resposta para que você evite acesso desnecessário à rede.
O uso do OkHttp é um acéfalo.
Http POST:
OkHttpClient client = new OkHttpClient(); MediaType JSON = MediaType.parse("application/json; charset=utf-8"); RequestBody body = RequestBody.create(JSON, json_str); Request request = new Request.Builder() .url(url) .post(body) .build(); Response response = client.newCall(request).execute(); return response.body().string();
Http OBTER:
OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url(urls[0]) .build(); Response responses = client.newCall(request).execute(); String jsonData = responses.body().string();
O OkHttp também suporta recursos úteis como rede assíncrona, consulta de rota de redirecionamento de solicitação, consulta de cache local e muito mais. Sinta-se à vontade para usá-los quando necessário. A maioria dos desenvolvedores usa o OkHttp como um substituto mais inteligente para o cliente HTTP padrão do Android, HttpURLConnection. Na verdade, todo esse projeto começou como um fork privado para HttpURLConnection.
Adoro sua robustez — ela adiciona imediatamente à sua camada de rede.
Picasso: Há uma boa razão para o Google usá-lo também!
O Picasso é a maneira mais simples e robusta de gerenciar download, armazenamento em cache, redimensionamento e corte de imagens.
Esta afirmação:
Picasso.with(context).load(url).resize(50,50).centerCrop().into(imageView)
Fará isso por você:
- Conecte-se a um URL remoto.
- Baixe uma imagem.
- Armazene-o em um cache LRU local que também gerenciará para você.
- Redimensione a imagem original antes de carregá-la na memória.
- Execute todos os itens acima em um pool de threads gerenciado por Picasso.
- Use a imagem redimensionada para preencher seu imageView.
- Antes de qualquer execução futura, verifique o cache local para garantir que uma viagem de ida e volta da rede seja realmente necessária.
Construir o conjunto de tarefas acima exigiria muitas horas de trabalho, mesmo para um desenvolvedor mestre. E isso pressupõe que você se lembrou de tudo. E se você esqueceu, digamos, a parte de redimensionamento?

Bem, em um dispositivo Android médio, um aplicativo não recebe mais de 50 a 60 megabytes de RAM, e o fator pixels por bytes para a maioria dos dispositivos Android é 4. Isso significa tentar carregar uma imagem de 13 megapixels do cartão SD exigiria 52 Megabytes de RAM. Em outras palavras, seu aplicativo travaria imediatamente.
Este é apenas um exemplo da força de Picasso. Uma das primeiras coisas que faço ao otimizar/depurar um projeto legado com uso intensivo de mídia é alternar todo o carregamento de imagens para Picasso. Você ficaria surpreso com o impacto que essa simples etapa tem na qualidade do aplicativo.
Um dos testemunhos mais fortes do poder dessa biblioteca: muitos dos exemplos de código Android do Google dos últimos dois anos empregam Picasso para carregar imagens.
ActiveAndroid: ORM sem sobrecarga de desempenho
ORM, abreviação de mapeamento relacional de objetos, tornou-se popular nos dias do J2EE. Ele permite que você armazene seus POJOs e os recupere de um banco de dados sem precisar convertê-los em campos separados.
É útil? Muito, pois permite que você escreva uma grande parte do seu aplicativo sem codificar nenhuma instrução SQL.
Também é muito eficiente. Antigamente, as plataformas ORM dependiam massivamente da reflexão e eram notórias por serem lentas. As plataformas modernas, incluindo o ActiveAndroid, são muito mais rápidas e, para a maioria dos requisitos práticos, não sofrerão sobrecargas de desempenho sobre a codificação SQL bruta.
Uso:
uma. Inicialize no objeto de aplicativo estendendo uma classe de aplicativo personalizada:
public class MyApplication extends extends com.activeandroid.app.Application { … }
b. Crie POJO, derivado de uma classe de modelo, com classes para cada um dos registros que você planeja armazenar no banco de dados. Cada um desses POJO pode residir em sua própria tabela. As anotações devem ser usadas para especificar o nome dos campos do banco de dados para cada membro armazenado:
@Table(name = "Categories") public class UserDetails extends Model { @Column(name = "Name") public String name; @Column(name = "Address") public String address; @Column(name = "Age") public int age; }
Se você deseja definir um índice para um membro, use a seguinte anotação:
@Column(name = "ID", index = true) public String userID;
c. Para evitar que a biblioteca itere durante todo o seu tempo de inicialização mais elegante, que é o comportamento padrão, é altamente recomendável que você especifique todas as suas classes de modelo na seguinte seção de manifesto:
<meta-data android:name="AA_MODELS" android:value=“com.myapp.MyModelA, com.myapp.MyModelB" />
Nota: As classes de modelo que não aparecem nesta lista não serão reconhecidas pelo ActiveAndroid.
d. Gravar no banco de dados:
UserDetails usr = new UserDetails(); usr.save(); // RUNS ON A BACKGROUND THREAD
Se várias gravações forem necessárias, uma maneira mais eficiente seria agrupá-las em uma única transação:
ActiveAndroid.beginTransaction(); try { for (UserDetails u: userList) item.save(); ActiveAndroid.setTransactionSuccessful(); } finally { ActiveAndroid.endTransaction(); }
e. Leia POJOs do banco de dados:
new Select() .from(UserDetails.class) .where("name = ?", usr.getName()) .orderBy("Age") .executeSingle();
ORM era uma ferramenta obrigatória durante meus dias como desenvolvedor do lado do servidor. Ele teve uma entrada um tanto tardia no domínio do Android. Mas, finalmente, aqui está: programação de banco de dados tão simples quanto possível. Aproveite.
LibStreaming: streaming de vídeo indolor
O streaming de vídeo em tempo real costumava ser um grande problema devido a APIs não documentadas, diferenças de versão entre SDKs, uso de reflexão e muito mais.
Felizmente, o libStreaming mudou tudo isso encapsulando a maioria das complexidades de streaming e expondo uma API simples e amigável que permite escrever um aplicativo de streaming básico em questão de horas.
Para usá-lo para H.264 e AAC, você precisa fazer o seguinte:
uma. Inicialize um objeto de sessão no método onCreate da sua atividade principal. Objetos de sessão representam streaming de mídia para um peer:
protected void onCreate(Bundle savedInstanceState) { mSession = SessionBuilder.getInstance() .setCallback(this) .setSurfaceView(mSurfaceView) .setPreviewOrientation(90) .setContext(getApplicationContext()) .setAudioEncoder(SessionBuilder.AUDIO_NONE) .setAudioQuality(new AudioQuality(16000, 32000)) .setVideoEncoder(SessionBuilder.VIDEO_H264) .setVideoQuality(new VideoQuality(320,240,20,500000)) .build(); mSurfaceView.getHolder().addCallback(this); }
b. Na verdade, inicie a sessão:
mSession.setDestination(destination_server_url); mSession.start();
c. Interrompa a sessão quando terminar:
mSession.stop();
Agora, por favor, não entenda mal. O streaming em tempo real é confuso por natureza e o libStreaming não elimina essa complexidade. No entanto, ele faz um bom trabalho escondendo isso de você na maioria das vezes. Em alguns casos, você precisará lidar com a complexidade, como ao selecionar a política de sinalização de peer, escolher a codificação da câmera (normalmente você deseja usar MediaCodec/surface-to-buffer) ou lidar com empacotamento.
Ainda assim, você descobrirá que os mocinhos por trás do libStreaming se esforçaram ao mesclar essas complexidades suavemente em uma API simples de usar.
O LibStreaming é compatível com a maioria dos codificadores usados por aplicativos Android, incluindo H.264, H.263, AAC e AMR.
Obtive ótimos resultados com esta biblioteca. Vários dos aplicativos de streaming mais populares o usam como parte de sua infraestrutura. Se você encontrar a necessidade, tenho certeza de que isso tornará sua experiência de streaming de mídia muito mais suave.
LeakCanary: detectar vazamentos de memória em uma linha de código
Vamos começar com a motivação por trás dessa biblioteca: vazamentos de memória . Os aplicativos Android são propensos a eles, especialmente se você não for cuidadoso com sua codificação. Na verdade, criar vazamentos de memória é muito simples. Tudo o que você precisa fazer é armazenar uma referência de atividade fora de seu contexto. Na verdade, mesmo armazenar uma referência a um único objeto de exibição fora do contexto de sua atividade criará um vazamento.
Por quê? Porque uma visão – todas as visualizações, na verdade – armazena internamente uma referência de contexto para sua atividade que a contém. Contanto que uma referência à exibição seja mantida, sua atividade de contenção - junto com o que está dentro dela, incluindo drawables, hierarquia de exibição e recursos - não pode ser recuperada pelo coletor de lixo.
Manter uma referência a uma atividade de vazamento nem sempre é óbvio como um parâmetro estático. Sempre que você criar uma classe interna ou gerar um encadeamento dentro de uma atividade, uma referência a essa atividade será criada e a atividade não poderá ser recuperada até que essa classe interna ou encadeamento seja concluída.
O vazamento de uma referência a uma única atividade com uso intensivo de recursos às vezes é suficiente para travar seu aplicativo com uma exceção de “ falta de memória ”.
Como você pode se proteger contra eles? Comece com práticas de codificação rigorosas , é claro. Mas nem todos nós somos desenvolvedores Android experientes, e mesmo os desenvolvedores experientes às vezes esquecem as regras.
Revisões periódicas de código com ênfase em vazamentos de memória podem ser úteis, mas levam tempo. Além disso, alguns vazamentos são realmente sorrateiros e difíceis de detectar por mera revisão de código.
Usar a ferramenta de memória do DDMS é uma ótima maneira de saber, ao longo do tempo, se seu aplicativo está vazando. Você definitivamente deve usá-lo. No entanto, não lhe dirá o que está causando o vazamento.
Aí vem o leakCanary para o resgate. É o melhor detector de vazamento de memória disponível e fornece detecção automática de vazamento - como em uma ou duas linhas de código - para todas as suas atividades.
Para usá-lo, basta inicializar o leakCanary com o objeto onCreate()
do seu aplicativo:
public class MyApp extends Application { @Override public void onCreate() { super.onCreate(); LeakCanary.install(this); // more initialisations } }
E você está feito. LeakCanary monitorará vazamentos de memória e enviará uma notificação se detectar um.
O LeakCanary consegue essa mágica injetando automaticamente um objeto chamado ActivityRefWatcher
em todas as suas atividades e monitorando sua contagem de referências após a chamada de onDestroy()
. Uma contagem de referência > 0 em uma atividade destruída só pode significar um vazamento.
Importante: a detecção de vazamento funciona apenas para aplicativos de modo de depuração. Nunca teste vazamentos (bem, não com LeakCanary) em um APK de modo de lançamento.
Mas e se eu quiser testar outras partes do meu sistema quanto a vazamentos? Aqui, LeakCanary oferece um objeto chamado refWatcher, que na verdade é o valor de retorno da chamada de inicialização:
refWatcher = LeakCanary.install(this);
Ele pode ser usado para observar valores que serão recuperados em breve. Mais precisamente, valores que eu acho que em breve serão recuperados. Para isso, ligue:
refWatcher.watch(my_soon_to_be_reclaimed_obj);
A biblioteca informará se este objeto não foi liberado pouco tempo após a chamada de observação.
Não consegui encontrar o valor desse “curto tempo” em nenhum lugar, mas provavelmente não é tão importante. Com o leakCanary, as coisas simplesmente funcionam. Impagável.
Resumo
Desenvolvedores experientes cortam dias e semanas de suas fases de codificação e depuração usando essas bibliotecas, portanto, não há motivo para você não fazer o mesmo.
Para resumir, aqui está o que minha seleção de bibliotecas do Android pode fazer por você:
ButterKnife – O código injetado automaticamente ajudará você a eliminar grande parte do código padrão do seu aplicativo. É a injeção de código final para Android. Preciso dizer mais?
AndroidAnnotations – Use classes extremamente rápidas geradas automaticamente e injeção de código baseada em nome para economizar tempo sem penalidade de desempenho sobre a lógica codificada manualmente.
EventBus – Desacoplar componentes para um código mais robusto, a comunicação entre componentes nunca foi tão simples.
OkHttp – Um substituto inteligente para HttpURLConnection, com suporte para rede assíncrona, solicitação de consulta de rota de redirecionamento, consulta de cache local e muito mais.
Picasso – Manipulação de imagem simplificada que é tão boa que agora é usada pelo Google. É uma grande economia de tempo em projetos de mídia pesada e certos projetos legados.
ActiveAndroid – ORM facilitado sem sobrecarga de desempenho.
LibStreaming – Streaming de vídeo em tempo real, usado pelos principais aplicativos de streaming.
Essas são as únicas bibliotecas do Android que valem o seu tempo? Certamente não. Mas eu prometo a você: usar qualquer um deles em seu próximo projeto fará de você um desenvolvedor muito melhor. Se você quiser vê-los em ação, dê uma olhada no meu GitHub.
Se você já estiver usando alguns ou todos eles, ou se estiver usando bibliotecas alternativas, peço que compartilhe suas experiências nos comentários abaixo.