Komponen Arsitektur Android adalah kumpulan pustaka yang membantu Anda mendesain aplikasi yang tangguh, dapat diuji, dan dapat dipelihara dengan lebih banyak kekuatan atas pengelolaan siklus hidup dan persistensi data.

Pada artikel ini, kita akan fokus pada subset komponen Arsitektur Android. Kami akan mengembangkan aplikasi catatan, yang pada dasarnya mengambil beberapa masukan dari pengguna (LiveData), simpan ke dalam database lokal (Room) dan tampilkan di layar (ViewModel).
Sebelum kita mulai membangun aplikasi ini, mari kita pahami terlebih dahulu MVVM.
Apa itu MVVM?
Jika Anda terbiasa dengan MVVM, Anda dapat melewati bagian ini sepenuhnya.
MVVM adalah salah satu pola arsitektur yang meningkatkan pemisahan masalah, memungkinkan pemisahan logika antarmuka pengguna dari logika bisnis (atau back-end). Targetnya adalah untuk mencapai prinsip berikut “Menjaga kode UI tetap sederhana dan bebas dari logika aplikasi agar lebih mudah dikelola” .
Ada 3 bagian arsitektur Model-View-ViewModel:
- Model adalah lapisan data aplikasi Anda. Ini mengabstraksi sumber data.
- Tampilan berisi UI aplikasi Anda. Paling sering diimplementasikan sebagai Aktivitas atau Fragmen. View menginformasikan ViewModel tentang interaksi pengguna dan menampilkan hasil yang diterima dari ViewModel. Tampilan harus ringan dan mengandung logika bisnis nol hingga sangat sedikit.
- ViewModel berfungsi sebagai jembatan antara View dan Model Anda. Ia bekerja dengan Model untuk mendapatkan dan menyimpan data. View mengamati dan bereaksi terhadap perubahan data yang diekspos oleh ViewModel.
Berikut adalah arsitektur aplikasi MVVM tingkat tinggi yang khas:

Langkah-langkah implementasi
Kami akan mengikuti langkah-langkah ini untuk mengimplementasikan Komponen Arsitektur Android di aplikasi kami:
- Tambahkan Ketergantungan Ruang dan Siklus Hidup
- Ruang Pengaturan
- Pelajari tentang Data Langsung
- Membuat Kelas Repositori/Lapisan Presentasi
- Terapkan ViewModel
- Tambahkan Adaptor dan RecyclerView
- Mengisi Database
- Hubungkan UI dan Data
- Buat AddNoteActivity
Sekarang kita membahas langkah-langkah ini dalam urutan yang disebutkan.
1. Tambahkan Ketergantungan
Kita perlu menambahkan komponen Room dan Lifecycle . Room pada dasarnya adalah perpustakaan pemetaan objek database yang digunakan untuk mengakses database. Siklus hidup memiliki beberapa set kelas yang bagus seperti ViewModel dan LiveData yang akan kita gunakan untuk mengelola siklus hidup aplikasi kita.
Pertama tambahkan nomor versi, lalu tambahkan pustaka ini ke file build.gradle (Modul: app), di akhir blok dependensi.
dependencies {
def lifecycle_version = "1.1.1"
def room_version = "1.1.1"
---
// Room components
implementation "android.arch.persistence.room:runtime:$room_version"
kapt "android.arch.persistence.room:compiler:$room_version"
// Lifecycle components
implementation "android.arch.lifecycle:extensions:$lifecycle_version"
kapt "android.arch.lifecycle:compiler:$lifecycle_version"
}
2. Ruang Pengaturan
Ada 3 komponen utama dalam Room:
- "Kesatuan"
- “Dao”
- “Basis Data”

Entitas
Entitas hanyalah sebuah POJO yang juga akan menjadi tabel dalam database. Misalnya, Anda dapat membuat kelas POJO dan membubuhi keterangan dengan anotasi “ @Entity” . Anda juga dapat mengidentifikasi bidang mana yang merupakan kunci utama dengan “@PrimaryKey”.
@Entity(tableName = "notes_table")
data class Note(
var title: String,
var description: String
) {
@PrimaryKey(autoGenerate = true)
var id: Int = 0
}
Di sini, kita memiliki kelas Note, dan nama tabelnya adalah “ notes_table”. Kami telah membuat tiga kolom id, title, description dan juga menjadikan id sebagai kunci utama dengan membubuhi keterangan “@PrimaryKey” dan autoGenerate menjadi true.
Data Access Object (DAO)
Data Access Object (DAO) adalah antarmuka yang dianotasi dengan Dao. Di sinilah operasi database CRUD (buat, baca, perbarui, dan hapus) didefinisikan. Setiap metode dianotasi dengan “ @Insert ”, “ @Delete ”, “ @Query(SELECT * FROM) ”.
@Dao
interface NoteDao {
@Insert
fun insert(note: Note)
@Query("DELETE FROM notes_table")
fun deleteAllNotes()
@Query("SELECT * FROM notes_table ")
fun getAllNotes(): LiveData<List<Note>>
}
Di sini, kami memiliki antarmuka NoteDao dan beberapa metode yang akan kami panggil untuk melakukan kueri kami. Untuk memasukkan data, kami memberi anotasi “@Insert” untuk menyisipkan metode. Room tidak memberi kami anotasi yang dapat membantu kami menghapus semuanya sehingga kami memiliki "@Query" untuk melakukan beberapa kueri khusus.
Database
Database buat kelas yang diperluas dari RoomDatabase dan beri anotasi dengan “@Database” . Kelas ini menyatukan Entitas dan DAO. Instance database dapat dibuat saat runtime, dengan memanggil Room.databaseBuilder()
(pada perangkat) atau Room.inMemoryDatabaseBuilder()
(dalam memori).
@Database(entities = [Note::class], version = 1)
abstract class NoteDatabase : RoomDatabase() {
abstract fun noteDao(): NoteDao
private var instance: NoteDatabase? = null
fun getInstance(context: Context): NoteDatabase? {
if (instance == null) {
synchronized(NoteDatabase::class) {
instance = Room.databaseBuilder(
context.applicationContext,
NoteDatabase::class.java, "notes_database"
)
.fallbackToDestructiveMigration()
.addCallback(roomCallback)
.build()
}
}
return instance
}
}
Di sini, kami memiliki kelas NoteDatabase Room di mana kami harus mendeklarasikan semua entitas dan versi database kami. metode getInstance()
akan mengembalikan instance database ruangan. Jika Anda ingin memodifikasi database, lihat Migration.
Live Data Kelas
LiveData berasal dari lifecycle library, untuk mengamati perubahan data. Ini adalah kelas pemegang data yang dapat diamati, dan juga sadar akan siklus hidup yang berarti bahwa ini akan memperbarui komponen yang berada dalam status siklus hidup aktif .
fun getAllNotes() : LiveData<List<Note>>
Di sini, di proyek kami, di mana kami mendapatkan daftar catatan dan kami membungkus daftar dengan LiveData.
4. Membuat Kelas Repositori/Lapisan Presentasi
Ini adalah kelas di mana kami akan memeriksa apakah akan mengambil data dari API atau database lokal, atau Anda dapat mengatakan kami menempatkan logika pengambilan database di kelas ini.
class NoteRepository(application: Application) {
private var noteDao: NoteDao
private var allNotes: LiveData<List<Note>>
init {
val database: NoteDatabase = NoteDatabase.getInstance(
application.applicationContext
)!!
noteDao = database.noteDao()
allNotes = noteDao.getAllNotes()
}
fun insert(note: Note) {
val insertNoteAsyncTask = InsertNoteAsyncTask(noteDao).execute(note)
}
fun deleteAllNotes() {
val deleteAllNotesAsyncTask = DeleteAllNotesAsyncTask(
noteDao
).execute()
}
fun getAllNotes(): LiveData<List<Note>> {
return allNotes
}
private class InsertNoteAsyncTask(noteDao: NoteDao) : AsyncTask<Note, Unit, Unit>() {
val noteDao = noteDao
override fun doInBackground(vararg p0: Note?) {
noteDao.insert(p0[0]!!)
}
}
private class DeleteAllNotesAsyncTask(val noteDao: NoteDao) : AsyncTask<Unit, Unit, Unit>() {
override fun doInBackground(vararg p0: Unit?) {
noteDao.deleteAllNotes()
}
}
}
Di sini, kami menambahkan pembungkus untuk insert(), getAllNotes() dan deleteAllNotes(). Room menjalankan operasinya pada utas non-UI/utas latar belakang, jadi kami menggunakan AsyncTask.
5. TampilanModel
Ini juga merupakan bagian dari perpustakaan siklus hidup ; ini akan membantu Anda untuk menyediakan data antara repositori dan UI . Ini bertahan dari data tentang perubahan konfigurasi dan membuat ViewModel yang ada terhubung kembali dengan instance pemilik yang baru.
Mengapa menggunakan ViewModel?
- ViewModel sadar akan siklus hidup sehingga akan bertahan dari perubahan konfigurasi. Ini akan hidup lebih lama dari Aktivitas atau Fragmen.
- Komunikasi yang lebih mudah antar fragmen, daripada mengandalkan Aktivitas hosting yang meneruskan komunikasi.
- Bekerja cukup baik dengan LiveData, pemegang data yang dapat diamati.
- Anda dapat menggunakan RxJava alih-alih LiveData.
class NoteViewModel(application: Application) : AndroidViewModel(application) {
private var repository: NoteRepository =
NoteRepository(application)
private var allNotes: LiveData<List<Note>> = repository.getAllNotes()
fun insert(note: Note) {
repository.insert(note)
}
fun deleteAllNotes() {
repository.deleteAllNotes()
}
fun getAllNotes(): LiveData<List<Note>> {
return allNotes
}
}
Di sini, kami menggunakan AndroidViewModel karena kami membutuhkan konteks aplikasi. Kami membuat pembungkus insert(), deleteAllNotes() dan getAllNotes() yang akan menggunakan metode terkait repositori.
6. Tambahkan Adaptor dan RecyclerView
Buat file tata letak (note_item.xml), sertakan dua TextView untuk Judul dan Deskripsi yang dibatasi di dalam Tata Letak Linear yang memiliki orientasi vertikal dan juga sertakan RecyclerView di activity_main.xml
. Juga, jangan lupa untuk menambahkan ketergantungan Recyclerview dan CardView di build.gradle. Kita juga perlu menambahkan kelas adaptor yang bertanggung jawab untuk menampilkan data kita di layar.
class NoteAdapter : RecyclerView.Adapter<NoteAdapter.NoteHolder>() {
private var notes: List<Note> = ArrayList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoteHolder {
val itemView = LayoutInflater.from(parent.context)
.inflate(R.layout.note_item, parent, false)
return NoteHolder(itemView)
}
override fun onBindViewHolder(holder: NoteHolder, position: Int) {
val currentNote = notes[position]
holder.textViewTitle.text = currentNote.title
holder.textViewDescription.text = currentNote.description
}
override fun getItemCount(): Int {
return notes.size
}
fun setNotes(notes: List<Note>) {
this.notes = notes
notifyDataSetChanged()
}
inner class NoteHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var textViewTitle: TextView = itemView.findViewById(R.id.text_view_title)
var textViewDescription: TextView = itemView.findViewById(R.id.text_view_description)
}
}
note_item.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp">
<LinearLayout
android:id="@+id/ll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="6dp"
android:orientation="vertical">
<TextView
android:id="@+id/text_view_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:text="Title"
android:textAppearance="@style/TextAppearance.AppCompat.Large"/>
<TextView
android:id="@+id/text_view_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Description"/>
</LinearLayout>
</android.support.v7.widget.CardView>
7. Isi Database
Di sini kami mengisi data saat aplikasi dimulai dan sebelum itu kami juga menghapus data yang ada. Kami membuat PopulateDbAsyncTask yang merupakan penggunaan AsyncTask untuk menghapus dan menyisipkan data.
---
private val roomCallback = object : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
PopulateDbAsyncTask(instance)
.execute()
}
}
}
class PopulateDbAsyncTask(db: NoteDatabase?) : AsyncTask<Unit, Unit, Unit>() {
private val noteDao = db?.noteDao()
override fun doInBackground(vararg p0: Unit?) {
noteDao?.insert(Note("Title 1", "description 1"))
noteDao?.insert(Note("Title 2", "description 2"))
noteDao?.insert(Note("Title 3", "description 3"))
}
}
8. Hubungkan UI dan Data
Untuk menampilkan data dari database, kita membutuhkan pengamat yang akan mengamati perubahan data, LiveData di ViewModel. Kami menggunakan ViewModelProvider yang akan membuat ViewModel untuk kami. Kita perlu menghubungkan ViewModel kita dengan ViewModelProvider , dan kemudian dalam metode onChanged, kita selalu mendapatkan data terbaru yang dapat kita tampilkan di layar.
class MainActivity : AppCompatActivity() {
private val ADD_NOTE_REQUEST = 1
private lateinit var noteViewModel: NoteViewModel
private val adapter = NoteAdapter()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
buttonAddNote.setOnClickListener {
startActivityForResult(
Intent(this, AddNoteActivity::class.java),
ADD_NOTE_REQUEST
)
}
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.setHasFixedSize(true)
recyclerView.adapter = adapter
noteViewModel = ViewModelProviders.of(this).get(NoteViewModel::class.java)
noteViewModel.getAllNotes().observe(this,
Observer<List<Note>> { t -> adapter.setNotes(t!!) })
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.main_menu, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
return when (item?.itemId) {
R.id.delete_all_notes -> {
noteViewModel.deleteAllNotes()
Toast.makeText(this, "All notes deleted!", Toast.LENGTH_SHORT).show()
true
}
else -> {
super.onOptionsItemSelected(item)
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == ADD_NOTE_REQUEST && resultCode == Activity.RESULT_OK) {
val newNote = Note(
data!!.getStringExtra(AddNoteActivity.EXTRA_TITLE),
data.getStringExtra(AddNoteActivity.EXTRA_DESCRIPTION)
)
noteViewModel.insert(newNote)
Toast.makeText(this, "Note saved!", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, "Note not saved!", Toast.LENGTH_SHORT).show()
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activities.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/note_item"
tools:itemCount="5"/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/buttonAddNote"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_add"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:layout_gravity="bottom|right"/>
</android.support.design.widget.CoordinatorLayout>
Di sini, Di MainActivity kami menambahkan tombol luar biasa untuk membuka aktivitas lain di mana kami dapat memasukkan catatan baru, dan kami membuat metode onActivityResult di mana kami mendapatkan data yang telah dimasukkan pengguna di AddNoteActivity.
9. Buat AddNoteActivity
Kami membuat aktivitas di mana pengguna dapat memasukkan data. Oleh karena itu kami membuat AddNoteActivity .Di sini, kami memiliki dua EditText untuk Title , Description , dan Button untuk mengirimkan catatan.
AddNoteActivity.kt
class AddNoteActivity : AppCompatActivity() {
companion object {
const val EXTRA_TITLE = "com.anubhav87.mvvmtutorial.EXTRA_TITLE"
const val EXTRA_DESCRIPTION = "com.anubhav87.mvvmtutorial.EXTRA_DESCRIPTION"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_note)
supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_close)
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.add_note_menu, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
return when (item?.itemId) {
R.id.save_note -> {
saveNote()
true
}
else -> super.onOptionsItemSelected(item)
}
}
private fun saveNote() {
if (edit_text_title.text.toString().trim().isBlank() || edit_text_description.text.toString().trim().isBlank()) {
Toast.makeText(this, "Can not insert empty note!", Toast.LENGTH_SHORT).show()
return
}
val data = Intent().apply {
putExtra(EXTRA_TITLE, edit_text_title.text.toString())
putExtra(EXTRA_DESCRIPTION, edit_text_description.text.toString())
}
setResult(Activity.RESULT_OK, data)
finish()
}
}
Itu saja untuk itu sekarang. Jalankan aplikasi Anda dan coba terapkan lebih banyak fungsi ke aplikasi.