Informação é provavelmente o recurso mais importante em que os usuários confiam com aplicações. Para desenvolvedores de aplicativos, essa informação nos diz quem é o usuário, o que nos capacita a fornecer uma boa experiência de usuário (UX). Além disso, ao aplicar regras de negócio a essas informações, definimos o comportamento que o aplicativo deve ter. Essas informações podem ser sensatas e expor os dados privados dos usuários, por isso é muito importante que as manipulemos corretamente para garantir sua integridade, privacidade e armazenamento adequado. Dentro deste tutorial do Android Room, mostraremos como você pode trabalhar com esses dados mais facilmente, garantindo sua integridade e segurança, tudo isso usando o Room. Room é parte da arquitetura Android Components.
Do You Need Room?
Quando falamos em armazenar informações de forma persistente no Android, a opção mais importante e “fácil” é usar o SQLite simples. No entanto, ter uma boa implementação de base de dados usando SQLite implica a geração de um monte de código que não fornece valor real. A arquitetura que segue frequentemente não é tão limpa ou clara como poderia ser.
Você também poderia usar o mapeamento objeto-relacional, ou ORM, mas você ainda precisará definir e criar manualmente o banco de dados, subclassificando SQLiteOpenHelper e criando as classes de contrato.
Então, a questão é: Existe alguma forma de simplificar todo este processo? A resposta é: Sim, o espaço é a sua solução.
Uma visão mais próxima de Room and How It Works
Room é uma das ferramentas mais importantes no Android Architectural Components. Lançado no Google I/O 2016, é uma poderosa ferramenta para armazenar e manipular informações em aplicativos Android. Fornece uma forma muito fácil de trabalhar com dados e garante sempre a sua segurança e integridade.
Room não é uma ORM; em vez disso, é uma biblioteca completa que nos permite criar e manipular bancos de dados SQLite mais facilmente. Usando anotações, podemos definir as nossas bases de dados, tabelas e operações. A Sala irá traduzir automaticamente estas anotações em instruções/queries SQLite para realizar as operações correspondentes no motor da base de dados.
Os três componentes principais da Sala são:
– Entidade: Representa uma tabela dentro da base de dados da Sala. Deve ser anotada com @Entity.
– DAO: Uma interface que contém os métodos para acessar a base de dados. Deve ser anotada com @Dao.
– Base de dados: Representa a base de dados. É um objeto que mantém uma conexão com o banco de dados SQLite, e todas as operações são executadas através dele. Ele é anotado com @Database.
A arquitectura da sala tem este aspecto:
Vejamos um exemplo
Vamos mergulhar neste tutorial da Sala Andróide. Imagine que nós precisamos criar um aplicativo para armazenar sua rotina de ginástica. Vamos ter quatro entidades no nosso banco de dados, como mostraremos abaixo. Todo o código de exemplo é escrito usando Kotlin (se você não conhece Kotlin ou quer aprender mais, convido-o a ler meu artigo sobre ele).
A primeira coisa que precisamos fazer é atualizar nosso arquivo de graduação. Deve ser parecido com isto:
apply plugin: 'com.android.application'apply plugin: 'kotlin-android'apply plugin: 'kotlin-android-extensions'apply plugin: 'kotlin-kapt'android { //.. Omitted since it is not relevant for the example}dependencies { //... some dependencies were omitted due to they are not relevant for the example def room_version = "2.2.0-rc01" implementation "androidx.room:room-runtime:$room_version" kapt "androidx.room:room-compiler:$room_version" compile 'com.google.code.gson:gson:2.2.4' implementation 'io.reactivex.rxjava2:rxandroid:2.0.2' implementation 'io.reactivex.rxjava2:rxjava:2.1.17'}
Vejamos e analisemos cada um dos três principais componentes da Sala: Entidades, DAOs, e Base de Dados.
Entidades
Para o nosso exemplo, vamos usar quatro entidades: Género, Exercício, Rotina e Estagiário.
Género.kt
Representa o gênero do trainee
@Entitydata class Gender( @PrimaryKey(autoGenerate = true) val id: Int? = null, val name: String)
Coisas a notar:
– Todas as classes que representam uma entidade da base de dados devem ser anotadas com @Entity
– Com a anotação @PrimaryKey(autoGenerate = true) estamos indicando que o id é a chave primária da entidade e deve ser autoGenerado pelo motor da base de dados.
Exercise.kt
Representa um exercício que faz parte de uma rotina.
@Entitydata class Exercise( @PrimaryKey(autoGenerate = true) val exerciseId: Int, val name: String, val repetitions:Int, @ColumnInfo(name = "machine_name") val machineName: String, val liftedWeight: Int)
Coisas a notar:
– Por padrão, Room usa os nomes dos campos como os nomes das colunas na base de dados. Se você quiser que uma coluna tenha um nome diferente, adicione a anotação @ColumnInfo a um campo.
Routine.kt
Basicamente um container de exercícios que juntos criam uma rotina de exercícios.
@Entity(tableName = "traineeRoutine")data class Routine( @PrimaryKey(autoGenerate = true) val routineId: Int, @ColumnInfo(name = "due_day") val dueDay: Date, @TypeConverters(ListConverter::class) val exercises: List)
Coisas a notar:
– Quando uma classe é anotada com @Entity, o nome da tabela será o nome da classe. Se quisermos usar um nome diferente, temos de adicionar a propriedade tableName juntamente com a anotação @Entity.
– A anotação @TypeConverters tem que ser usada quando declaramos uma propriedade para a qual o tipo é uma classe personalizada, uma lista, tipo de data, ou qualquer outro tipo que Room e SQL não sabem como serializar. Neste caso, estamos usando a anotação no nível do campo de classe, onde somente esse campo será capaz de usá-la. Dependendo de onde a anotação for colocada, ela se comportará de forma diferente como explicado aqui.
Trainee.kt
Representa o dono da rotina.
@Entity(indices = , foreignKeys = , childColumns = )])data class Trainee( @PrimaryKey(autoGenerate = true) val id: Int, val name: String, val age: Int, val gender: Int?, @Embedded val routine: Routine)
DAOs
Data Access Objects (DAOs) são usados para acessar nossos dados quando implementamos o Room. Cada DAO tem que incluir um conjunto de métodos para manipular os dados (inserir, atualizar, excluir ou obter).
Um DAO pode ser implementado como uma interface ou como uma classe abstrata. No nosso caso, estamos usando uma interface. Como todos os DAOs são basicamente idênticos, mostraremos apenas um.
GenderDao.kt@Daointerface GenderDao { @Insert(onConflict = OnConflictStrategy.REPLACE) fun insertGender(gender: Gender) @Update fun updateGender(gender: Gender) @Delete fun deleteGender(gender: Gender) @Query("SELECT * FROM Gender WHERE name == :name") fun getGenderByName(name: String): List @Query("SELECT * FROM Gender") fun getGenders(): List}
Poucas coisas para notar:
– Todos os DAOs têm que ser anotados com @Dao.
– Uma função anotada com @Insert, @Update, ou @Delete tem que receber uma instância da classe desejada como parâmetro, que representa o objeto que queremos inserir, atualizar ou excluir, respectivamente.
– No caso de operações de inserção ou atualização, podemos usar a propriedade onConflict para indicar o que fazer quando ocorrer um conflito realizando a operação. As estratégias disponíveis para usar são: REPLACE, ABORT, FAIL, IGNORE e ROLLBACK.
– Se quisermos obter informações específicas de uma ou mais entidades, podemos anotar uma função com @Query e fornecer um script SQL como parâmetro.
Base de dados
Representa a base de dados. Ele mantém uma conexão com o banco de dados SQLite real.
AppDatabase.kt@Database(entities = , version = 1)@TypeConverters(DateTypeConverter::class)abstract class AppDatabase : RoomDatabase() { abstract fun exerciseDao(): ExerciseDao abstract fun genderDao(): GenderDao abstract fun routineDao(): RoutineDao abstract fun traineeDao(): TraineeDao companion object { var INSTANCE: AppDatabase? = null fun getAppDataBase(context: Context): AppDatabase? { if (INSTANCE == null){ synchronized(AppDatabase::class){ INSTANCE = Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "myDB").build() } } return INSTANCE } fun destroyDataBase(){ INSTANCE = null } }}
Coisas a notar aqui:
– Esta é uma classe abstrata que tem que estender RoomDatabase.
– Ele tem que ser anotado com @Database, e recebe uma lista de entidades com todas as classes que compõem o banco de dados (todas estas classes têm que ser anotadas com @Entity). Nós também temos que fornecer uma versão da base de dados.
– Temos que declarar uma função abstrata para cada uma das entidades incluídas na anotação da @Database. Esta função tem que retornar o DAO correspondente (uma classe anotada com @Dao).
– Finalmente, declaramos um objeto companheiro para obter acesso estático ao método getAppDataBase, o que nos dá uma instância de um único botão do banco de dados.
Type Converters
Type Converters são usados quando declaramos uma propriedade que Room e SQL não sabem como serializar. Vamos ver um exemplo de como serializar um tipo de dado ‘data’.
DateTypeConverter.ktclass DateTypeConverter { @TypeConverter fun fromTimestamp(value: Long?): Date? { return if (value == null) null else Date(value) } @TypeConverter fun dateToTimestamp(date: Date?): Long? { return date?.time }}
Usando o Room Database
Agora vamos ver um exemplo muito simples de como usar o Room Database que acabamos de criar:
MainActivity.ktclass MainActivity : AppCompatActivity() { private var db: AppDatabase? = null private var genderDao: GenderDao? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) Observable.fromCallable({ db = AppDatabase.getAppDataBase(context = this) genderDao = db?.genderDao() var gender1 = Gender(name = "Male") var gender2 = Gender(name = "Female") with(genderDao){ this?.insertGender(gender1) this?.insertGender(gender2) } db?.genderDao()?.getGenders() }).doOnNext({ list -> var finalString = "" list?.map { finalString+= it.name+" - " } tv_message.text = finalString }).subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe() }}
O que estamos a fazer?
– Obtendo a instância da base de dados e do GéneroDao.
– Criando duas instâncias de Género: Macho e Fêmea.
– Inserindo as duas instâncias criadas no banco de dados.
– Consultando o banco de dados para obter todos os gêneros armazenados nele.
– Fundindo o nome de todos os gêneros que obtivemos do banco de dados, e definindo o texto do TextView com esse valor.
Para fazer mais com menos
Pauma é um dos elementos importantes dos Componentes Arquitetônicos do Android. Ele nos dá uma estrutura muito robusta para trabalhar com informações persistentes, garantindo sempre a segurança e integridade dos dados. Ele também fornece facilidade de uso para os desenvolvedores, para que eles possam escrever código legível e auto-explicativo. Se você quer fazer mais com menos código, e também garantir a segurança dos dados do usuário, você deve estar usando o Room como sua camada de persistência em seu app.
E é isso! Isto é quase tudo que você precisa saber para criar e usar um banco de dados no Android com Room. Obtenha o código completo para este projeto aqui. Obrigado por ler este tutorial sobre o Android Room!