![Monetizando Sua APP Android Com AppJolt no Uninstall](./img/post/50-50/monetizando-sua-app-android-com-appjolt-no-uninstall.png)
Eventos de Leitura e Firebase UI Android - Parte 2
(5272) (17)
![Capa do curso Prototipagem Profissional de Aplicativos](.//view/infoproduct/book/mapas-android-de-alta-qualidade/img/capa-curso-android-prototipagem-profissional-de-aplicativos.png)
CategoriasAndroid, Design, Protótipo
AutorVinícius Thiengo
Vídeo aulas186
Tempo15 horas
ExercíciosSim
CertificadoSim
![](./img/read-book/95-135/trabalho-eficaz-com-codigo-legado.jpeg)
CategoriaEngenharia de Software
Autor(es)Michael C. Feathers
EditoraBookman
Edição3ª
Ano2013
Páginas428
Opa, blz?
Nesse vídeo dois da série Firebase no Android são apresentados os cinco eventos de leitura e o uso do Firebase UI para inserção de dados no RecyclerView de usuários do chat.
É apresentado também a vantagem que temos na atualização dos dados na APP quando a entidade ativa da comunicação é o servidor e não a APP. Com o Firebase não há necessidade da utilização de serviços de push message quando a APP está sendo utilizada. O Firebase se encarrega de informar (via mensagem no modelo broadcast) todos os devices que estão utilizando a APP.
Fique atento quanto aos listeners Value e Child, pois apesar do uso do ValueEventListener ser mais simples ele é também ineficiente.
No decorrer do post seguimos com o código do primeiro vídeo. O primeiro passo é atualizar o build.gradle (Module: app). A minTargetSDK é 16 agora, atualizamos a versão do compile do Firebase e inserimos o compile do Firebase UI:
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
defaultConfig {
applicationId "br.com.thiengo.thiengocalopsitafbexample"
minSdkVersion 16
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
repositories {
mavenCentral()
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.2.1'
compile 'com.android.support:design:23.2.1'
compile 'com.firebase:firebase-client-android:2.5.2+'
compile 'com.firebaseui:firebase-ui:0.3.1'
}
Nossa próxima atualização é no código da classe User.class, primeiro no método saveDB, onde depois da remoção das chamadas a setId() e setPassword() ficamos com o seguinte código:
...
public void saveDB(){
Firebase firebase = LibraryClass.getFirebase();
firebase = firebase.child("users").child( getId() );
firebase.setValue(this);
}
...
Logo acima da assinatura da classe definimos um annotation para ignorar os campos id e password na leitura e inserção de dados no Firebase utilizando um objeto (ou modelo de classe) do tipo User:
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
...
@JsonIgnoreProperties({"id", "password"})
public class User {
...
}
Nosso próximo passo é atualizar o layout de nossa MainActivity.class, activity_main.xml. Foi removido o TextView e Button e adicionado um RecyclerView:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="br.com.thiengo.thiengocalopsitafbexample.MainActivity">
<android.support.v7.widget.RecyclerView
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:id="@+id/rv_users"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
Agora seguimos com a atualização da classe MainActivity.class. Primeiro removemos o antigo método de logoff e adicionamos os métodos onCreateOptionsMenu() e onOptionsItemSelected():
...
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if(id == R.id.action_update){
startActivity(new Intent(this, UpdateActivity.class));
}
else if(id == R.id.action_logout){
firebase.unauth();
Intent intent = new Intent(this, LoginActivity.class);
startActivity(intent);
finish();
}
return super.onOptionsItemSelected(item);
}
...
Note que nossa lógica de logoff está no segundo condicional do método onOptionsItemSelected(), sem a referencia a nossa LibraryClass, pois firebase agora é uma variável de instância.
Uma pausa para colocarmos o menu.xml no folder /res/menu:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity">
<item
android:id="@+id/action_update"
android:orderInCategory="100"
android:title="@string/atualizar"
app:showAsAction="never" />
<item
android:id="@+id/action_logout"
android:orderInCategory="100"
android:title="@string/logout"
app:showAsAction="never" />
</menu>
Agora voltando a MainActivity devemos definir firebase como variável de instância e implementar o novo código de onCreate():
...
private Firebase firebase;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
firebase = LibraryClass.getFirebase().child("users");
}
...
Agora implementamos um método init() que será responsável por inicializar nosso RecyclerView. Esse método será chamado no onResume():
...
@Override
protected void onResume() {
super.onResume();
init();
}
private void init(){
RecyclerView rvUsers = (RecyclerView) findViewById(R.id.rv_users);
rvUsers.setHasFixedSize( true );
rvUsers.setLayoutManager( new LinearLayoutManager(this));
}
...
Note que ainda não colocamos o adapter no RecyclerView. Essa parte será implementada posteriormente no post.
Na raiz do projeto, onde se encontram as Activities crie um novo package com o nome "listener". Agora criamos a primeira classe listener de teste. Antes é necessário informar que o FIrebase tem cinco eventos de leitura, vamos criar duas novas classes que juntas implementam esses cinco listeners de leitura. A classe CustomValueEventListener implementa o evento de leitura Value e a classe CustomChildEventListener implementa os outros quatro eventos de leitura, são eles: onChildAdded(), onChildChanged(), onChildRemoved() e onChildMoved(). Abaixo a implementação de CustomValueEventListener:
import android.util.Log;
import com.firebase.client.DataSnapshot;
import com.firebase.client.FirebaseError;
import com.firebase.client.ValueEventListener;
import br.com.thiengo.thiengocalopsitafbexample.domain.User;
public class CustomValueEventListener implements ValueEventListener {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
for( DataSnapshot d : dataSnapshot.getChildren() ){
User u = d.getValue( User.class );
Log.i("log", "Name: "+u.getName());
Log.i("log", "Email: "+u.getEmail());
}
}
@Override
public void onCancelled(FirebaseError firebaseError) {}
}
Nosso objetivo com as classes de que implementam as interfaces de listeners, além de entender delas, é descobrir se alguma será útil para o fácil preenchimento de nosso RecyclerView. O método onDataChange() será chamado sempre que houver alguma atualização, inserção ou remoção no nodo do Firebase que estiver sendo utilizado. O método onDataChange() será também chamado na primeira execução do método que vincula essa classe listener ao Firebase. Essa primeira chamada também é verdade para o método onChildAdded() da classe que implementa a interface ChildEventListener.
Abaixo a imagem do nodo "users" que vamos utilizar como conteúdo em nosso código exemplo:
Ainda temos de implementar a CustomChildEventListener que tem os outros métodos listeners:
import android.util.Log;
import com.firebase.client.ChildEventListener;
import com.firebase.client.DataSnapshot;
import com.firebase.client.FirebaseError;
import br.com.thiengo.thiengocalopsitafbexample.domain.User;
public class CustomChildEventListener implements ChildEventListener {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
User u = dataSnapshot.getValue( User.class );
Log.i("log", "ADDED");
Log.i("log", "Name: "+u.getName());
Log.i("log", "Email: "+u.getEmail());
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
User u = dataSnapshot.getValue( User.class );
Log.i("log", "CHANGED");
Log.i("log", "Name: "+u.getName());
Log.i("log", "Email: "+u.getEmail());
}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
User u = dataSnapshot.getValue( User.class );
Log.i("log", "REMOVED");
Log.i("log", "Name: "+u.getName());
Log.i("log", "Email: "+u.getEmail());
}
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {}
@Override
public void onCancelled(FirebaseError firebaseError) {}
}
Note que em ambas implementações temos o método onCanceled() que é acionado quando há algum problema na leitura dos dados do Frebase por parte das classes listeners. O método onMoved() de CustomChildEventListener pode ser ignorado para o conteúdo desse post.
O parâmetro DataSnapshot en anbas as classes representa o último estado dos dados do Firebase, porém representa os dados de acordo com a granularidade dos listeners. O ValueEventListener recebe todos os dados abaixo do nodo "users" que está sendo utilizado, ou seja, se somente um dado do nodo "users" for atualizado todo o conteúdo de "users" será retornado no método onDataChange() pelo parâmetro DataSnapshot.
A granularidade de ChildEventLsistener é menor, no caso somente o nodo atualizado terá os dados retornados e não todo o conteúdo de "users".
Agora partimos para o primeiro teste utilizando somente a classe que implementa o listener ValueEventListener. O primeiro passo é atualizar o método onCreate() da MainActivity além de adicionar uma variável de instância nomeada customValueEventListener:
...
private Firebase firebase;
private CustomValueEventListener customValueEventListener;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
firebase = LibraryClass.getFirebase().child("users");
customValueEventListener = new CustomValueEventListener();
firebase.addValueEventListener( customValueEventListener );
}
...
O método addValueListener() adiciona a referência ao listener Value, porém também temos a opção de utilizar o método addListenerForSingleValueEvent() que ao invés de persistir com o listener enquanto ele não é removido do Firebase ou a APP é encerrada, esse método muda o comportamento de um ValueEventListener. Ele ativará o listener somente uma vez e logo depois o removerá do firebase.
Para podermos testar a utilização do listener ValueEventListener ainda falta a implementação do onDestroy() da MainActivity. Nesse método vamos remover o listener do firebase antes de destruir a Activity. Isso é também útil para que seja reduzida as chances de um OutOfMemoryException devido o aumento de memory leak na APP. Segue código:
...
@Override
protected void onDestroy() {
super.onDestroy();
firebase.removeEventListener( customValueEventListener );
}
...
Vou evitar a fadiga e não postarei a imagem da APP depois desse teste, pois o resultado já foi comentado anteriormente. Logo de inicio todos os dados do nodo "users" são apresentados e logo de pois, testando a atualização de somente um nodo abaixo de "users", todos os dados são noavemente retornados. Todos esse print é apresentado nos logs do AndroidStudio por meio da chamada a Log.i() em nosso código.
Para o teste com a classe que implementa o listener ChildEventListener apenas devemos alterar o nome Value por Child nas entidades adicionadas para o teste com a classe que implementa ValueEventListener, como abaixo:
...
private CustomChildEventListener customChildEventListener;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
firebase = LibraryClass.getFirebase().child("users");
customChildEventListener = new CustomChildEventListener();
firebase.addChildEventListener( customChildEventListener );
}
...
E no onDestroy():
...
@Override
protected void onDestroy() {
super.onDestroy();
firebase.removeEventListener( customChildEventListener );
}
...
A vantagem dessa implementação com ChildEventListener é que somente o nodo alterado é retornado em algum dos métodos listeners. Mesmo assim ela ainda não nos atende no objetivo de preencher nosso RecyclerView com o menor esforço possível, logo vamos para a utilização das classes de Firebase UI.
Nosso primeiro passo é a criação de um novo package chamado adapter. Esse package ficará no mesmo level que o package listener, no mesmo level das Activities.
Nosso segundo passo é a definição de uma classe que herda de RecyclerView.ViewHolder. Em nosso projeto é a classe UserViewHolder que ficará dentro de nosso novo package, adapter:
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;
public class UserViewHolder extends RecyclerView.ViewHolder {
public TextView text1;
public TextView text2;
public UserViewHolder(View itemView) {
super(itemView);
text1 = (TextView) itemView.findViewById(android.R.id.text1);
text2 = (TextView) itemView.findViewById(android.R.id.text2);
}
}
Os TextViews acima são referentes ao layout nativo android.R.layout.two_line_list_item que será informado na instanciação do adapter de nosso RecyclerView, lá na MainActivity.
O próximo passo é a criação de uma classe personalizada para ser o adapter, essa classe herdará de FirebaseRecyclerAdapter e também estará no package adapter:
import com.firebase.client.Query;
import com.firebase.ui.FirebaseRecyclerAdapter;
import br.com.thiengo.thiengocalopsitafbexample.domain.User;
public class UserRecyclerAdapter extends FirebaseRecyclerAdapter<User, UserViewHolder> {
public UserRecyclerAdapter(
Class<User> modelClass,
int modelLayout,
Class<UserViewHolder> viewHolderClass,
Query ref ){
super( modelClass, modelLayout, viewHolderClass, ref );
}
@Override
protected void populateViewHolder(
UserViewHolder userViewHolder,
User user, int i) {
userViewHolder.text1.setText( user.getName() );
userViewHolder.text2.setText( user.getEmail() );
}
}
Note que nessa precisamos definir o construtor, pois é nele que será passado o modelo de classe para deserialização dos dados do Firebase para um objeto POJO Java. O id do layout que será utilizado. O modelo de classe que implementa o ViewHolder e uma referência a Firebase (Query herda de Firebase). Esses parâmetros vão permitir que o trabalho pesado de apresentação e atualização do RecyclerView seja feito pelo Firebase UI.
O método populateViewHolder() apenas mapeia os dados nas views de UserViewHolder.
Nosso próximo passo é configurar a MainActivity para vincular esse novo adapter ao RecyclerView. No método init() e no método onDestroy() agora temos:
...
private void init(){
RecyclerView rvUsers = (RecyclerView) findViewById(R.id.rv_users);
rvUsers.setHasFixedSize( true );
rvUsers.setLayoutManager( new LinearLayoutManager(this));
adapter = new UserRecyclerAdapter(
User.class,
android.R.layout.two_line_list_item,
UserViewHolder.class,
firebase );
}
@Override
protected void onDestroy() {
super.onDestroy();
adapter.cleanup();
}
...
O método cleanup() é responsável por remover o listener de mudança de dados no Firebase, listener que já é adicionado pelo Firebase UI. Depois do teste temos:
Note que para atualização de dados não precisamos do apoio de scripts de push message, o Firebase server trabalha como parte ativa da comunicação entre server e APP além de disparar a atualização da base no formato Broadcast.
O projeto completo pode ser encontrado no GitHub: https://github.com/viniciusthiengo/nosso-chate
O projeto parte dois também é apresentado por completo no vídeo abaixo:
Abaixo a lista de posts / vídeos que podem lhe ajudar a compreender melhor o conteúdo desse post dois além de links úteis para acompanhar o conteúdo sobre Firebase:
RecyclerView, Material Design Android - Parte 2
PlayList sobre Firebase no canal Google Developers
Página da doc Firebase sobre Retriving Data, fonte do conteúdo de eventos de leitura desse post
Vlw
Relacionado
Comentários Blog (17)
![](img/post/comment/60-60/default-01.png)
![](img/user/60-60/thiengo-calopsita.jpeg)
![](img/user/60-60/thiengo-calopsita.jpeg)
![](img/post/comment/60-60/default-01.png)
![](img/post/comment/60-60/default-01.png)
![](img/user/60-60/thiengo-calopsita.jpeg)
![](img/post/comment/60-60/default-01.png)
![](img/user/60-60/thiengo-calopsita.jpeg)
![](img/post/comment/60-60/default-01.png)
![](img/user/60-60/thiengo-calopsita.jpeg)
![](img/post/comment/60-60/default-01.png)
![](img/user/60-60/thiengo-calopsita.jpeg)
![](https://lh5.googleusercontent.com/-Ksoxo3Ail94/AAAAAAAAAAI/AAAAAAAABQI/AZ1c41hnRWs/photo.jpg)
![](img/user/60-60/thiengo-calopsita.jpeg)
![](img/post/comment/60-60/default-01.png)
![](img/post/comment/60-60/default-01.png)
Comentários Facebook