O artigo de hoje começa de uma forma diferente e, logo, o leitor saberá o motivo. Para os desenvolvedores de aplicativos móveis um pouco mais antigos, a palavra Java ME soa de forma saudosista e desafiadora. Antes da dupla iOS e Android dominarem o mercado mobile e ditar os principais SDKs, plataformas e lojas virtuais, existia um sistema operacional chamado Symbian com uma plataforma de desenvolvimento líder, o Java ME.
Mas por que relembrar isso em pleno 2018? Porque, naquele tempo, a persistência de dados era feita de forma um pouco arcaica, se comparada às tecnologias do mundo atual. Banco de dados relacional, banco de dados NoSQL, nada disso estava disponível para nós, desenvolvedores. A bola da vez era o Record Management System. A imagem abaixo mostra como era a persistência de dados com esse sistema:
Quer dizer que não existiam tabelas, campos, tipos de dados, entre outros? Sim, tudo era armazenado como um vetor. Para saber quando começava e acabava um campo, era inserido um caractere especial nesse array unidimensional de bytes. Por exemplo: para salvar um registro com informações de um cliente: ricardo;ogliari;33;rua x, 123;passo fundo, essa string era transformada em vetor de bytes e persistida.
Bem, mas as linguagens de programação para desenvolvimento móvel evoluíram, bem como seus SDKs, bibliotecas e, consequentemente, a forma como os dados são persistidos. Tanto Android como iOS, os líderes de mercado, oferecem opções bastante similares. Porém, neste artigo vou focar na plataforma do Google.
Basicamente, as duas principais maneiras de persistir informações no Android com API nativa são: utilizando um par de chave valor, chamado de Shared Preference, ou usando um banco de dados relacional convencional, com o SQLite. Considero ambas fáceis, se compararmos ao vetor de bytes que existia no Java ME. Porém, principalmente no relacional, o uso da famigerada classe que herda de SQLiteOpenHelper e a mistura com SQL e Java deixam alguns programadores inexperientes malucos. E com uma certa razão, diga-se de passagem.
Porém, atualmente, existem bibliotecas que facilitam até mesmo o uso dessas APIs nativas do Android. E a facilidade e a modernidade no uso dessas soluções me inspiraram a escrever este pequeno artigo.
O uso do Shared Preference já era simples e com uso de classes autoexplicativas, como mostra a listagem de código abaixo. Depois de recuperar e valorizar os objetos de SharedPreference e sua classe interna, Editor, basta chamar um dos métodos put com sufixo de um tipo primitivo, como o putInt. O primeiro parâmetro é a chave e o segundo, o valor. Por fim, o commit persistia esse par de dados.
SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPref.edit(); editor.putInt(“highScore”, newHighScore); editor.commit();
Mas isso pode e ficou ainda mais simples. Basta usar a TinyDB, como mostra a pequena listagem de código abaixo. A página do projeto no GitHub da biblioteca a descreve da seguinte forma: This class simplifies calls to SharedPreferences in a line of code. It can also do more like: saving a list of strings, integers and saving images. All in 1 line of code!
TinyDB tinydb = new TinyDB(context);
tinydb.putInt(“highScore”, newHighScore);
A TinyDB me impressiona por conseguir simplificar e turbinar algo que já era simples. Todavia, o grande impacto no desenvolvimento é quando usamos bibliotecas que otimizam o uso de banco de dados relacional, ou seja, o SQLite. Até pouco tempo atrás, existiam diversas soluções de terceiros. Uma das mais famosas, e preferida também por este que vos fala, era a Realm. Ela, inclusive, oferece suporte para outras plataformas.
A página oficial do Realm apresenta dados muito impressionantes sobre essa ferramenta de ORM (Object-relational mapping). Segue um resumo:
- Mais de 2 bilhões de usuários utilizam Realm;
- Na lista de algumas das empresas que utilizam o Realm constam Google, Amazon, eBay, IBM, Siemens, Nike, CISCO, Sony, SAP, Alibaba, Macdonald’s e Netflix.
- Suporte para Java, Swift, Objective-C, Javascript e Xamarim.
Para exemplificar também como ele é simples, reproduzo aqui o trecho de código presente no site oficial do Realm:
//na definição da classe basta estender de RealmObject public class Dog extends RealmObject { public String name; public int age; } //um simples objeto é instanciado e, depois, será persistido. De forma indolor e sem //mistura com SQL Dog dog = new Dog(); dog.name = "Rex"; dog.age = 1; //trecho de código que efetivamente persiste o dado no banco de dados Realm realm = Realm.getDefaultInstance(); realm.beginTransaction(); realm.copyToRealm(dog); realm.commitTransaction(); //a pesquisa contém semelhança com SQL, porém, toda a pesquisa é configurada //com classes e métodos da linguagem de programação escolhida, neste caso, Java RealmResults<Dog> pups = realm.where(Dog.class) .lessThan("age", 2) .findAll();
Talvez o único problema que existia na Realm foi solucionado pelo próprio Android: agora temos uma biblioteca oficial e padrão para uso de ORM nos nossos aplicativos, chamada de Room Persistance Library. Parafraseando a própria documentação: The Room persistence library provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite.
O uso dessa biblioteca é ainda mais sofisticado, pois faz uso massivo de anotações para definir características de classes e sua persistência. Por exemplo, supondo que a nossa aplicação necessite salvar dados de clientes, três passos seriam necessários para fazer isso usando a Room. Em primeiro lugar, definir uma classe e inserir a anotação @Entity. Também é possível usar a @PrimaryKey para definir a chave primária com geração automática do valor:
@Entity public class Customer implements Serializable{ @PrimaryKey(autoGenerate = true) private int id; private String name; private String address; private String phone; //métodos getters e settres }
O segundo passo é a geração do famoso DAO (Data Access Object). É nesse ponto que as anotações deixam a classe extremamente legível. Veja abaixo:
@Dao public interface CustomerDAO { @Insert void createCustomer(Customer customer); @Query("SELECT * FROM Customer") List<Customer> readCustomers(); @Update void updateCustomer(Customer customer); @Delete void deleteCustomer(Customer customer); }
E por fim, mas não menos importante, uma classe que estende RoomDatabase. As anotações novamente são o ponto chave, definindo as entidades/tabelas do banco de dados, assim como a versão (lembram-se do construtor de SQLiteOpenHelper e a chamada ao super construtor?). Veja na listagem de código abaixo:
@Database(entities = {Customer.class}, version = 1) public abstract class DatabaseCtrl extends RoomDatabase { private static DatabaseCtrl instance; public static DatabaseCtrl getDatabase(Context context) { if (instance == null) { instance = Room.databaseBuilder(context.getApplicationContext(), DatabaseCtrl.class, "meu_db") .build(); } return INSTANCE; } public abstract CustomerDAO customerDAO(); }
Espero que o leitor tenha ficado tão impactado quanto eu fiquei ao conhecer a Room. A evolução na persistência de dados e o uso de bancos de dados para o mundo móvel simplesmente decolaram. Passamos de um modelo orientado a registro para acesso a um banco de dados relacional, através de uma biblioteca rica, utilizando padrões reconhecidos e amplamente aceitos no mercado, como anotações e singleton. E, cada vez mais, precisamos nos preocupar só com as regras de negócio e menos com SQL e/ou persistência.
***
Artigo publicado na revista iMasters, edição #25: https://issuu.com/imasters/docs/25