After a lot of struggle, I managed to solve this. Here is my solution using MMVM architecture:
Student.kt
@Entity(tableName = "students")
data class Student(
@NotNull var name: String,
@NotNull var password: String,
var subject: String,
var email: String
) {
@PrimaryKey(autoGenerate = true)
var roll: Int = 0
}
StudentDao.kt
interface StudentDao {
@Insert
fun insertStudent(student: Student) : Long
}
StudentRepository.kt
class StudentRepository private constructor(private val studentDao: StudentDao)
{
fun getStudents() = studentDao.getStudents()
fun insertStudent(student: Student): Single<Long>? {
return Single.fromCallable(
Callable<Long> { studentDao.insertStudent(student) }
)
}
companion object {
// For Singleton instantiation
@Volatile private var instance: StudentRepository? = null
fun getInstance(studentDao: StudentDao) =
instance ?: synchronized(this) {
instance ?: StudentRepository(studentDao).also { instance = it }
}
}
}
StudentViewModel.kt
class StudentViewModel (application: Application) : AndroidViewModel(application) {
var status = MutableLiveData<Boolean?>()
private var repository: StudentRepository = StudentRepository.getInstance( AppDatabase.getInstance(application).studentDao())
private val disposable = CompositeDisposable()
fun insertStudent(student: Student) {
disposable.add(
repository.insertStudent(student)
?.subscribeOn(Schedulers.newThread())
?.observeOn(AndroidSchedulers.mainThread())
?.subscribeWith(object : DisposableSingleObserver<Long>() {
override fun onSuccess(newReturnId: Long?) {
Log.d("ViewModel Insert", newReturnId.toString())
status.postValue(true)
}
override fun onError(e: Throwable?) {
status.postValue(false)
}
})
)
}
}
In the Fragment:
class RegistrationFragment : Fragment() {
private lateinit var dataBinding : FragmentRegistrationBinding
private val viewModel: StudentViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initialiseStudent()
viewModel.status.observe(viewLifecycleOwner, Observer { status ->
status?.let {
if(it){
Toast.makeText(context , "Data Inserted Sucessfully" , Toast.LENGTH_LONG).show()
val action = RegistrationFragmentDirections.actionRegistrationFragmentToLoginFragment()
Navigation.findNavController(view).navigate(action)
} else
Toast.makeText(context , "Something went wrong" , Toast.LENGTH_LONG).show()
//Reset status value at first to prevent multitriggering
//and to be available to trigger action again
viewModel.status.value = null
//Display Toast or snackbar
}
})
}
fun initialiseStudent() {
var student = Student(name =dataBinding.edName.text.toString(),
password= dataBinding.edPassword.text.toString(),
subject = "",
email = dataBinding.edEmail.text.toString())
dataBinding.viewmodel = viewModel
dataBinding.student = student
}
}
I have used DataBinding.Here is my XML:
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<variable
name="student"
type="com.kgandroid.studentsubject.data.Student" />
<variable
name="listener"
type="com.kgandroid.studentsubject.view.RegistrationClickListener" />
<variable
name="viewmodel"
type="com.kgandroid.studentsubject.viewmodel.StudentViewModel" />
</data>
<androidx.core.widget.NestedScrollView
android:id="@+id/nestedScrollview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
tools:context="com.kgandroid.studentsubject.view.RegistrationFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constarintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:isScrollContainer="true">
<TextView
android:id="@+id/tvRoll"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:gravity="center_horizontal"
android:text="Roll : 1"
android:textColor="@color/colorPrimary"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/edName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginEnd="16dp"
android:ems="10"
android:inputType="textPersonName"
android:text="Name"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvRoll" />
<TextView
android:id="@+id/tvName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:text="Name:"
android:textColor="@color/colorPrimary"
android:textSize="18sp"
app:layout_constraintBaseline_toBaselineOf="@+id/edName"
app:layout_constraintEnd_toStartOf="@+id/edName"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/tvEmail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Email"
android:textColor="@color/colorPrimary"
android:textSize="18sp"
app:layout_constraintBaseline_toBaselineOf="@+id/edEmail"
app:layout_constraintEnd_toStartOf="@+id/edEmail"
app:layout_constraintStart_toStartOf="parent" />
<EditText
android:id="@+id/edEmail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginEnd="16dp"
android:ems="10"
android:inputType="textPersonName"
android:text="Name"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/edName" />
<TextView
android:id="@+id/textView6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Password"
android:textColor="@color/colorPrimary"
android:textSize="18sp"
app:layout_constraintBaseline_toBaselineOf="@+id/edPassword"
app:layout_constraintEnd_toStartOf="@+id/edPassword"
app:layout_constraintStart_toStartOf="parent" />
<EditText
android:id="@+id/edPassword"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginEnd="16dp"
android:ems="10"
android:inputType="textPersonName"
android:text="Name"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/edEmail" />
<Button
android:id="@+id/button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="32dp"
android:background="@color/colorPrimary"
android:text="REGISTER"
android:onClick="@{() -> viewmodel.insertStudent(student)}"
android:textColor="@android:color/background_light"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/edPassword" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</layout>
I have struggled a lot to accomplish this with asynctask as room insert and delete operation must be done in a separate thread. Finally able to do this with Single
type observable in RxJava.
Here is Gradle dependencies for rxjava:
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation 'io.reactivex.rxjava2:rxjava:2.0.3'
int
orlong
instead ofvoid
as the result of the@Insert
operation? – DownfallaSyncTask
? how are you returning the value from your repository function? – Monolayer