Teknik Menggunakan Dependency Injection Hilt dan Modularization pada Aplikasi Android
Kita akan menggunakan study kasus aplikasi paling sederhana agar mudah dipahami.
Part 1 : Pada study kasus Aplikasi ini dapat menambah nama user dan menyimpannya didalam room database, kemudian menampilkan nya menggunakan RecyclerView dan sudah menggunakan Modularization
Part 2: Lanjutan dari part 1 dengan menambahkan fitur lihat detail item nama user, halaman edit nama user, dan hapus nama user dan sudah menggunakan Modularization
Part 3 : Penerapan core android untuk meringkas source code dengan memodifikasi source code pada bagian part 2 dan sudah menggunakan Modularization
Sebelumnya Berjumpa lagi dengan Saya Nanda Adisaputra, S.Kom. Pada kesempatan kali ini Saya akan sharing mengenai Dependency Injection Hilt, MVVM, Data Binding , Room Database, Corountines, RecycleView, dan Modularisasi.
Yuk langsung saja Kita bahas.
— — — — — — — —
Langkah pertama mari Kita buat Project baru di Android Studio dengan kriteria sebagai berikut:
Nama Project : SimpleModularization, Target & Minimum Target SDK : Phone and Tablet, Api level 23, Tipe Activity : Empty Activity, Activity Name : MainActivity, Language : Kotlin
Langkah selanjutnya buatlah beberapa package dengan nama adapter, base, data, injection, room, dan ui. Kemudian didalam package data, buatlah package lagi dengan nama model dan room. Caranya yaitu dengan klik kanan klik kanan pada nama package -> New -> Package
Tambahkan library berikut ini di build.gradle(Module:App) tepatnya harus didalam dependencies { }
/* tambahkan library dibawah ini untuk implementasi dependency injection hilt*/
implementation 'com.google.dagger:hilt-android:2.44'
kapt 'com.google.dagger:hilt-compiler:2.44'
/* tambahkan library dibawah ini untuk implementasi room database */
implementation 'androidx.room:room-ktx:2.5.1'
implementation 'androidx.appcompat:appcompat:1.6.1'
kapt 'androidx.room:room-compiler:2.5.1'
/* tambahkan ktx untuk implementasi by viewmodels() */
implementation 'androidx.fragment:fragment-ktx:1.5.7'
Setelah menambahkan library diatas. Kemudian tambahkan source code dibawah ini untuk mengaktifkan databinding dan viewbinding.
buildFeatures {
/* untuk mengaktifkan databinding dan viewbinding */
dataBinding true
viewBinding true
}
Lalu tambahkan plugin hilt, kotlin kapt, dan parcelize seperti dibawah ini.
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
/* tambahkan plugin dibawah ini */
id 'com.google.dagger.hilt.android'
id 'kotlin-kapt'
id 'kotlin-parcelize'
}
Jangan lupa klik Sync Now setiap ada perubahan dibagian gradle
Sehingga tampilan keseluruhan source code dibagian build.gradle(Module:App) sebagai berikut ini.
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
/* tambahkan plugin dibawah ini */
id 'com.google.dagger.hilt.android'
id 'kotlin-kapt'
id 'kotlin-parcelize'
}
android {
namespace 'com.nandaadisaputra.simplemodularization'
compileSdk 33
defaultConfig {
applicationId "com.nandaadisaputra.simplemodularization"
minSdk 23
targetSdk 33
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
packagingOptions {
resources.excludes.add("META-INF/*")
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
/* untuk mengaktifkan databinding dan viewbinding */
dataBinding true
viewBinding true
}
}
dependencies {
/* tambahkan library dibawah ini untuk implementasi dependency injection hilt*/
implementation 'com.google.dagger:hilt-android:2.44'
kapt 'com.google.dagger:hilt-compiler:2.44'
/* tambahkan library dibawah ini untuk implementasi room database */
implementation 'androidx.room:room-ktx:2.5.1'
implementation 'androidx.appcompat:appcompat:1.6.1'
kapt 'androidx.room:room-compiler:2.5.1'
/* tambahkan ktx untuk implementasi by viewmodels() */
implementation 'androidx.fragment:fragment-ktx:1.5.7'
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.6.0'
implementation 'com.google.android.material:material:1.8.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}
Kita akan berpindah ke pada build.gradle(Project:App) untuk menambahkan beberapa source code.
Sebelum Kita tambahkan source code apapun secara default akan seperti berikut.
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id 'com.android.application' version '7.3.1' apply false
id 'com.android.library' version '7.3.1' apply false
id 'org.jetbrains.kotlin.android' version '1.8.0' apply false
}
Nah dari default itu Kita akan tambahkan task clean, dan beberapa id didalam plugins sebagai berikut ini.
plugins {
id 'com.android.application' version '7.3.1' apply false
id 'com.android.library' version '7.3.1' apply false
id 'org.jetbrains.kotlin.android' version '1.8.0' apply false
/* untuk mengimplementasikan dependency injection hilt */
id 'com.google.dagger.hilt.android' version '2.44' apply false
}
/* untuk clean cache */
task clean(type: Delete) {
delete rootProject.buildDir
}
Jangan lupa klik Sync Now kembali setiap ada perubahan di gradle.
Yey, untuk konfigurasi dibagian gradle sudah kelar, saatnya Kita mulai berpindah ke package yang telah Kita buat sebelumnya.
Pertama tama mari Kita buat class App.kt didalam package base. Caranya yaitu dengan klik kanan pada package base -> New -> Kotlin Class / File
Tambahkan source code dibawah ini didalam class App.kt
@HiltAndroidApp
class App: Application()
Langkah selanjutnya tambahkan android:name didalam AndroidManifest.xml agar terbaca oleh sistem bahwa Kita menggunakan DI Hilt.
android:name=".base.App"
Sehingga keseluruhan source code dibagian AndroidManifest.xml sebagai berikut.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!--TODO Karena Kita menggunakan DI Hilt maka Kita tambahkan android:name=".base.App"
di dalam <application agar terbaca sistem kalau Kita menggunakan core bawaan DI Hilt-->
<application
android:name=".base.App"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.SimpleModularization"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
</application>
</manifest>
Selanjutnya buatlah class Users.kt dan interface UserDao.kt didalam package model yang berada didalam package data. Caranya yaitu dengan klik kanan pada package model ->New -> Kotlin Class / File
Langkah berikutnya tambahkan source code dibawah ini dibagian Users.kt untuk membuat table. Beri nama table yang Kita buat dengan nama users.
import android.os.Parcelable
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import kotlinx.parcelize.Parcelize
/*bungkus dengan interface Parcelable untuk Implementasi Intent mengirim Data dengan Parcelable."*/
@Parcelize
@Entity(tableName = "users")
data class Users(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id")
val id: Int = 0,
@ColumnInfo(name = "username")
val username: String?
) : Parcelable
Selanjutnya Kita juga akan menambahkan source code dibagian interface UsersDao.kt sebagai berikut.
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import kotlinx.coroutines.flow.Flow
@Dao
interface UsersDao {
/*untuk menyimpan data*/
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(users: Users)
/*untuk mengambil data*/
@Query("SELECT * FROM users ORDER BY id DESC")
fun getAllUsername(): Flow<List<Users>>
}
Mari Kita berpindah ke class UsersDatabase.kt didalam package room. Caranya yaitu dengan klik kanan pada package room ->New -> Kotlin Class / File
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import com.nandaadisaputra.simplemodularization.data.model.Users
import com.nandaadisaputra.simplemodularization.data.model.UsersDao
@Database(
entities = [Users::class],
/*versi database. Silahkan ubah versi yang lebih tinggi ketika
kalian mengubah tabel pada class Users*/
version = 1
)
abstract class UsersDatabase : RoomDatabase() {
abstract fun userDao(): UsersDao
companion object {
@Volatile
private var INSTANCE: UsersDatabase? = null
@Synchronized
fun getInstance(context: Context): UsersDatabase {
val tempInstance = INSTANCE
if (tempInstance != null) {
return tempInstance
}
val instance = Room.databaseBuilder(context.applicationContext, UsersDatabase::class.java, "Users_Database.db")
.fallbackToDestructiveMigration()
.build()
INSTANCE = instance
return instance
}
}
}
Note : Setiap ada perubahan dibagian class Users.kt kalian wajib menambahkan yang lebih tinggi . Misal nya version = 1 menjadi version = 2 , dan seterusnya.
Selanjutnya Kita akan menambahkan layout dengan nama item_user.xml. Caranya yaitu dengan klik kanan pada package layout -> New -> Layout Resource File -> Kemudian beri nama file dengan nama item_users -> Klik OK.
Kemudian tambahkan source code berikut ini.
<?xml version="1.0" encoding="utf-8"?>
<!--TODO Tambahkan <layout><data></data></layout>
karena akan menggunakan DataBinding-->
<layout>
<data>
<variable
name="users"
type="com.nandaadisaputra.simplemodularization.data.model.Users" />
</data>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{users.username}'
android:textSize="18sp"
tools:text="Nanda Adisaputra" />
<!--satuan ukuran teks gunakan satuan sp dan untuk satuan ukuran selain teks
seperti padding dan margin gunakan satuan dp-->
<!-- tools:text=""membuat teks yang kalian jadikan value dari child
akan bersifat dinamis jadi ketika dirunning tidak ada datanya maka
tidak akan menampilkan teks Nanda Adisaputra seperti pada contoh diatas-->
</LinearLayout>
</layout>
Langkah berikut nya adalah membuat class UsersAdapter.kt didalam package adapter. Caranya yaitu dengan klik kanan pada package adapter -> New -> Kotlin Class / File. Kemudian tambahkan source code dibawah ini didalam class UsersAdapter.kt
import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.nandaadisaputra.simplemodularization.R
import com.nandaadisaputra.simplemodularization.data.model.Users
import com.nandaadisaputra.simplemodularization.databinding.ItemUsersBinding
class UsersAdapter: ListAdapter<Users, UsersAdapter.ItemViewHolder>(DiffUtilCallback()) {
private var onItemClick: ((Users) -> Unit)? = null
fun setOnClickItem(onClickItem: (Users) -> Unit) {
this.onItemClick = onClickItem
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
val binding = DataBindingUtil.inflate<ItemUsersBinding>(
LayoutInflater.from(parent.context),
R.layout.item_users,
parent,
false
)
return ItemViewHolder(binding)
}
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
getItem(position)?.let { users ->
holder.bind(users)
holder.itemView.setOnClickListener {
onItemClick?.invoke(users)
}
}
}
inner class ItemViewHolder(private val binding: ItemUsersBinding): RecyclerView.ViewHolder(binding.root) {
fun bind(users: Users) {
/*users diambil dari <variable name="users" yang berada di layout item_users.xml*/
binding.users = users
binding.executePendingBindings()
}
}
class DiffUtilCallback<T: Any> : DiffUtil.ItemCallback<T>() {
override fun areItemsTheSame(oldItem: T, newItem: T): Boolean {
return oldItem == newItem
}
@SuppressLint("DiffUtilEquals")
override fun areContentsTheSame(oldItem: T, newItem: T): Boolean {
return newItem === oldItem
}
}
}
Berikutnya Kita akan membuat class DataModule.kt didalam package injection. Caranya yaitu dengan klik kanan pada package injection -> New ->Kotlin Class / File. Kemudian tambahkan source code berikut ini didalam class DataModule.
import android.content.Context
import androidx.room.Room
import com.nandaadisaputra.simplemodularization.data.model.UsersDao
import com.nandaadisaputra.simplemodularization.data.room.UsersDatabase
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
class DataModule {
@Provides
fun provideAppDatabase(@ApplicationContext context: Context) = UsersDatabase.getInstance(context)
@Provides
fun provideUsersDao(database: UsersDatabase): UsersDao = database.userDao()
}
Langkah berikutnya agar lebih rapi pindahkan kan class MainActivity.kt dengan cara di drag ke package ui seperti berikut ini. Kemudian klik Refactor
Selanjutnya lakukan Clean Project dengan cara klik Build -> Clean Project .
Setelah proses Clean Project selesai lakukan juga rebuild project. Caranya yaitu dengan klik Build -> Rebuild Project.
Sehingga untuk struktur projectnya sementara akan seperti berikut ini.
Langkah selanjutnya tambahkan class MainViewModel.kt didalam package ui. Caranya yaitu dengan klik kanan pada package ui -> New -> Kotlin Class / File. Kemudian tambahkan source code berikut ini didalam class MainViewModel.kt.
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.nandaadisaputra.simplemodularization.data.model.Users
import com.nandaadisaputra.simplemodularization.data.model.UsersDao
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
import javax.inject.Inject
/*Karena menggunakan DI Hilt maka Kita wajib menambahkan anotasi @HiltViewModel*/
@HiltViewModel
class MainViewModel @Inject constructor(private val nameDao: UsersDao): ViewModel() {
/*Kita berikan isi variabel users dengan perintah mengambil data*/
/*Karena nilainya tetap tidak berubah ubah value nya maka kita pakai val bukan var*/
val users = nameDao.getAllUsername()
/*Kita tuliskan type Private karena hanya diakses oleh class ini*/
private val _responseSave = Channel<Boolean>()
/*Untuk mengetahui respon save*/
val responseSave = _responseSave.receiveAsFlow()
/*Untuk menambahkan user ke table*/
fun addUsers(users: Users) = viewModelScope.launch {
nameDao.insert(users)
_responseSave.send(true)
}
}
Langkah selanjutnya Kita akan berpindah ke layout activity_main.xml. Lakukan pengeditan sehingga untuk keseluruhan source code dibagianactivity_main.xml akan seperti berikut ini.
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable
name="adapter"
type="com.nandaadisaputra.simplemodularization.adapter.UsersAdapter" />
<variable
name="activity"
type="com.nandaadisaputra.simplemodularization.ui.MainActivity" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.MainActivity">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/edt_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/username"
android:text='@={activity.usernameStudent}'
android:autofillHints="username"
android:inputType="textPersonName"
android:padding="16dp"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_save"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/save"
android:onClick='@{() -> activity.saveUsername()}'
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/edt_username" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:clipToPadding="false"
android:orientation="vertical"
android:adapter='@{adapter}'
tools:listitem="@layout/item_users"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_save" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Nah sampai disini pasti kalian akan menjumpai error bagian android:hint=”@string/username” dan android:text=”@string/save”.
Maka Kita perlu menambahkan resource String seperti berikut ini.
<resources>
<string name="app_name">SimpleModularization</string>
<!-- Tambahkan Resource String berikut ini -->
<string name="username">username</string>
<string name="save">Save</string>
</resources>
Sampai disini kalian pasti akan menjumpai error dibagian android:text=’@={activity.usernameStudent}’ dan android:onClick=’@{() -> activity.saveUsername()}’ . Hal ini terjadi karena Kita belum memanggil fungsi saveUsername dan usernameStudent di MainActivity.kt. Karena Kita menggunakan databinding makan kalian wajib menambahkan.
<?xml version=”1.0" encoding=”utf-8"?>
<layout>
<data>
<variable/>
<variable/>
</data>
// Kemudian baru parentview, View dan juga widget
</layout>
Langkah selanjutnya Kita akan memodifikasi dibagian MainActivity.kt dengan source code berikut ini.
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.activity.viewModels
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.nandaadisaputra.simplemodularization.R
import com.nandaadisaputra.simplemodularization.adapter.UsersAdapter
import com.nandaadisaputra.simplemodularization.data.model.Users
import com.nandaadisaputra.simplemodularization.databinding.ActivityMainBinding
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
/*Karena menggunakan DI Hilt maka Kita wajib menambahkan anotasi @AndroidEntryPoint*/
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
/*Kita deklarasikan mainViewModel terlebih dahulu*/
private val mainViewModel: MainViewModel by viewModels()
/*Kita deklarasikan juga binding yang akan Kita gunakan*/
private val binding: ActivityMainBinding by lazy {
DataBindingUtil.setContentView(this, R.layout.activity_main)
}
/*Berikutnya deklarasikan juga adapternya*/
private val adapter = UsersAdapter()
/*Jangan lupa deklarasikan usernameStudent*/
/* username diambil dari xml di activity_main dari android:text='@={activity.usernameStudent}'*/
/*Karena Kita menerapkan DataBinding*/
var usernameStudent = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
/*inisialisasi dulu activitynya*/
/*wajib karena Kita memakai DataBinding*/
binding.activity = this
binding.lifecycleOwner = this
/*Kita akan memakai fungsi initData() dan observe()
* maka perlu Kita deklarasikan dulu */
initData()
observe()
}
private fun initData() {
/*Ketika salah satu item di List diklik akan memunculkan aksi*/
adapter.setOnClickItem { name ->
/*Disini Saya kasih aksi memunculkan Toast*/
/*Kalian bisa menambahkan sendiri Intent ke Detail Activity dibagian ini*/
Toast.makeText(this, "Ini namanya ${name.username}", Toast.LENGTH_SHORT).show()
}
/*Jangan lupa setelah deklarasi di inisialisasi adapter yak*/
binding.adapter = adapter
}
private fun observe() {
/*Karena menggunakan Coroutines kita dapat memanggil lifecycleScope.launch */
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
/*panggil variabel user dari viewmodel yang isinya perintah menampilkan data*/
mainViewModel.users.collect { username ->
/*Pada bagian ini digunakan untuk menampilkan data */
adapter.submitList(username)
}
/*Kita panggil responseSave untuk menampilkan respon ketika data tersimpan */
mainViewModel.responseSave.collect { success ->
if (success) {
/*Katika berhasil tersimpan datanya akan muncul pesan Berhasil Menyimpan Data. */
Toast.makeText(this@MainActivity, "Berhasil Menyimpan Data.", Toast.LENGTH_SHORT).show()
}
}
}
}
}
}
fun saveUsername() {
/*Apabila inputan kosong / tidak ada inputan */
if (usernameStudent.isEmpty()) {
/*Akan menampilan pesan Form Masih Kosong Lur. */
Toast.makeText(this, "Form Masih Kosong Lur.", Toast.LENGTH_SHORT).show()
return
}
/*Kita inisialisasi variabel newUsername isinya adalah class Users*/
val newUsername = Users(username = usernameStudent)
/*Kita panggil addUsers untuk menyimpan data ke tabel Users*/
mainViewModel.addUsers(newUsername)
}
}
Maka untuk untuk struktur project akan seperti berikut ini.
Nah saatnya Kita running aplikasinya .
Untuk keseluruhan source code diatas sebagai berikut ini.
PART 2 ( MODULARIZATION )
Selanjutnya saatnya Kita belajar modularisasi
Pertama tama Kita membuat Module baru terlebih dahulu. Caranya yaitu dengan klik File -> New -> New Module -> Android Library ->beri nama Module dengan nama core -> edit package name dengan ditambahkan simplemodularization -> Klik Finish
Maka akan menjadi seperti berikut struktur project nya
Selanjutnya karena Kita akan menggunakan module core pada module app, maka Kita harus menambahkan source code berikut pada build.gradle (Module: app)
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation project(":core")
Untuk mengantisipasi terjadinya dependency yang sama antara module core dengan module utama misal seperti Dagger Hilt. Kita akan membuat file untuk sharing dependency yang akan digunakan di banyak module.
Caranya yaitu dengan klik kanan pada build.gradle(Project:App) -> New -> File-> Beri nama dengan nama shared_dependencies.gradle -> Klik Enter
Maka strukturnya akan menjadi seperti berikut ini.
Selanjutnya pindahkan seluruh library selain
implementation fileTree(dir: “libs”, include: [“*.jar”])
implementation project(“:core”)
di build.gradle (Module: app) ke shared_dependencies.gradle(Project:App)
Sehingga yang tersisa sebagai berikut
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation project(":core")
}
Perlu diketahui bahwa ViewBinding tetap harus diaktifkan di setiap modul yang membutuhkan ViewBinding. Dan perlu ditambahkan apply kapt dan shared_dependencies yang baru saja dibuat
apply from: '../shared_dependencies.gradle'
Nah untuk keseluruhan source code dibagian build.gradle (Module: app) akan seperti ini.
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
/* tambahkan plugin dibawah ini */
id 'com.google.dagger.hilt.android'
id 'kotlin-kapt'
/* karena di module app tidak butuh kotlin-parcelize jadi
Saya hapus disini, karena yang pakai cuma module core
karena nanti untuk package data,adapter dan injection akan Kita pindahkan
ke package core di module core*/
}
//TODO Tambahkan source code berikut ini//
apply from: '../shared_dependencies.gradle'
android {
namespace 'com.nandaadisaputra.simplemodularization'
compileSdk 33
defaultConfig {
applicationId "com.nandaadisaputra.simplemodularization"
minSdk 23
targetSdk 33
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
packagingOptions {
resources.excludes.add("META-INF/*")
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
/* untuk mengaktifkan databinding dan viewbinding */
dataBinding true
viewBinding true
}
}
dependencies {
//TODO Tambahkan source code berikut ini//
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation project(":core")
}
Sedangkan untuk bagian build.gradle (Module: core ) keseluruhan codenya akan seperti ini.
plugins {
id 'com.android.library'
id 'org.jetbrains.kotlin.android'
/* tambahkan plugin dibawah ini */
id 'com.google.dagger.hilt.android'
id 'kotlin-kapt'
id 'kotlin-parcelize'
}
//TODO Tambahkan source code berikut ini//
apply from: '../shared_dependencies.gradle'
android {
namespace 'com.nandaadisaputra.simplemodularization.core'
compileSdk 33
defaultConfig {
minSdk 23
targetSdk 33
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
packagingOptions {
resources.excludes.add("META-INF/*")
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
/* untuk mengaktifkan databinding dan viewbinding */
dataBinding true
viewBinding true
}
}
dependencies {
//TODO Tambahkan source code berikut ini//
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation 'androidx.core:core-ktx:1.10.1'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.9.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}
Kemudian untuk bagian shared_dependencies.gradle(Project:App) keseluruhan codenya akan seperti ini.
dependencies {
/* tambahkan library dibawah ini untuk implementasi dependency injection hilt*/
implementation 'com.google.dagger:hilt-android:2.44'
kapt 'com.google.dagger:hilt-compiler:2.44'
/* tambahkan ktx untuk implementasi by viewmodels() */
implementation 'androidx.fragment:fragment-ktx:1.5.7'
/* tambahkan library dibawah ini untuk implementasi room database */
implementation 'androidx.room:room-ktx:2.5.1'
implementation 'androidx.appcompat:appcompat:1.6.1'
kapt 'androidx.room:room-compiler:2.5.1'
implementation 'androidx.core:core-ktx:1.10.1'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.9.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}
Selanjutnya Kita akan membuat Resource Directory baru di dalam module core. Caranya yaitu dengan klik kanan pada module core -> New -> Android Resource Directory ->Pilih Resource type layout dan directory nam layout -> Klik Ok.
Langkah berikutnya Kita akan memindahkan layout item_users.xml dari module app ke dalam layout di dalam module core. Caranya yaitu dengan drag layout item_users.xml tersebut ke package layout di dalam modul core seperti pada gambar berikut ini , kemudian klik Refactor -> Continue
Kemudian Kita juga akan pindah kan package adapter, package base, package data, dan juga package injection yang berada didalam module app ke package core yang berada di module core. Caranya yaitu dengan drag ke package core -> Refactor -> Klik Continue
Sehingga Struktur Project nya akan berubah menjadi seperti berikut ini.
Nah pasti kalian akan mengalami error di UsersAdapter.kt, silahkan dapat disesuaikan untuk importnya yang tadi nya
import com.nandaadisaputra.simplemodularization.R
import com.nandaadisaputra.simplemodularization.databinding.ItemUsersBinding
akan diubah menjadi berikut ini
import com.nandaadisaputra.simplemodularization.core.R
import com.nandaadisaputra.simplemodularization.core.databinding.ItemUsersBinding
Kemudian untuk layout item_users.kt Kita sesuaikan yang tadinya
<layout>
<data>
<variable
name="users"
type="com.nandaadisaputra.simplemodularization.data.model.Users" />
</data>
Akan diubah menjadi
<layout>
<data>
<variable
name="users"
type="com.nandaadisaputra.simplemodularization.core.data.model.Users" />
</data>
Yey, sudah tidak ada error dibagian UsersAdapter.kt kan.
Sebelum Kita running sebaiknya Kita clean project dulu yuk temen temen
Selanjutnya setelah proses Clean Project selesai. Kita lakukan Rebuild Project.
Selanjutnya Kita optimize_import di modul app, setelah itu Kita juga optimize_import di modul core.
Yuk, saatnya Kita running kembali aplikasi Kita.
Yey Kita telah berhasil running aplikasi tanpa error sedikitpun.
Untuk keseluruhan code pada penerapan Part 1 Modularization diatas sebagai berikut :
PART 3 Menambahkan Fitur Lihat Detail Item, Edit , dan Delete
Langkah selanjutnya buatlah beberapa package didalam package ui dengan nama edit, detail, dan home. Kemudian didalam package data, buatlah package lagi dengan nama constant. Caranya yaitu dengan klik kanan klik kanan pada nama package -> New -> Package
Selanjutnya drag and drop class MainActivity.kt dan MainViewModel.kt diatas kedalam package home dengan cara drag drop ->klik button refactor. Sehingga akan menjadi seperti berikut ini
Selanjutnya Kita akan menambahkan class dengan nama Const.kt. Caranya yaitu dengan klik kanan pada package constant-> New -> Kotlin Class / File-> Kemudian beri nama file dengan nama Const-> Klik Enter.
Kemudian tambahkan source code berikut ini didalam class Constant.kt.
class Const {
object TOAST {
const val SAVE= "Berhasil Menyimpan Data."
const val DELETE= "Berhasil Menghapus Data."
const val UPDATE= "Berhasil Mengubah Data."
}
object ID {
const val ID_USERNAME = "id_username"
}
object VALIDATION {
const val EMPTY = "Masih Ada Form yang Kosong."
}
}
Sehingga pada MainActivity.kt untuk bagian teks String dapat kalian ubah menjadi seperti berikut ini.
Selanjutnya Kita akan menambahkan class dengan nama DetailActivity.kt. Caranya yaitu dengan klik kanan pada package detail-> New -> Activity-> Kemudian beri nama file dengan nama DetailActivity-> Klik Finish.
Lakukan juga cara yang sama untuk menambahkan class dengan nama EditActivity.kt didalam package edit. Caranya yaitu dengan klik kanan pada package edit-> New -> Activity-> Kemudian beri nama file dengan nama EditActivity-> Klik Finish.
Langkah selanjutnya Class ViewModel dengan nama DetailViewModel.kt . Caranya yaitu dengan klik kanan pada package detail-> New -> Kotlin Class / File-> Kemudian beri nama file dengan nama DetailViewModel-> Klik Enter.
Lakukan hal yang sama untuk membuat Class ViewModel dengan nama EditViewModel.kt . Caranya yaitu dengan klik kanan pada package edit-> New -> Kotlin Class / File-> Kemudian beri nama file dengan nama EditViewModel-> Klik Enter.
Sehingga tampilannya akan seperti berikut ini.
Langkah selanjutnya Kita akan berpindah ke layout activity_detail.xml. Lakukan pengeditan sehingga untuk keseluruhan source code dibagian activity_detail.xml akan seperti berikut ini.
<?xml version="1.0" encoding="utf-8"?>
<!--TODO Tambahkan <layout><data></data></layout>
karena akan menggunakan DataBinding-->
<layout>
<data>
<variable
name="activity"
type="com.nandaadisaputra.simplemodularization.ui.detail.DetailActivity" />
<variable
name="users"
type="com.nandaadisaputra.simplemodularization.core.data.model.Users" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.detail.DetailActivity">
<TextView
android:id="@+id/tv_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autofillHints="username"
android:padding="16dp"
android:text='@{users.username}'
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_update"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick='@{() -> activity.updateUsername()}'
android:text="@string/update"
app:layout_constraintEnd_toEndOf="@id/btn_delete"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_username" />
<Button
android:id="@+id/btn_delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick='@{() -> activity.deleteUsername()}'
android:text="@string/delete"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/btn_update"
app:layout_constraintTop_toBottomOf="@id/tv_username" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Pada text String diatas masih terlihat merah karena Kita belum memasukkan teks tersebut kedalam strings.xml. Maka Kita akan menambahkan text Strings tersebut seperti dibawah ini.
<resources>
<string name="app_name">SimpleModularization</string>
<string name="username">username</string>
<string name="save">Save</string>
<!-- Tambahkan Resource String berikut ini -->
<string name="delete">Delete</string>
<string name="update">Update</string>
</resources>
Langkah selanjutnya Kita akan berpindah ke layout activity_edit.xml. Lakukan pengeditan sehingga untuk keseluruhan source code dibagian activity_edit.xml akan seperti berikut ini.
<?xml version="1.0" encoding="utf-8"?>
<!--TODO Tambahkan <layout><data></data></layout>
karena akan menggunakan DataBinding-->
<layout>
<data>
<variable
name="activity"
type="com.nandaadisaputra.simplemodularization.ui.edit.EditActivity" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.edit.EditActivity">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/edt_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/username"
android:text='@={activity.usernameStudent}'
android:autofillHints="username"
android:inputType="textPersonName"
android:padding="16dp"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_save"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/update"
android:onClick='@{() -> activity.updateUsername()}'
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/edt_username" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Langkah selanjutnya Kita akan berpindah ke class UsersDao.kt yang terletak di data -> didalam package model. Tambahkan source code dibawah pada class tersebut
/*Tambahkan beberapa source code dibawah ini untuk tutorial part 2*/
/*untuk mengambil 1 data*/
@Query("SELECT * FROM users WHERE id = :id")
fun getUsername(id: Int): Flow<Users>
/*untuk delete data*/
@Query("DELETE FROM users WHERE id = :id")
suspend fun delete(id: Int)
/*untuk update data*/
@Update()
suspend fun update(users: Users)
Sehingga keseluruhan source code pada class UsersDao.kt akan menjadi sebagai berikut ini.
@Dao
interface UsersDao {
/*untuk menyimpan data*/
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(users: Users)
/*untuk mengambil seluruh data*/
@Query("SELECT * FROM users ORDER BY id DESC")
fun getAllUsername(): Flow<List<Users>>
/*Tambahkan beberapa source code dibawah ini untuk tutorial part 2*/
/*untuk mengambil 1 data*/
@Query("SELECT * FROM users WHERE id = :id")
fun getUsername(id: Int): Flow<Users>
/*untuk delete data*/
@Query("DELETE FROM users WHERE id = :id")
suspend fun delete(id: Int)
/*untuk update data*/
@Update()
suspend fun update(users: Users)
}
Langkah selanjutnya tambahkan source code dibawah ini didalam class DetailViewModel.kt pada package ui.
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.nandaadisaputra.simplemodularization.core.data.model.UsersDao
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch
import javax.inject.Inject
/*Karena menggunakan DI Hilt maka Kita wajib menambahkan anotasi @HiltViewModel*/
@HiltViewModel
class DetailViewModel @Inject constructor(private val nameDao: UsersDao): ViewModel() {
/*Karena nilainya tetap tidak berubah ubah value nya maka kita pakai val bukan var*/
/*Kita tuliskan type Private karena hanya diakses oleh class ini*/
private val _responseDelete = MutableSharedFlow<Boolean>()
/*Untuk mengetahui respon delete*/
val responseDelete = _responseDelete.asSharedFlow()
/*Untuk mengambil data user dari table*/
fun getUsername(idUsers: Int) = nameDao.getUsername(idUsers)
/*Untuk delete user dari table*/
fun deleteUsername(idUsers: Int) = viewModelScope.launch {
nameDao.delete(idUsers)
_responseDelete.emit(true)
}
}
Kemudian tambahkan source code dibawah ini didalam class EditViewModel.kt pada package ui.
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.nandaadisaputra.simplemodularization.core.data.model.Users
import com.nandaadisaputra.simplemodularization.core.data.model.UsersDao
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class EditViewModel @Inject constructor(private val nameDao: UsersDao): ViewModel() {
/*Karena nilainya tetap tidak berubah ubah value nya maka kita pakai val bukan var*/
/*Kita tuliskan type Private karena hanya diakses oleh class ini*/
private val _responseSave = MutableSharedFlow<Boolean>()
/*Untuk mengetahui respon save*/
val responseSave = _responseSave.asSharedFlow()
/*Untuk mengambil data user dari table*/
fun getUsers(idUsers: Int) = nameDao.getUsername(idUsers)
/*Untuk menambahkan user ke table*/
fun addUsers(users: Users) = viewModelScope.launch {
nameDao.insert(users)
_responseSave.emit(true)
}
}
Langkah selanjutnya Kita akan berpindah ke class DetailActivity.kt. Lakukan pengeditan sehingga untuk keseluruhan source code dibagian class DetailActivity.kt akan seperti berikut ini.
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.nandaadisaputra.simplemodularization.R
import com.nandaadisaputra.simplemodularization.core.data.constant.Const
import com.nandaadisaputra.simplemodularization.databinding.ActivityDetailBinding
import com.nandaadisaputra.simplemodularization.ui.edit.EditActivity
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
/*Karena menggunakan DI Hilt maka Kita wajib menambahkan anotasi @AndroidEntryPoint*/
@AndroidEntryPoint
class DetailActivity : AppCompatActivity() {
/*Kita deklarasikan detailViewModel terlebih dahulu*/
private val detailViewModel: DetailViewModel by viewModels()
/*Kita deklarasikan juga binding yang akan Kita gunakan*/
private val binding: ActivityDetailBinding by lazy {
DataBindingUtil.setContentView(this, R.layout.activity_detail)
}
/*deklarasikan variabel idUsername dengan diberi isi secara default value 0*/
private var idUsername = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
/*inisialisasi dulu activitynya*/
/*wajib karena Kita memakai DataBinding*/
binding.activity = this
binding.lifecycleOwner = this
idUsername = intent.getIntExtra(Const.ID.ID_USERNAME, 0)
/*Kita akan memakai fungsi observe()
* maka perlu Kita deklarasikan dulu */
observe()
}
private fun observe() {
/*Karena menggunakan Coroutines kita dapat memanggil lifecycleScope.launch */
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
/*panggil fungsi getUsername dengan param id Username dari viewmodel yang isinya perintah menampilkan data username*/
detailViewModel.getUsername(idUsername).collect { username ->
/*untuk menampilkan data username*/
binding.users = username
}
}
launch {
/*panggil variabel responseDelete dari ViewModel*/
detailViewModel.responseDelete.collect { success ->
//jika delete sukses
if (success) {
/*Ketika berhasil delete datanya akan muncul pesan Berhasil Menghapus Data. */
Toast.makeText(
this@DetailActivity,
Const.TOAST.DELETE,
Toast.LENGTH_SHORT
).show()
/*Beri finish() agar anda tidak dapat kembali ke aktivitas sebelumnya dengan tombol "kembali".*/
finish()
}
}
}
}
}
}
fun updateUsername() {
/*Ketika button update di klik maka akan berpindah ke halaman EditActivity dengan membawa data id username. */
val editIntent = Intent(this, EditActivity::class.java).apply {
/*Untuk membawa data ID Username ketika terjadi perpindahan activity*/
putExtra(Const.ID.ID_USERNAME, idUsername)
}
startActivity(editIntent)
}
fun deleteUsername() {
/*Ketika button delete di klik maka akan terhapus datanya. */
detailViewModel.deleteUsername(idUsername)
}
}
Langkah selanjutnya Kita akan berpindah ke class EditActivity.kt. Lakukan pengeditan sehingga untuk keseluruhan source code dibagian class EditActivity.kt akan seperti berikut ini.
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.nandaadisaputra.simplemodularization.R
import com.nandaadisaputra.simplemodularization.core.data.constant.Const
import com.nandaadisaputra.simplemodularization.core.data.model.Users
import com.nandaadisaputra.simplemodularization.databinding.ActivityEditBinding
import com.nandaadisaputra.simplemodularization.ui.home.MainActivity
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
/*Karena menggunakan DI Hilt maka Kita wajib menambahkan anotasi @AndroidEntryPoint*/
@AndroidEntryPoint
class EditActivity : AppCompatActivity() {
/*Kita deklarasikan editViewModel terlebih dahulu*/
private val editViewModel: EditViewModel by viewModels()
/*Kita deklarasikan juga binding yang akan Kita gunakan*/
private val binding: ActivityEditBinding by lazy {
DataBindingUtil.setContentView(this, R.layout.activity_edit)
}
/*deklarasikan variabel idUsername dengan diberi isi secara default value 0*/
private var idUsername = 0
/*Jangan lupa deklarasikan usernameStudent*/
/* username diambil dari xml di activity_edit dari android:text='@={activity.usernameStudent}'*/
/*Karena Kita menerapkan DataBinding*/
var usernameStudent = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
title = getString(R.string.app_name)
/*inisialisasi dulu activitynya*/
/*wajib karena Kita memakai DataBinding*/
binding.activity = this
binding.lifecycleOwner = this
/*Kita inisialisasi variabel idUsername yang isinya adalah menerima data id username yang dibawa dari MainActivity.kt*/
idUsername = intent.getIntExtra(Const.ID.ID_USERNAME, 0)
/*Kita akan memakai fungsi observe()
* maka perlu Kita deklarasikan dulu */
observe()
}
private fun observe() {
/*Karena menggunakan Coroutines kita dapat memanggil lifecycleScope.launch */
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
/*panggil variabel responseDelete dari ViewModel*/
editViewModel.responseSave.collect { success ->
if (success) {
/*Ketika berhasil tersimpan datanya akan muncul pesan Berhasil Mengubah Data. */
Toast.makeText(this@EditActivity,Const.TOAST.UPDATE, Toast.LENGTH_SHORT).show()
/*Beri finish() agar anda tidak dapat kembali ke aktivitas sebelumnya dengan tombol "kembali".*/
finish()
}
}
}
/*Pengecekan jika id username tidak sama dengan 0*/
if (idUsername != 0) {
launch {
/*panggil fungsu getUsers dengan param id Username dari ViewModel*/
editViewModel.getUsers(idUsername).collect { users ->
/*menampilkan teks username sebelum proses edit, sehingga tidak perlu ketik ulang
* semuanya*/
usernameStudent = users.username ?: ""
binding.activity = this@EditActivity
}
}
}
}
}
}
fun updateUsername() {
/*Apabila inputan kosong / tidak ada inputan */
if (usernameStudent.isEmpty()) {
/*Ketika formnya kosong akan menampilkan pesan toast Masih ada form yang kosong. . */
Toast.makeText(this@EditActivity,Const.VALIDATION.EMPTY, Toast.LENGTH_SHORT).show()
return
}
/*Ketika berhasil update data akan berpindah secara otomatis ke MainActivity.kt */
val editIntent = Intent(this, MainActivity::class.java)
startActivity(editIntent)
/*Kita inisialisasi variabel newUsername isinya adalah class Users*/
val newUsername = Users(id = idUsername, username = usernameStudent)
/*Kita panggil addUsers untuk menyimpan data ke tabel Users*/
editViewModel.addUsers(newUsername)
}
}
Secara default ketika Kita menambahkan class Activity baru biasanya akan error seperti dibawah ini dibagian AndroidManifest.xml.
Sehingga perlu Kita modifikasi sedikit menjadi seperti berikut ini.
<activity
android:name=".ui.edit.EditActivity"
android:exported="false" />
<activity
android:name=".ui.detail.DetailActivity"
android:exported="false" />
Supaya Kita dapat berpindah ke DetailActivity.kt ketika salah satu item di RecyclerView diklik, maka Kita perlu tambahkan Intent di class MainActivity.kt. Seperti pada gambar dibawah ini.
/*Agar dapat berpindah ke DetailActivity.kt ketika salah satu item diklik
* maka Kita perlu tambahkan Intent seperti pada dibawah ini*/
val detailIntent = Intent(this, DetailActivity::class.java).apply {
putExtra(Const.ID.ID_USERNAME, name.id)
}
startActivity(detailIntent)
Nah pada saat ini Kita dapat running aplikasinya
Untuk keseluruhan code pada penerapan Part 2 Modularization diatas sebagai berikut :
PART 3 ANDROID CORE
Langkah pertama Kita tambahkan library berikut ini di shared_dependencies.gradle
/* tambahkan library core android crocodic */
implementation 'com.github.crocodic-studio:AndroidCoreProject:4.0.9'
Selanjutnya tambahkan https://jitpack.io didalam settings.gradle
maven { url "https://jitpack.io" }
Selanjutnya buka console https://firebase.google.com dan klik Add project
Selanjutnya beri nama project nya, kemudian klik Continue
Klik Continue sekali lagi
Pilih default Account for Firebase pada spinner , kemudian klik button Create project
Selanjutnya klik icon android didalam project Latihan
Masukkan package name sesuai nama package aplikasi yang kalian buat.
Sebelum melanjutkan langkah berikutnya, Silahkan download terlebih dahulu file google-services.json
Selanjutnya Kita akan Copy google-services untuk dipastekan pada modul app.
Jangan lupa klik sync now setelah melakukan perubahana didalam gradle.
Selanjutnya klik Next
Kemudian tambahkan plugin dibawah ini didalam build.gradle(Module:app)
/* pada part 3 tambahkan plugin dibawah ini */
id 'com.google.gms.google-services'
id 'com.google.firebase.crashlytics'
Sehingga keseluruhan source code pada build.gradle(Module:app) akan menjadi sebagai berikut.
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
/* tambahkan plugin dibawah ini */
id 'com.google.dagger.hilt.android'
id 'kotlin-kapt'
/* pada part 3 tambahkan plugin dibawah ini */
id 'com.google.gms.google-services'
id 'com.google.firebase.crashlytics'
/* karena di module app tidak butuh kotlin-parcelize jadi Saya hapus disini, karena yang pakai cuma module core
karena nanti untuk package data,adapter dan injection akan Kita pindahkan ke package core di module core*/
}
//TODO Tambahkan source code berikut ini//
apply from: '../shared_dependencies.gradle'
android {
namespace 'com.nandaadisaputra.simplemodularization'
compileSdk 33
defaultConfig {
applicationId "com.nandaadisaputra.simplemodularization"
minSdk 23
targetSdk 33
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
packagingOptions {
resources.excludes.add("META-INF/*")
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
/* untuk mengaktifkan databinding dan viewbinding */
dataBinding true
viewBinding true
}
}
dependencies {
//TODO Tambahkan source code berikut ini//
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation project(":core")
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
}
Kemudian Kita tambahkan source berikut ini didalam build.gradle(Project:app)
buildscript {
dependencies {
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.5'
}
}
plugins {
..................
..................
...............
/* tambahkan plugins Google Service */
id 'com.google.gms.google-services' version "4.3.14" apply false
}
/* untuk clean cache */
task clean(type: Delete) {
delete rootProject.buildDir
}
Sehingga untuk keseluruhan source code dibagian build.gradle (Project:app) akan menjadi sebagai berikut ini
buildscript {
dependencies {
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.5'
}
}
plugins {
id 'com.android.application' version '7.3.1' apply false
id 'com.android.library' version '7.3.1' apply false
id 'org.jetbrains.kotlin.android' version '1.8.0' apply false
/* untuk mengimplementasikan dependency injection hilt */
id 'com.google.dagger.hilt.android' version '2.44' apply false
/* tambahkan plugins Google Service */
id 'com.google.gms.google-services' version "4.3.14" apply false
}
/* untuk clean cache */
task clean(type: Delete) {
delete rootProject.buildDir
}
Selanjutnya Klik Next pada Firebase console
Kemudian Klik Continue to console
Sekarang konfigrasi untuk dapat menggunakan Android Core telah selesai
Selanjutnya akan Kita coba untuk memdifikasi kode menggunakan Android Core Crocodic
Langkah pertama Kita akan membuat package baru dengan nama activity dan viewmodel. Caranya yaitu dengan klik kanan klik kanan pada nama package -> New -> Package
Langkah selanjutnya Kita akan membuat class BaseActivity.kt didalam package activity dan BaseViewModel.kt didalam package viewmodel. Caranya yaitu dengan klik kanan pada package base -> New -> Kotlin Class / File
Langkah selanjutnya Kita akan ke class BaseActivity.kt. Lakukan penambahan source code , sehingga untuk keseluruhan source code dibagian class BaseActivity.kt akan seperti berikut ini.
import androidx.databinding.ViewDataBinding
import com.crocodic.core.base.activity.CoreActivity
import com.crocodic.core.base.viewmodel.CoreViewModel
import com.nandaadisaputra.simplemodularization.core.data.room.UsersDatabase
import javax.inject.Inject
open class BaseActivity<VB: ViewDataBinding, VM: CoreViewModel>(layoutRes: Int): CoreActivity<VB, VM>(layoutRes){
@Inject
lateinit var appDatabase: UsersDatabase
}
Kemudian Kita akan ke class BaseViewModel.kt. Lakukan penambahan source code , sehingga untuk keseluruhan source code dibagian class BaseViewModel.kt akan seperti berikut ini.
import com.crocodic.core.base.viewmodel.CoreViewModel
open class BaseViewModel: CoreViewModel() {
override fun apiLogout() {
}
override fun apiRenewToken() {
}
}
Selanjutnya Kita dapat modifikasi source codenya sebagai berikut.
Contoh pada MainActivity.kt dan MainViewModel.kt
Lakukan hal yang sama untuk bagian class EditActivity.kt dan EditViewModel.kt, serta pada class DetailActivity.kt dan DetailViewModel.kt
Yuk, saatnya Kita running kembali aplikasi Kita.
Yey Kita telah berhasil running aplikasi tanpa error sedikitpun.
Untuk keseluruhan code pada penerapan Part 3 Android Core diatas sebagai berikut :
Sekian tutorial singkat dari Saya. Jika ada pertanyaan silahkan tinggal kan kalimat di kolom komentar, semoga bermanfaat…
Silahkan follow juga Linkedin Saya untuk saling membangun koneksi dan relasi : https://www.linkedin.com/in/nandaadisaputra/