Can't insert into room database because of var initialization Android - android

I'm trying to make an Android application using Kotlin, Room Database and both view model and view model factory.
The issue is that when I try to create the object of the entity I am trying to insert, I just get this warning (which i think in this case makes no sense):
Dialog with warning "unexpected tokens (Use ';' to separate expressions on the same line)"
Anyway, here are my Fragment, FragmentViewModel, FragmentViewModelFactory, Entity, Dao and Database files (or relevant parts):
Fragment:
class RegisterFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = DataBindingUtil.inflate<RegisterFragmentBinding>(
inflater,
R.layout.register_fragment,
container,
false
)
val application = requireNotNull(this.activity).application
val dataSource = NbaCafeDB.getInstance(application).usuariDao
val viewModelFactory = RegisterViewModelFactory(dataSource, application)
val registerViewModel =
ViewModelProvider(this, viewModelFactory).get(RegisterViewModel::class.java)
binding.setLifecycleOwner(this)
...
binding.endavantButton.setOnClickListener { View ->
val email = binding.email.text.toString()
val username = binding.registerUser.text.toString()
val password = binding.registerPassword.text.toString()
val confPassword = binding.confirmPassword.text.toString()
if (email != "" && username != "") {
if (registerViewModel.userExists(username)) {
Toast.makeText(context, "Aquest nom d'usuari ja existeix", Toast.LENGTH_LONG)
.show()
} else if (password == confPassword) {
registerViewModel.insert(username, email, password)
} else {
Toast.makeText(context, "Les contrassenyes no coincideixen", Toast.LENGTH_LONG)
.show()
}
}
}
FragmentViewModel:
class RegisterViewModel(
private val dataSource: UsuariDao, application: Application
) : AndroidViewModel(application) {
fun insert(nomUsuari: String, emailUsuari: String, passUsuari: String) {
val usuari: Usuari(nomUsuari, emailUsuari, passUsuari)
viewModelScope.launch {
dataSource.insert(usuari)
}
}
fun userExists(usuariNom: String): Boolean {
return dataSource.userExists(usuariNom)
}
}
FragmentViewModelFactory:
class RegisterViewModelFactory(
private val dataSource: UsuariDao,
private val application: Application
) : ViewModelProvider.Factory {
#Suppress("Unchecked_cast")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(RegisterViewModel::class.java)) {
return RegisterViewModel(dataSource, application) as T
}
throw IllegalArgumentException("Unknown View Model Class")
}
}
Entity:
#Entity
data class Usuari(
#PrimaryKey
#ColumnInfo(name = "nom_usuari")
var nomUsuari: String,
#ColumnInfo(name = "email_usuari")
var emailUsuari: String,
#ColumnInfo(name = "password_usuari")
var passwordUsuari: String
)
DAO:
#Dao
interface UsuariDao {
#Insert
suspend fun insert(usuari: Usuari)
#Query ("SELECT EXISTS(SELECT * FROM Usuari WHERE nomUsuari = :usuariNom)")
fun userExists(usuariNom: String): Boolean
}
And finally, Database:
#Database(
entities =
[Beguda::class,
Comanda::class,
Postre::class,
Sandwich::class,
Usuari::class],
version = 3,
exportSchema = false
)
abstract class NbaCafeDB : RoomDatabase() {
abstract val begudaDao: BegudaDao
abstract val comandaDao: ComandaDao
abstract val postreDao: PostreDao
abstract val sandwichDao: SandwichDao
abstract val usuariDao: UsuariDao
companion object {
#Volatile
private var INSTANCE: NbaCafeDB? = null
fun getInstance(context: Context): NbaCafeDB {
synchronized(this) {
var instance = INSTANCE
if (instance == null) {
instance = Room.databaseBuilder(
context.applicationContext,
NbaCafeDB::class.java,
"nba_cafe_database"
)
.fallbackToDestructiveMigration()
.allowMainThreadQueries()
.build()
INSTANCE = instance
}
return instance
}
}
}
}
I hope someone has an answer for this, thanks in advance!!

Related

Android Room Select query returns null or empty strings even the database is not empty

I'm new to android and room. I'm trying to make a local db but I'm struggling at this point where the database is not empty as the image below proves. But when I try to select any data it returns an empty list or null values. Note that the insert query works fine.
Code:
Entity:
#Entity(tableName = "product_table")
#Parcelize
data class Product(
#PrimaryKey
#SerializedName("id")
val id : String,
#SerializedName("title")
val title : String,
#SerializedName("price")
val price : String,
#SerializedName("category")
val category : String,
#SerializedName("description")
val description : String,
#SerializedName("image")
val image : String,
val color : String,
val size : String
): Parcelable
Dao:
#Dao
interface CartDao {
#Query("SELECT * FROM product_table")
fun get_all_carts(): LiveData<List<Product>>
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert_item_to_cart(product: Product) : Long
#Delete
suspend fun delete_item_from_cart(product: Product)
#Query("Delete FROM product_table")
fun delete_all_cart()
}
Database:
#Database(entities = [Product::class], version = 1)
abstract class ProductDatabase : RoomDatabase() {
abstract fun productDao(): CartDao
companion object{
#Volatile
private var INSTANCE: ProductDatabase? = null
fun getDataseClient(context: Context) : ProductDatabase {
if (INSTANCE != null) return INSTANCE!!
synchronized(this) {
INSTANCE = Room
.databaseBuilder(context, ProductDatabase::class.java, "product_database")
.fallbackToDestructiveMigration()
.build()
return INSTANCE!!
}
}
}
}
Repository:
class CartRepository {
companion object {
var productDatabase: ProductDatabase? = null
var product: LiveData<Product>? = null
fun initializeDB(context: Context) : ProductDatabase {
return ProductDatabase.getDataseClient(context)
}
fun get_all_data(context: Context) : List<Product> {
productDatabase = initializeDB(context)
var temp_list = emptyList<Product>()
CoroutineScope(Dispatchers.IO).launch {
temp_list = productDatabase!!.productDao().get_all_carts()
}
return temp_list
}
fun get_first_item(context: Context, input_id : String) : Product {
productDatabase = initializeDB(context)
var temp_item : Product = Product("","","","","","","","")
CoroutineScope(Dispatchers.IO).launch {
temp_item = productDatabase!!.productDao().get_item(input_id)
}
return temp_item
}
}
}
View Model:
#HiltViewModel
class CartFragmentViewModel #Inject constructor(
private val productDao : CartDao
) : ViewModel() {
var products_list : MutableLiveData<List<Product>>
init {
products_list = MutableLiveData()
}
fun get_all_products(context: Context) : List<Product>{
return CartRepository.get_all_data(context)
}
fun get_first_item(context: Context, input_id : String) : Product{
return CartRepository.get_first_item(context, input_id)
}
}
Fragment:
class CartFragment #Inject constructor(
) : Fragment(R.layout.cart_fragment_layout) {
lateinit var cart_list : List<Product>
val cart_adapter = CartRecyclerViewAdapter()
val viewModel by activityViewModels<CartFragmentViewModel>()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
rv_cart.adapter = cart_adapter
rv_cart.layoutManager = LinearLayoutManager(
activity?.applicationContext,
LinearLayoutManager.VERTICAL,
false
)
rv_cart.setHasFixedSize(true)
/*cart_list = viewModel.get_all_products(activity?.applicationContext!!)
cart_adapter.submitList(
cart_list
)
cart_adapter.notifyDataSetChanged()
Log.d(TAG, "Fetched Data: {${cart_list.get(1).title}}")*/
var p = viewModel.get_first_item(activity?.applicationContext!!, "1")
cart_adapter.submitList(
listOf(
p
)
)
cart_adapter.notifyDataSetChanged()
var s = p.title
Log.d(TAG, "Fetched Data: {$s}")
/*viewModel.get_first_item(activity?.applicationContext!!).observe(viewLifecycleOwner, Observer {
cart_adapter.submitList(listOf(it))
cart_adapter.notifyDataSetChanged()
})*/
//viewModel.get_first_item(activity?.applicationContext!!)
}
}
There are many comments and logs in the Fragment Class for the sake of trying to figure what the problem is. I can't really know what is happening and when I use LiveData as return type of dao get functions the app crashes. Hope someone can help me out and thanks for your attention.
The problem with your CartRepository methods.
fun get_first_item(context: Context, input_id : String) : Product {
productDatabase = initializeDB(context)
var temp_item : Product = Product("","","","","","","","")
CoroutineScope(Dispatchers.IO).launch {
temp_item = productDatabase!!.productDao().get_item(input_id)
}
return temp_item
}
In the above method, you are fetching an item in a background thread which means it goes into another thread and allows the return temp_item to always returns immediately, without blocking the code to wait for a result so that's why you are getting null and or empty list.
Solution is :
Make all database operation methods in CartRepository as suspended, see below:
Note: I use an object instead of class
object CartRepository {
var productDatabase: ProductDatabase? = null
fun initializeDB(context: Context) : ProductDatabase {
return ProductDatabase.getDataseClient(context)
}
suspend fun get_all_data(context: Context) : List<Product> {
productDatabase = initializeDB(context)
return productDatabase!!.productDao().get_all_carts()
}
suspend fun get_first_item(context: Context, input_id : String) : Product {
productDatabase = initializeDB(context)
return productDatabase!!.productDao().get_item(input_id)
}
}
And in your viewModel call this suspend function in viewModelScope like below:
#HiltViewModel
class CartFragmentViewModel #Inject constructor(
private val productDao : CartDao
) : ViewModel() {
.....
var productData = MutableLiveData<Product>()
fun get_first_item(context: Context, input_id: String) {
viewModelScope.lauch(Dispatchers.IO){
val data = CartRepository.get_first_item(context, input_id)
withContext(Dispatchers.Main){
productData.value = data
}
}
}
....
In your fragment call get_first_item first then observe you data productData and you can do the same things for other database operations also by following all steps.
I hope this will help you if you dont understand any code just let me know in the comments and please ignore the typos
To use a LiveData with room, firstly, you should attach an observer to the live data.
So instead of returning the value of LiveData from the Repository methods, you should return the Live Data object itself,
And then observe that Livedata in your Viewmodel class.
If you need the code sample, you can ask me in the comment section
Happy Coding :D

Obtaining an array from room Database - Android, Kotlin

I'm new with Room Database and I would like to extract from one database an array of int.
I think that the problem is in the code of the fragment, when I would like to use the function "stationlist" to obtain an array of the codes of stations present in database. The output "StationList" seems to be Unit instead of Array: does anyone know the solution?
Thank you
This is my entity and data class:
#Parcelize
#Entity(tableName = "user_table")
data class User(
#PrimaryKey(autoGenerate = true)
val id: Int,
val Name: String,
val StationCode: Int
): Parcelable
This is my Dao:
#Dao
interface UserDao {
#Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun addUser(user: User)
#Query("SELECT intArrayOf(StationCode) FROM user_table")
suspend fun stationlist() : Array
}
This is my database:
#Database(entities = [User::class], version = 1, exportSchema = false)
abstract class UserDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
companion object {
#Volatile
private var INSTANCE: UserDatabase? = null
fun getDatabase(context: Context): UserDatabase{
val tempInstance = INSTANCE
if(tempInstance != null){
return tempInstance
}
synchronized(this){
val instance = Room.databaseBuilder(
context.applicationContext,
UserDatabase::class.java,
"user_database"
).build()
INSTANCE = instance
return instance
}
}
}
}
This is my Repository:
class UserRepository(private val userDao: UserDao) {
val readAllData: LiveData<List<User>> = userDao.readAllData()
suspend fun addUser(user: User){
userDao.addUser(user)
}
suspend fun stationlist(){
userDao.stationlist()
}
}
and this is my Viewmodel:
class UserViewModel(application: Application): AndroidViewModel(application) {
val readAllData: LiveData<List<User>>
private val repository: UserRepository
init {
val userDao = UserDatabase.getDatabase(
application
).userDao()
repository = UserRepository(userDao)
readAllData = repository.readAllData
}
fun addUser(user: User){
viewModelScope.launch(Dispatchers.IO) {
repository.addUser(user)
}
}
fun stationlist(){
viewModelScope.launch(Dispatchers.IO) {
repository.stationlist()
}
}
}
At last this is a part of the fragment:
class AddFragment : Fragment(), OnMapReadyCallback {
private lateinit var mUserViewModel: UserViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// here is all the part regarding displaying the layout and collecting data
mUserViewModel = ViewModelProvider(this).get(UserViewModel::class.java)
view.add_btn.setOnClickListener {
insertDataToDatabase()
}
return view
}
private fun insertDataToDatabase() {
if(inputCheck(name, stationcode)){
val user = User(
0,
name,
stationcode)
mUserViewModel.addUser(user)
// Here is the problem:
val StationList= mUserViewModel.stationlist()
}
The problem is the dao method. Change it with this:
#Query("SELECT StationCode FROM user_table")
suspend fun stationlist() : Array<Int>
EDIT
There are some other problems with coroutines.
In fragment you call this:
val StationList= mUserViewModel.stationlist()
But this method does not return immediately.
fun stationlist(){
viewModelScope.launch(Dispatchers.IO) {
repository.stationlist()
}
}
You should implement a live data array, which is updated when async db query returns. In this way:
val stationList: MutableLiveData<Array<Int>>() = MutableLiveData()
fun stationlist(){
viewModelScope.launch(Dispatchers.IO) {
stationList.value = repository.stationlist()
}
}
And in fragment you have to observe the list:
mUserViewModel.stationList.observe(viewLifecycleOwner) { array ->
// do stuff
}
You also have to change repository stationlist() return type:
suspend fun stationlist(): Array<Int> {
return userDao.stationlist()
}
These are useful links to understand coroutines, room and livedata:
https://developer.android.com/topic/libraries/architecture/livedata
https://developer.android.com/codelabs/kotlin-android-training-coroutines-and-room#0

How to show query to database in ListView in kotlin with android studio?

I am learning to develop in kotlin with android studio, so I don't have any experience.
I am wanting to insert data into a local database using room database, so far I think I'm doing fine. Now I need to be able to consult those data but I cannot do it, I have searched the internet but I have not been able to solve my problem.
I attach the code.
Class #Entity
class TablasBdApp {
#Entity(tableName = TblConteo.TABLE_NAME)
data class TblConteo(
#PrimaryKey(autoGenerate = true) #ColumnInfo(name = "linea_id") val Linea_Id: Int = 0,
#ColumnInfo (name = "articulo") val Articulo : String?
)
{
companion object {
const val TABLE_NAME = "TablaConteo"
}
}
}
Class #Dao
#Dao
public interface ItblConteoDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertArticulo(taskTblConteo: TablasBdApp.TblConteo);
#Query("SELECT * FROM " + TablasBdApp.TblConteo.TABLE_NAME + " ORDER BY Linea_Id desc")
fun getConteoArticulos(): LiveData<List<TablasBdApp.TblConteo>>
}
Class #DataBase
class BaseDeDatos {
#Database(entities = [TablasBdApp.TblConteo::class], version = 1)
abstract class PortatilDataBase : RoomDatabase() {
abstract fun itblconteoDao () : ItblConteoDao
companion object {
private const val DATABASE_NAME = "portatildb"
#Volatile
private var INSTANCE: PortatilDataBase? = null
fun getInstance(context: Context): PortatilDataBase? {
INSTANCE ?: synchronized(this) {
INSTANCE = Room.databaseBuilder(
context.applicationContext,
PortatilDataBase::class.java,
DATABASE_NAME
).build()
}
return INSTANCE
}
}
}
}
Class #Repository
class ConteoRepository(application: Application) {
private val itblConteoDao: ItblConteoDao? = BaseDeDatos.PortatilDataBase.getInstance(application)?.itblconteoDao()
fun insert (tblconteo: TablasBdApp.TblConteo){
if(itblConteoDao != null) InsertAsyncTask(itblConteoDao).execute(tblconteo)
}
fun getConteo(): LiveData<List<TablasBdApp.TblConteo>> {
return itblConteoDao?.getConteoArticulos() ?: MutableLiveData<List<TablasBdApp.TblConteo>>()
}
private class InsertAsyncTask(private val itblConteoDao: ItblConteoDao) :
AsyncTask<TablasBdApp.TblConteo, Void, Void>() {
override fun doInBackground(vararg tblconteos: TablasBdApp.TblConteo?): Void? {
for (tblconteo in tblconteos) {
if (tblconteo != null) itblConteoDao.insertArticulo(tblconteo)
}
return null
}
}
}
Class #CustomAdapter
class CustomAdapter(context: Context): BaseAdapter() {
private val mContext: Context
init{
mContext = context
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
//val repository = ConteoRepository(application = Application())
var textView = TextView(mContext)
val observer = Observer<List<TablasBdApp.TblConteo>> { conteos ->
if (conteos != null) {
var text = ""
for (conteo in conteos) {
text += conteo.Linea_Id.toString() + " " + conteo.Articulo
}
textView.text = text
}
}
//repository.getConteo().observe(this, observer)
return textView
}
override fun getItem(position: Int): Any {
return "Test Articulo"
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getCount(): Int {
TODO("Not yet implemented")
}
}
Class #Fragment
class CapturaConteoFragment() : Fragment() {
private lateinit var capturaConteoViewModel: CapturaConteoViewModel
#SuppressLint("ResourceType")
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
capturaConteoViewModel =
ViewModelProviders.of(this).get(CapturaConteoViewModel::class.java)
val root = inflater.inflate(R.layout.fragment_capturaconteo, container, false)
//val textView: TextView = root.findViewById(R.id.text_capturaconteo)
capturaConteoViewModel.text.observe(viewLifecycleOwner, Observer {
//textView.text = it
val botonGuardar: Button = root.findViewById(R.id.btn_guardar)
val textoArticulo : EditText = root.findViewById(R.id.edit_art_upc)
var listview: ListView = root.findViewById(R.id.list_conteo)
botonGuardar.setOnClickListener {
if(textoArticulo.getText().toString().trim().isEmpty()){
Toast.makeText(activity, "Captura un articulo", Toast.LENGTH_LONG).show()
textoArticulo.requestFocus()
}
else{
saveConteo(TablasBdApp.TblConteo(Articulo = textoArticulo.text.trim().toString()))
Toast.makeText(activity, "Articulo guardado", Toast.LENGTH_LONG).show()
//var listaconteo = arrayOf(muestraConteo())
var prodAdapter = CustomAdapter(**this**) **<- This is where it marks error.**
listview?.adapter = prodAdapter
}
}
})
return root
}
private fun saveConteo(tblConteo: TablasBdApp.TblConteo) {
val repository = ConteoRepository(application = Application())
repository.insert(tblConteo)
val conteo = repository.getConteo()
}
private fun showConteo (){
val repository = ConteoRepository(application = Application())
repository.getConteo()
}
}
Thanks for your help.
Regards.
In your DAO you are returning LiveData that means you need to observe it and best practice would be through ViewModel which implies you should MVVM architecture, there are plenty of tutorials and courses on it.
Here is something from google Codelabs
Other way would would be to return just list rather than live data of a list. This is bad practice because you will have to refresh it all the time and do a lot of extra work.
PS your adapter needs fixing too my suggestion is follow codelabs or watch complete tutorial on Room + MVVM.
Hope this helps

Implementing Room with a viewmodel,how to get the return value of a query in DAO. instead of a List<> it just returns "CouroutineJob"

i am implementing Room with a vIewModel, my structure is the following
#DAO,#Entity,#Database,#Repository
#Entity(tableName="dx_table")
class dx_table(
#ColumnInfo(name = "name")
val naxme: String,
#PrimaryKey
#ColumnInfo(name = "phone")
val phone: String,
#ColumnInfo(name = "passx")
val passx: String,
#ColumnInfo(name = "login_fx")
val login_fx: String
)
#Dao
interface dx_dao{
#Query("SELECT * FROM dx_table")
fun get_all():LiveData<List<dx_table>>
#Insert
suspend fun insertTrx(dx_table:dx_table)
#Query("UPDATE dx_table SET login_fx =:login_fx where phone=:phonex")
suspend fun insertFx(login_fx: String,phonex: String)
#Query("SELECT * from dx_table where phone=:phonex")
suspend fun get_name_px(phonex: String):List<dx_table>
#Query("Delete from dx_table")
suspend fun deleteAll()
#Query("Select * from dx_table where login_fx=1")
suspend fun selectFx():List<dx_table>
}
#Database(entities = arrayOf(dx_table::class), version = 1, exportSchema = false)
public abstract class DxDatabase : RoomDatabase() {
abstract fun dxDao(): dx_dao
companion object {
// Singleton prevents multiple instances of database opening at the
// same time.
#Volatile
private var INSTANCE: DxDatabase? = null
fun getDatabase(context: Context): DxDatabase {
val tempInstance = INSTANCE
if (tempInstance != null) {
return tempInstance
}
synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
DxDatabase::class.java,
"dx_database"
).build()
INSTANCE = instance
return instance
}
}
}
}
class dxRepository(private val dxDao: dx_dao ){
val k_d:LiveData<List<dx_table>> = dxDao.get_all()
suspend fun insert_trx(dx_table: dx_table){
dxDao.insertTrx(dx_table)
}
suspend fun insert_fx(login_fx: String,phonex: String) {
dxDao.insertFx(login_fx,phonex)
}
suspend fun select_fx() {
dxDao.selectFx()
}
suspend fun get_name_px(phonex: String) :List<dx_table> {
return dxDao.get_name_px(phonex) as List<dx_table>
}
}
The viewmodel is
class DxViewModel (application: Application) : AndroidViewModel(application) {
var repository: dxRepository
var k_d: LiveData<List<dx_table>>
init {
// Gets reference to WordDao from WordRoomDatabase to construct
// the correct WordRepository.
val dxDao = DxDatabase.getDatabase(application).dxDao()
repository = dxRepository(dxDao)
k_d = repository.k_d
}
fun insert_trx(dxTable: dx_table) = viewModelScope.launch {
repository.insert_trx(dxTable)
}
fun insert_fx(login_fx: String, phonex: String) = viewModelScope.launch {
repository.insert_fx(login_fx, phonex)
}
fun select_fx() = viewModelScope.launch {
repository.select_fx()
}
fun get_name_px(phonex: String) = viewModelScope.launch {
repository.get_name_px(phonex)
}
}
I can track the live data using observe,its not an issue, the problem i am facing is with the get_name_px(phone)
var mView = ViewModelProviders.of(this).get(DxViewModel::class.java)
var lm = mView.get_name_px(phone)
here lm seems to be job type , i need the return List , how do i get it .
In your viewModel function select_fx() return a job, because launch does not return result, so you have to either:
1) Use async and await
fun get_name_px(phonex: String) = viewModelScope.async {
repository.get_name_px(phonex)
}.await()
2) Not use launch viewModel, use it in Activity/Fragment
suspend fun get_name_px(phonex: String) = repository.get_name_px(phonex)
class CardFragment : Fragment() {
fun get() {
// launch in Dispatchers.Main
lifecycleScope.launch {
var lm = mView.get_name_px(phone)
}
// launch in background thread
lifecycleScope.launch(Dispatchers.Default) {
var lm = mView.get_name_px(phone)
}
}
}

Reload RecyclerView after data change with Room, ViewModel and LiveData

I am trying, without success, to solve a problem for days. I would like to update my recyclerView whenever the records of a particular model change in the Database (DB Room). I use ViewModel to handle the model data and the list of records are stored in LiveData.
Database
#Database(entities = arrayOf(Additive::class), version = ElementDatabase.DB_VERSION, exportSchema = false)
abstract class ElementDatabase() : RoomDatabase() {
companion object {
const val DB_NAME : String = "element_db"
const val DB_VERSION : Int = 1
fun get(appContext : Context) : ElementDatabase {
return Room.databaseBuilder(appContext, ElementDatabase::class.java, DB_NAME).build()
}
}
abstract fun additivesModels() : AdditiveDao
}
Model
#Entity
class Additive {
#PrimaryKey #ColumnInfo(name = "id")
var number : String = ""
var dangerousness : Int = 0
var description : String = ""
var names : String = ""
var notes : String = ""
var risks : String = ""
var advice : String = ""
}
Dao
#Dao
interface AdditiveDao {
#Query("SELECT * FROM Additive")
fun getAllAdditives() : LiveData<List<Additive>>
#Query("SELECT * FROM Additive WHERE id = :arg0")
fun getAdditiveById(id : String) : Additive
#Query("DELETE FROM Additive")
fun deleteAll()
#Insert(onConflict = REPLACE)
fun insert(additive: Additive)
#Update
fun update(additive: Additive)
#Delete
fun delete(additive: Additive)
}
ViewModel
class AdditiveViewModel(application: Application) : AndroidViewModel(application) {
private var elementDatabase : ElementDatabase
private val additivesModels : LiveData<List<Additive>>
init {
this.elementDatabase = ElementDatabase.get(appContext = getApplication())
this.additivesModels = this.elementDatabase.additivesModels().getAllAdditives()
}
fun getAdditivesList() : LiveData<List<Additive>> {
return this.additivesModels
}
fun deleteItem(additive : Additive) {
DeleteAsyncTask(this.elementDatabase).execute(additive)
}
private class DeleteAsyncTask internal constructor(private val db: ElementDatabase) : AsyncTask<Additive, Void, Void>() {
override fun doInBackground(vararg params: Additive): Void? {
db.additivesModels().delete(params[0])
return null
}
}
}
Fragment
class AdditivesFragment : LifecycleFragment() {
private var viewModel : AdditiveViewModel? = null
private var adapter : AdditivesAdapter? = null
companion object {
fun newInstance() : AdditivesFragment {
val f = AdditivesFragment()
val args = Bundle()
f.arguments = args
return f
}
}
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater?.inflate(R.layout.fragment_additives, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
this.adapter = AdditivesAdapter(ArrayList<Additive>())
this.additives_list.layoutManager = GridLayoutManager(this.context, 2, GridLayoutManager.VERTICAL, false)
this.additives_list.adapter = this.adapter
this.viewModel = ViewModelProviders.of(this).get(AdditiveViewModel::class.java)
this.viewModel?.getAdditivesList()?.observe(this, Observer<List<Additive>> { additivesList ->
if(additivesList != null) {
this.adapter?.addItems(additivesList)
}
})
super.onActivityCreated(savedInstanceState)
}
}
Now, my question is why is the observer called only once (at the start of the fragment) and then is not called back again? How can I keep the observer constantly listening to the changes in the DB (insert, update, delete) so that my recyclerView instantly can be updated? Thanks a lot for any suggestion.
This is where you made a mistake:
this.viewModel = ViewModelProviders.of(this).get(AdditiveViewModel::class.java)
you are passing this while you are inside the fragment which is pretty disturbing for some people cause it is not a syntax error but logical. You have to pass activity!! instead, it will be like this:
this.viewModel = ViewModelProviders.of(activity!!).get(AdditiveViewModel::class.java)
UPDATE:
Pass viewLifecycleOwner while being inside fragment while observing the Data
mainViewModel.data(viewLifecycleOwner, Observer{})
If you're using fragmentKtx, you can init viewModel this way:
private val viewModel by viewModels<MainViewModel>()
If You've viewModelFactory:
private val viewModel by viewModels<MainViewModel>{
viewModelFactory
}
with this approach you don't need to call:
// you can omit this statement completely
viewModel = ViewModelProviders.of(this).get(AdditiveViewModel::class.java)
You can simply just start observing the data..

Categories

Resources