W pewnym momencie Twoja aplikacja i tak będzie korzystać z bazy danych. Możesz łączyć się z bazą zdalnie i lokalnie. Jeśli pobierasz dane, które znajdują się na serwerze nie stanowi to większego problemu ponieważ możesz zaimplementować REST API. Jeśli posiadasz bazę danych na urządzeniu, na którym działa aplikacja to już może stanowi pewien problem. Problem polega na tym w jaki sposób najłatwiej i najszybciej pobrać te dane, a następnie je przetworzyć tak aby użytkownik długo nie czekał na wyświetlenie informacji.
Android Architecture Components: Room
Za nim pojawiła się biblioteka Room, trzeba było zdefiniować strukturę bazy danych, utworzyć klasę SQLiteOpenHelper, napisać kod dla CursorAdaptera itd. Jeżeli programista nie był ostrożny, aplikacja mogła łatwo wykonać długotrwałą operację na głównym wątku. A to nie jest dobry pomysł. Aby nie martwić się o te wszystkie rzeczy dostaliśmy od wujka Google fajne narzędzie do zarządzania bazą SQLite.
Czym jest biblioteka Room?
- jest to zbiór klas, które zapewnią warstwę abstrakcji nad SQLite, aby umożliwić płynny dostęp do bazy danych,
- sprawdza poprawności zapytań SQL w czasie kompilacji – istnieje małe prawdopodobieństwo awarii aplikacji ze względu na złe zapytania do bazy danych,
- kod SQL staje się lepszy dzięki implementacji adnotacji,
- możesz w pełni zintegrować ją z innymi komponentami architektury Androida, takimi jak LiveData.
Istnieją 3 główne elementy komponentu Room:
- Database – zawiera właściciela bazy danych i służy jako główny punkt dostępu do podstawowego połączenia. Klasa, do której przypisano adnotacje @Database powinna spełniać następujące warunki:
- być abstrakcyjną klasą, która rozszerza się o RoomDatabase,
- zawiera abstrakcyjną metodę, która nie posiada argumentów i zwraca klasę, której przypisano adnotację @Dao.
- Entity – reprezentuje tabelę w bazie danych,
- DAO – zawiera metody używane do uzyskiwania dostępu do bazy danych.
Uzupełnij wiedzę o: Zaawansowany monitoring akumulatora – Battery Historian
Prosty przykład
Poniższy fragment kodu zawiera przykładową konfigurację bazy danych z jednym obiektem DAO. Przykład wyświetla artykuł.
@Entity (tableName = "Article") public class ArticleEntity { @PrimaryKey(autoGenerate = true) private int uid; public String title; public String site; public String image; public boolean premium; public int comment; public int getUid() { return uid; } public void setUid(int uid) { this.uid = uid; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getSite() { return site; } public void setSite(String site) { this.site = site; } public boolean isPremium() { return premium; } public void setPremium(boolean premium) { this.premium = premium; } public int getComment() { return comment; } public void setComment(int comment) { this.comment = comment; } public String getImage() { return image; } public void setImage(String url) { this.image = url; } }
@Dao public interface ArticleDao { @Query("SELECT * FROM Article") List<ArticleEntity> getAll(); @Query("SELECT * FROM Article WHERE uid IN (:ids)") List<ArticleEntity> loadByIds(int[] ids); @Insert void insertAll(ArticleEntity... article); @Query("UPDATE Article SET premium = (:premium) WHERE uId = (:id)") void changePremiumById(long id, boolean premium); @Delete void delete(ArticleEntity article); @Query("DELETE FROM Article WHERE uId = (:id)") void deleteById(long id); }
@Database(entities = {ArticleEntity.class}, version = 1) public abstract class ArticleDB extends RoomDatabase { public abstract ArticleDao articleDao(); }
public class RoomBasicActivity extends AppCompatActivity { private TextView title, comments; private ImageView image, premium; private Button createDatabase, loadData, changePremiumBtn; private ArticleDB database; private List<ArticleEntity> article; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.room_basic_activity); database = Room.databaseBuilder(this,ArticleDB.class, "Article").allowMainThreadQueries().build(); init(); createDatabase.setOnClickListener(view -> {createData();}); loadData.setOnClickListener(view -> {loadData();}); changePremiumBtn.setOnClickListener(view -> {changePremium();}); } private void createData() { ArticleEntity articleEntity = new ArticleEntity(); articleEntity.setTitle("Android Architecture Components: Room"); articleEntity.setSite("www.myenv.net"); articleEntity.setImage("https://dev.myenv.net/wp-content/uploads/2018/06/logo_myenv_black.png"); articleEntity.setComment(10); articleEntity.setPremium(false); database.articleDao().insertAll(articleEntity); } private void loadData(){ article = database.articleDao().getAll(); if (article.isEmpty()) { Log.d("TAG", "Database is empty"); return; } title.setText(article.get(0).getTitle()); comments.setText("Comments: "+String.valueOf(article.get(0).getComment())); Glide.with(this) .load(article.get(0).getImage()) .into(image); if (article.get(0).isPremium()) premium.setVisibility(View.VISIBLE); else premium.setVisibility(View.INVISIBLE); } private void changePremium() { if (article.get(0).isPremium()) database.articleDao().changePremiumById(article.get(0).getUid(), false); else database.articleDao().changePremiumById(article.get(0).getUid(), true); } private void init() { title = findViewById(R.id.title); comments = findViewById(R.id.comments); image = findViewById(R.id.image); premium = findViewById(R.id.premium); loadData = findViewById(R.id.changePremium); createDatabase = findViewById(R.id.createDatabase); loadData = findViewById(R.id.loadData); changePremiumBtn = findViewById(R.id.changePremium); } }
W klasie ArticleEntity tworzymy strukturę tabeli w bazie danych dla artykułów. Klasę Entity tworzymy za pomocą adnotacji @Entity. Definujemy nazwę tabeli w 'tableName’. Jeśli Twoja nazwa klasy jest taka sama jak nazwa Twojej tabeli zignoruj wartość tableName. Mamy także zdefiniowany klucz podstawowy za pomocą @PrimaryKey(autoGenerate=true). Wartość „autoGenerate” ustawiamy na true ponieważ chcemy automatycznie generowanć wartości w tej konkretnej kolumnie. Nazwy zmiennych są odwzorowane w tabeli. Jeżeli chcesz uzyć nnych nazw kolumn w tabeli niż nazwy zmiennych skorzystaj z adnotacji @ColumnInfo(name=”nazwa_kolumny”) przed zmienną.
Jeżeli chodzi o interfejs DAO, który jest opatrzony adnotacją @DAO definiuję zapytania SQL. Zapytania są oznaczone za pomocą adnotacji @Query i metody bezpośredniej, aby wstawić i usunąć artykuł z bazy danych, korzystamy z adnotacji odpowiednio @Insert i @Delete. Powinieneś znać przynajmniej podstawowe komendy języka SQL, aby móc swobodnie korzystać z zapytań do bazy danych.
Musimy także stworzyć klasę abstrakcyjną, która jest rozszerzona o RoomDatabase. Jak już wiemy, klasa Database musi być abstrakcyjna i musi rozszerzać klasę o RoomDatabase. Korzystamy z wzorca singletom aby mieć tylko jedną instancji klasy AppDatabase w całej aplikacji. Adnotacja @Database tworzy bazę danych. W wartości entities musisz określić wszystkie encje (tabele), które utworzyłeś. Na przykład oprócz artykułów mamy również klasę z komentarzami. Wtedy adnotacja może wyglądać tak:
@Database(entities = {ArticleEntity.class, CommentsEntity.class}, version = 1 )
Na koniec mamy aktywność – RoomBasicActivity. W niej przedstawiłem w jaki sposób można umieszczać i pobierać dane z bazy danych. Oprócz tego znalazła sie także metoda, która aktualizuje nam wartość premium artykyłu.