Livro 3 - Projeto Agenda de Contatos - Parte 3

3. Biblioteca Room e persistência de dados

3.5. Classe ContactsRepository (Repository)

O que é um Repositório?

A classe Repository é uma classe que abstrai o acesso a múltiplas fontes de dados. O repositório não é um componente de arquitetura do Android JetPack mas é uma prática recomendada. Um repositório manipula operações sobre as fontes de dados e oferece uma API limpa para ser usada pelo resto da aplicação. A figura abaixo ilustra como o camada repositório se encaixa na arquitetura do projeto. Note que UI se refere a User Interface, ou seja, a interface gráfica.

Porque usar um Repositório?

Um repositório gerencia as threads das consultas e permite o uso de múltiplas fontes de dados. Em um caso de uso mais comum, o repositório implementa a lógica para decidir de os dados devem ser obtidos através de um Web Service ou de uma cache armazenada em um banco de dados local.

Implementando a classe ContactsRepository

A classe ContactsRepository implementará todas as operações necessárias para o aplicativo Agenda de Contatos. Para cada consulta ao banco será usada uma AsyncTask para executar a operação em plano de fundo.

package ...

import android.app.Application;
import android.os.AsyncTask;

import androidx.lifecycle.LiveData;

import java.util.List;

public class ContactsRepository {
private ContactsDAO mContactsDao;
private LiveData<List<Contact>> mAllContacts; // create a cached data


public ContactsRepository(Application application) {
ContactsDatabase db = ContactsDatabase.getDatabase(application);
mContactsDao = db.contactsDao();
mAllContacts = mContactsDao.getAllContacts();
}

/***********************************************
GET ALL CONTACTS
***********************************************/
public LiveData<List<Contact>> getAllContacts() {
return mAllContacts;
}

/***********************************************
GET CONTACT BY ID
***********************************************/
public LiveData<Contact> getContactById(int id) {
return mContactsDao.getContactById(id);
}

/***********************************************
INSERT CONTACT TASKS
***********************************************/
public void insert (Contact contact) {
new insertAsyncTask(mContactsDao).execute(contact);
}

private static class insertAsyncTask extends AsyncTask<Contact, Void, Void> {
private ContactsDAO mAsyncTaskDao;

insertAsyncTask(ContactsDAO dao) {
mAsyncTaskDao = dao;
}

@Override
protected Void doInBackground(final Contact... params) {
mAsyncTaskDao.insert(params[0]);
return null;
}
}

/***********************************************
UPDATE CONTACT TASKS
***********************************************/
public void update (Contact contact) {
new updateAsyncTask(mContactsDao).execute(contact);
}

private static class updateAsyncTask extends AsyncTask<Contact, Void, Void> {
private ContactsDAO mAsyncTaskDao;

updateAsyncTask(ContactsDAO dao) {
mAsyncTaskDao = dao;
}

@Override
protected Void doInBackground(final Contact... params) {
mAsyncTaskDao.update(params[0]);
return null;
}
}

/***********************************************
DELETE CONTACT TASKS
***********************************************/
public void delete (Contact contact) {
new deleteAsyncTask(mContactsDao).execute(contact);
}

private static class deleteAsyncTask extends AsyncTask<Contact, Void, Void> {
private ContactsDAO mAsyncTaskDao;

deleteAsyncTask(ContactsDAO dao) {
mAsyncTaskDao = dao;
}

@Override
protected Void doInBackground(final Contact... params) {
mAsyncTaskDao.delete(params[0]);
return null;
}
}
}
  • O repositório cria dois objetos em seu construtor para serem usados ao longo do aplicativo. mContactsDAO representa o DAO para executar as operações no banco de dados e mAllContacts representa a lista de contatos.
  • Observe que não foi implementando um método para buscar todos os contatos. Isso ocorre durante o construtor e será invocado logo no inicio do aplicativo apenas uma vez. Quando um contato for adicionado, alterado ou removido, você deve imaginar que a lista de contatos deveria ser atualizada e um nova consulta ao banco de dados solicitado para exibir na tela essas novas informações. No entanto, estamos usando LiveData e assim não precisamos fazer uma nova solicitação pois qualquer mudança a lista de contatos será automaticamente processada! Assim, ao ser chamado o método getAllContacts podemos simplesmente retornar a lista de objetos LiveData sem ter que fazer explicitamente uma nova solicitação ao banco de dados com o DAO.
  • Ademais, no caso de ser buscado as informações de um contato específico precisamos apenas fazer a solicitação ao DAO pois ele irá automaticamente buscar o valor salvo na cache da consulta já feita logo não será uma tarefa demorada.
  • Para as demais operações, usamos uma AsyncTask para configurar uma ação em segunda plano e executar o método apropriado do DAO.

Classe AsyncTask

A classe AsyncTask é um classe que permite facilmente a declaração de ações para serem executadas em segundo plano sem você ter que manipular explicitamente threads. Essa classe é criada com a declaração de três tipos respectivamente um tipo associado aos parâmetros, progresso e resultado. Esses tipos serão usados nos métodos onPreExecute, doInBackground, onProgressUpdate e onPostExecute. Todo código declarado dentro do método doInBackground será executado em uma thread paralela. Você pode então fazer uma solicitação a um Web Service em paralela e após receber os dados executar uma ação, como atualizar os valores na tela, com o método onPostExecute.

No caso do aplicativo da Agenda de Contatos não precisamos de uma ação de retorno pois apenas iremos fazer solicitações para o banco de dados. Logo usar apenas o método doInBackground é o suficiente. Você poderia no entanto, mostrar uma mensagem de sucesso após uma consulta o banco usando o método onPostExecute.