I'm using the PagedList architectural components using a Room database and I am having trouble returning results to the observe method.
Here is my Dao:
#Dao
interface WorkPackageStorageDao {
#Query("SELECT * from workpackages where id = :id")
fun getById(id: String): Workpackage
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(workpackage: Workpackage)
#Query("Select * from workpackages WHERE id LIKE '%' || :searchString || '%' order by :orderBy")
fun searchWorkpackages(searchString : String, orderBy : String) : DataSource.Factory<Int, Workpackage>
#Query("SELECT * FROM workpackages")
fun searchWorkPackgesTest() : List<Workpackage>
#Query("Select * from workpackages WHERE id LIKE '%' || :searchString || '%' order by :orderBy")
fun searchWorkPackgesTestQuery(searchString : String, orderBy : String) : List<Workpackage>
#Query("DELETE from workpackages")
fun deleteAll()
}
My repository:
fun getAllWorkPackagesTestQuery() : List<Workpackage> {
return workpackagesDao.searchWorkPackgesTestQuery("",SortedBy.WorkPackageNumber.type)
}
fun getAllWorkPackages() : DataSource.Factory<Int, Workpackage> {
return getSortedAndSearchedWorkPackages("",
SortedBy.WorkPackageNumber
)
}
fun getSortedAndSearchedWorkPackages(searchString : String, sortBy: SortedBy) : DataSource.Factory<Int, Workpackage> {
return workpackagesDao.searchWorkpackages(searchString,sortBy.type)
}
Here is the method in my view model:
suspend fun fetchWorkPackagesInitial(
workWeek: Configurations.AppWeek,
pagingLimit: Int
) {
coroutineScope {
withContext(Dispatchers.IO) {
val factory: DataSource.Factory<Int, Workpackage> =
workPackageRepository.getAllWorkPackages()
val pagedListBuilder =
LivePagedListBuilder<Int, Workpackage>(factory, pagingLimit)
workPackagesList = pagedListBuilder.build()
val list = workPackageRepository.getAllWorkPackagesTestQuery() //27 Items returned, query is fine.
}
}
}
Here is my fragment:
mainViewModel.week.observe(this, Observer {
it ?: return#Observer
launch { workPackagesViewModel.fetchWorkPackagesInitial(it, PAGING_LIMIT) }
})
//Observe never called.
workPackagesViewModel.workPackagesList?.observe(this, Observer { wpList ->
wpList ?: return#Observer
adapter = WorkPackagesRecyclerAdapter(this)
adapter.submitList(wpList)
binding.workPackagesRecyclerView.adapter = adapter
adapter.notifyDataSetChanged()
})
As a test to my query, I've implemented:
val list = workPackageRepository.getAllWorkPackagesTestQuery()
which returns 27 items, so query is fine. Am I setting up the the Dao wrong, the LivePagedListBuilder wrong? Why is observe not called?
You're not getting items because it's a PagedList. You need to trigger the load in order to obtain pages.
That is why giving the PagedList to a PagedListAdapter via submitList will eventually load the data.
You also don't need to manually invoke adapter.notifyDataSetChanged() when you're using a PagedListAdapter, because the DiffUtil will handle that internally.
However, you should definitely be retrieving the DataSource.Factory and the LivePagedListBuilder and the PagedList on the UI thread (Dispatcher.MAIN), because threading is handled by the Paging lib's Room integration internally. Observing (and invoking getItem( on an unloaded element) will trigger the load, and the load will be executed asynchronously by the DataSource out of the box.
The way to use Paging is like this:
class MyViewModel(
private val workPackageStorageDao: WorkPackageStorageDao
): ViewModel() {
private val searchQuery: MutableLiveData<String> = MutableLiveData("")
val workPackages: LiveData<PagedList<WorkPackage>> = Transformations.switchMap(searchQuery) { searchText ->
val factory = workPackageStorageDao.searchWorkPackages(searchText, SortedBy.WorkPackageNumber)
val pagedListBuilder = LivePagedListBuilder<Int, WorkPackage>(factory, pagingLimit)
pagedListBuilder.build()
}
}
Then in Fragment:
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// ... setup RecyclerView, etc
viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java, viewModelFactory)
viewModel.workPackages.observe(viewLifecycleOwner, Observer { pagedList ->
adapter.submitList(pagedList)
})
}
And in adapter:
class MyAdapter: PagedListAdapter<WorkPackage, MyAdapter.ViewHolder>(WorkPackage.ITEM_CALLBACK) {
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
val workPackage = getItem(position)
if(workPackage != null) {
holder.bind(workPackage)
}
}
}
Long story short, you don't need coroutines here. Use the Paging Library and LiveData, and it will load on the correct threads.
Related
I want to use livedata in an recyclerview. But I only want to observe Livedata with a certain ID. The data gets loaded, but it doesn't update.
So here is the function in m Dao:
#Query("SELECT * FROM zutaten_table NATURAL JOIN table_ref WHERE table_ref.rezeptid = :id")
fun getZutatenforRezept(id:Int): LiveData<List<ZutatenData>>
I use a Viewmodel and a repository like this:
class LiveDataZutatenRepository(private val rezeptDao: AllDao, rezeptID: Int){
val Dao = rezeptDao
val allZutaten = Dao.getZutatenforRezept(rezeptID)
}
class SpecialZutatViewmodel(application: Application): AndroidViewModel(application){
private val repository: JustGetSpecialTypesRepository
private lateinit var repositoryLiveData: LiveDataZutatenRepository
lateinit var ZutatenforRezept : LiveData<List<ZutatenData>>
val Dao : AllDao
init {
Dao = EssenRoomDatabase.getDatabase(application, viewModelScope).allDao()
repository = JustGetSpecialTypesRepository(Dao)
}
suspend fun getRezeptWithZutat(id: Int):RezeptWithZutat{
return repository.getRezeptWithZutatFromID(id)
}
suspend fun getMengen(rezid: Int): List<RefZutatRezept>{
return repository.getMengen(rezid)
}
fun setLiveData(rezeptid: Int){
repositoryLiveData = LiveDataZutatenRepository(Dao, rezeptid )
ZutatenforRezept = repositoryLiveData.allZutaten
}
}
an in my view I use an observer to get the livedata:
val zutatViewmodel = ViewModelProvider(this).get(SpecialZutatViewmodel::class.java)
lifecycleScope.launch {
zutatViewmodel.setLiveData(rezeptid)
}
zutatViewmodel.ZutatenforRezept.observe(this, Observer { zutaten ->
zutaten?.let { adapterzut.setZutaten(it) }
})
Viewholder function:
override fun onBindViewHolder(holder: ZutatenViewHolder, position: Int) {
val current = zutaten[position]
holder.rezepteItemView.text = current.zutname
if(current.bild>=0) {
holder.rezeptePicView.setImageResource(current.bild)
holder.rezeptePicView.drawable.isFilterBitmap = false
}
}
unfortenatly the list doesn't update when the database is changed, but is loaded correctly the first time. What am I doing wrong?
it seems you are using room. So, instead of passing the entire list to the adapter, you can pass a list of all the ids in the database. Then, in the onBindViewholder, you can call the rest of the elements by using the id of the element. the code sample below might give you a better idea -
override fun onBindViewHolder(holder: PassViewHolder, position: Int) {
viewModel.getById(getItem(position)).asLiveData().observe(lifecycleOwner) {
try {
holder.bind(it)
}catch (e:Exception){
Log.e(TAG,"PassData passed = null")
e.printStackTrace()
}
}
}
I had the same problem where the views weren't getting updated but the changes where still being recorded. this method fixed it all.
the below piece of code returns the elements linked to the id as a flow.
viewModel.getById(getItem(position))
And then you covert it to live data and add an observer.
if you want, you can have a look at the project where I implemented this
Update: how to use ViewModelFactory and is it necessary to use this for passing our parameter? What's the benefit? is that going to break live data concept?
I want to send a parameter to my word Dao of my room database for query but in my case, I don't know how to pass that parameter. so let's begin with codes...
WordDao.kt
#Dao
interface WordDao {
#Insert
fun insert(word: Word)
#Update
fun update(word: Word)
#Delete
fun delete(word: Word)
#Query("delete from En_Fa")
fun deleteAllNotes()
#Query("SELECT * FROM En_Fa ORDER BY id ASC")
fun getAllNotes(): LiveData<List<Word>>
#Query("Select * From En_Fa WHERE date == :today ")
fun getTodayWords(today: String): LiveData<List<Word>>
}
WordRepository.kt
class WordRepository(private val wordDao: WordDao, today: String) {
val readAllData: LiveData<List<Word>> = wordDao.getAllNotes()
val readToday: LiveData<List<Word>> = wordDao.getTodayWords(today)
fun addWord(word: Word) {
wordDao.insert(word)
}
}
WordViewModel.kt
class WordViewModel(application: Application): AndroidViewModel(application) {
val readAllData2: LiveData<List<Word>>
private val repository: WordRepository
init {
val wordDao = WordDatabase.getInstance(application).wordDao()
repository = WordRepository(wordDao, today)
readAllData2 = repository.readToday
}
fun addWord(word: Word){
viewModelScope.launch(Dispatchers.IO){
repository.addWord(word)
}
}
}
and this is the line of my code that make an object of this wordview model class in my fragment
private val mWordViewModel: WordViewModel by viewModels()
so how to pass my (today) variable from my fragment to my WordViewModel class
I think you are looking for this:
#Dao
interface WordDao {
// ...
#Query("Select * From En_Fa WHERE date == :today ")
fun getTodayWords2(today: String): <List<Word>
}
Then in the repository:
class WordRepository{
// ... ...
var mutableWords: MutableLiveData<List<Word>> = MutableLiveData()
fun getWords(today: String): List<Word> { // WARNING! run this in background thread else it will crash
return wordDao.getTodayWords2(today)
}
fun getWordsAsync(today: String) {
Executors.newSingleThreadExecutor().execute {
val words = getWords(today)
liveWords.postValue(words) // <-- just doing this will trigger the observer and do next thing, such as, updating ui
}
}
}
Then in your viewModel:
class WordViewModel(application: Application): AndroidViewModel(application) {
// ... ...
val liveWords: LiveData<List<Word>> = repository.mutableWords
fun getWordsAsync(today: String) {
repository.getWordsAsync(today)
}
}
Then finally inside your activity / fragment:
fun viewModelDemo() {
mWordViewModel.liveWords.observe(this, Observer{
// todo: update the ui, eg
someTextView.text = it.toString() // <-- here you get the output
})
someButton.setOnClickListener{
// here you give the input
mWordViewModel.getWordsAsync(today) // get `today` from date picker or something
}
}
Edit
So you have a recyclerView which has an adapter. When the dataset changes, you call notifyDataSetChanged. Suppose, the adapter looks like this:
class MyAdapter: RecyclerView.Adapter<ViewHolder> {
private var words: List<Word> = ArrayList() // initially points to an empty list
override fun getCount() { return words.size }
// ... ... other methods
// public method:
fun submitList(words1: List<Word>) {
this.words = words1 // so now it points to the submitted list
this.notifyDataSetChanged() // this tells recyclerView to update itself
}
}
Then in your activity or fragment:
private lateinit var myAdapter: MyAdapter
override fun onCreate() { // or onViewCreated if using fragment
// ... ... some codes
this.myAdapter = MyAdapter()
binding.recyclerView.adapter = myAdapter
viewModelDemo()
}
fun viewModelDemo() {
mWordViewModel.liveWords.observe(this, Observer{
// todo: update the ui, eg
myAdapter.submitList(it) // <----- Here you call the submitList method
// <-- here you get the output
})
// --- ---
}
I hope this works.
If i get your question correctly then,
First, you need to make a function that will return the liveData object to observe, then you need to use ViewModelProviders that will provide you the object of your ViewModel in your fragment.
mWordViewModel: WordViewModel = ViewModelProvider(this/getActivity()).get(WordViewModel::class.java)
mWordViewModel.getLiveDataFunction().observe(this/lifeCycleOwner, {
process result/response here
}
Then simply use.
mWordViewModel.addWord(today)
I have a ShoppingList App that is built on MVVM architecture Android.
I did not make it, but I followed a tutorial on Youtube.
This is the image of the app(1/2) where the shopping list is shown. The bottom right side is a button for adding new elements of the list.
This is the second view(2/2) where the dialog appears to enter a name of our element and amount of it. Here we have cancel button and add button.
The problem is when I click the ADD button on the Dialog Box I do not know how to get an ID of this added item to the recycler view on my VIEW and to make it appear via the TOAST command on my main Activity.
The question is - How to get an ID of a new added element to my shopping list and show it on my MainActivity(ShoppingActivity) VIEW when I click the ADD button?
If you need additional information ask me out immediately! I will provide you anything you need.
Code is provided here:
ShoppingActivity(View)
class ShoppingActivity : AppCompatActivity(), KodeinAware {
override val kodein by kodein()
private val factory: ShoppingViewModelFactory by instance()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_shopping)
// View Model is being created out of other classes to set changes to View
val viewModel = ViewModelProviders.of(this, factory).get(ShoppingViewModel::class.java)
// Adapters and Recycler View
val adapter = ShoppingItemAdapter(listOf(), viewModel)
rvShoppingItems.layoutManager = LinearLayoutManager(this)
rvShoppingItems.adapter = adapter
// ViewModel makes changes to the Activity
viewModel.getAllShoppingItems().observe(this, Observer {
adapter.items = it
adapter.notifyDataSetChanged()
})
fab.setOnClickListener {
AddShoppingItemDialog(this ,
object: AddDialogListener{
override fun onAddButtonClicked(item: ShoppingItem) {
viewModel.upsert(item)
showToast(viewModel.getID(item).toString().toInt())
}
}).show()
}
}
fun showToast(id: Int) {
Toast.makeText(this#ShoppingActivity, "ID записи: $id", Toast.LENGTH_LONG).show()
}}
ShoppingViewModel(ViewModel)
class ShoppingViewModel(private val repository: ShoppingRepository): ViewModel() {
fun upsert(item: ShoppingItem) = CoroutineScope(Dispatchers.IO).launch {
repository.upsert(item)
}
fun delete(item: ShoppingItem) = CoroutineScope(Dispatchers.IO).launch {
repository.delete( item)
}
fun getID(item: ShoppingItem) = repository.getID(item)
fun getAllShoppingItems() = repository.getAllShoppingItems()
}
AddShoppingItemDialog(the logic of showing Dialog info)
class AddShoppingItemDialog(context: Context, var addDialogListener: AddDialogListener): AppCompatDialog(context) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.dialog_add_shopping_item)
tvAdd.setOnClickListener {
val name = etName.text.toString()
val amount = etAmount.text.toString()
if(name.isEmpty()) {
Toast.makeText(context, "Please enter the name", Toast.LENGTH_LONG).show()
return#setOnClickListener
}
if(amount.isEmpty()) {
Toast.makeText(context, "Please enter the amount", Toast.LENGTH_LONG).show()
return#setOnClickListener
}
val item = ShoppingItem(name, amount.toInt())
// We need to
addDialogListener.onAddButtonClicked(item)
dismiss()
}
tvCancel.setOnClickListener {
cancel()
}
}}
Repository
class ShoppingRepository(private val db: ShoppingDatabase) {
suspend fun upsert(item: ShoppingItem) = db.getShoppingDao().upsert(item)
suspend fun delete(item: ShoppingItem) = db.getShoppingDao().delete(item)
fun getID(item: ShoppingItem) = db.getShoppingDao().getID(item)
fun getAllShoppingItems() = db.getShoppingDao().getAllShoppingItems()}
ShoppingDAO
#Dao
interface ShoppingDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun upsert(item: ShoppingItem) : Long
#Delete
suspend fun delete(item: ShoppingItem)
#Query("SELECT * FROM shopping_items WHERE id = $CURRENT_POSITION")
fun getID(item: ShoppingItem): LiveData<Int>
#Query("SELECT * FROM shopping_items")
fun getAllShoppingItems(): LiveData<List<ShoppingItem>>
}
ShoppingItem
const val CURRENT_POSITION = 0
#Entity(tableName = "shopping_items")
data class ShoppingItem(
#ColumnInfo(name = "item_name")
var name: String,
#ColumnInfo(name = "item_amount")
var amount: Int
) {
#PrimaryKey(autoGenerate = true)
var id: Int? = CURRENT_POSITION
}
AddDialogListener
interface AddDialogListener {
fun onAddButtonClicked(item: ShoppingItem)
}
App View with added items
Since insert/upsert operations on Database are suspend functions, observe returned id in view model
In ShoppingViewModel
private var _itemId : Long = MutableLiveData<Long>()
val itemId : LiveData<Long>
get() = _itemId
fun upsert(item: ShoppingItem) = CoroutineScope(Dispatchers.IO).launch {
val id = repository.upsert(item)
_itemId.postValue(id)
}
In ShoppingActivity,
viewModel.itemId.observe(this, Observer {id ->
showToast(id)
})
Please let me know if you need to know more details.
You need to use SingleLiveEvent to observe the value only once.
Add this SingleLiveEvent class
/**
* A lifecycle-aware observable that sends only new updates after subscription, used for events like
* navigation and Snackbar messages.
*
*
* This avoids a common problem with events: on configuration change (like rotation) an update
* can be emitted if the observer is active. This LiveData only calls the observable if there's an
* explicit call to setValue() or call().
*
*
* Note that only one observer is going to be notified of changes.
*/
class SingleLiveEvent<T> : MutableLiveData<T>() {
private val mPending = AtomicBoolean(false)
#MainThread
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
if (hasActiveObservers()) {
Log.w(TAG, "Multiple observers registered but only one will be notified of changes.")
}
// Observe the internal MutableLiveData
super.observe(owner, Observer { t ->
if (mPending.compareAndSet(true, false)) {
observer.onChanged(t)
}
})
}
#MainThread
override fun setValue(t: T?) {
mPending.set(true)
super.setValue(t)
}
/**
* Used for cases where T is Void, to make calls cleaner.
*/
#MainThread
fun call() {
value = null
}
companion object {
private val TAG = "SingleLiveEvent"
}
}
In ShoppingViewModel
Replace this
private var _itemId : Long = MutableLiveData<Long>()
val itemId : LiveData<Long>
get() = _itemId
with
private var _itemId : Long = SingleLiveEvent<Long>()
val itemId : LiveData<Long>
get() = _itemId
You can use Event wrapper when there are more than one observer.
For more details:
https://medium.com/androiddevelopers/livedata-with-snackbar-navigation-and-other-events-the-singleliveevent-case-ac2622673150
i want to use SearchView for search some element in room db and i have a problem with this because i cant use getFilter in RecyclerViewAdapter because i have ViewModel maybe whoes know how to combine all of this element in one project.
I search one way use Transormations.switchMap. But I couldn’t connect them.
ProductViewModel
class ProductViewModel(application: Application) : AndroidViewModel(application) {
private val repository: ProductRepository
val allProducts: LiveData<List<ProductEntity>>
private val searchStringLiveData = MutableLiveData<String>()
init {
val productDao = ProductsDB.getDatabase(application, viewModelScope).productDao()
repository = ProductRepository(productDao)
allProducts = repository.allProducts
searchStringLiveData.value = ""
}
fun insert(productEntity: ProductEntity) = viewModelScope.launch {
repository.insert(productEntity)
}
val products = Transformations.switchMap(searchStringLiveData) { string ->
repository.getAllListByName(string)
}
fun searchNameChanged(name: String) {
searchStringLiveData.value = name
}
}
ProductDao
interface ProductDao {
#Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insertProduct(productEntity: ProductEntity)
#Query("SELECT * from products")
fun getListAllProducts(): LiveData<List<ProductEntity>>
#Query("DELETE FROM products")
suspend fun deleteAll()
#Query("SELECT * FROM products where product_name_ent LIKE :name or LOWER(product_name_ent) like LOWER(:name)")
fun getListAllByName(name: String):LiveData<List<String>>
}
ProductDao
#Query("SELECT * FROM products where product_name_ent LIKE :name or LOWER(product_name_ent) like LOWER(:name)")
fun getListAllByName(name: String):LiveData<List<ProductEntity>>
This Method in your dao should return LiveData<List<ProductEntity>> and not LiveData<List<String>>, because this query selects everything (*) from the entity and not a specific column.
it is similar to (#Query("SELECT * from products") fun getListAllProducts():LiveData<List<ProductEntity>>)
ProductViewModel
class ProductViewModel(application: Application) : AndroidViewModel(application) {
private val repository: ProductRepository
init {
val productDao = ProductsDB.getDatabase(
application,
viewModelScope
).productDao()
repository = ProductRepository(productDao)
}
private val searchStringLiveData = MutableLiveData<String>("") //we can add initial value directly in the constructor
val allProducts: LiveData<List<ProductEntity>>=Transformations.switchMap(searchStringLiveData)
{
string->
if (TextUtils.isEmpty(string)) {
repository.allProducts()
} else {
repository.allProductsByName(string)
}
}
fun insert(productEntity: ProductEntity) = viewModelScope.launch {
repository.insert(productEntity)
}
fun searchNameChanged(name: String) {
searchStringLiveData.value = name
}
}
Repository
...with the other method that you have, add the following:
fun allProducts():LiveData<List<ProductEntity>>=productDao.getListAllProducts()
fun allProductsByNames(name:String):LiveData<List<ProductEntity>>=productDao.getListAllByName(name)
In Your Activity Or Fragment Where you have the recyclerview adapter
inside onCreate() (if it is an activity)
viewModel.allProducts.observe(this,Observer{products->
//populate your recyclerview here
})
or
onActivityCreated(if it is a fragment)
viewModel.allProducts.observe(viewLifecycleOwner,Observer{products->
//populate your recyclerview here
})
Now set a listener to the searchView, everytime the user submit the query , call viewModel.searchNameChanged(// pass the new value)
Hope this helps
Regards,
I am creating a social media app and I am following MVVM Pattern
I am stuck at observing data from DB . As far I know is the Dao methods have to be executed on the background thread. But I am not able to implement viewmodel from Repository Class.I will paste the code ,Can anyone give me some help?
Dao
#Dao
interface FeedDao{
//Post feed item
#Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(feed:Feed):Long
//Update feed item
#Update(onConflict = OnConflictStrategy.IGNORE)
fun update(feed: Feed):Int
//Get feed from user friend's
#Query("SELECT * from feed")
fun getAllFeed():LiveData<List<Feed>>
}
Repository
class FeedRepository private constructor(private val feedDao: FeedDao) {
companion object {
#Volatile private var instance:FeedRepository? = null
private val apiClient = ApiClient.getApiClient().create(UserClient::class.java)
fun getInstance(feedDao: FeedDao)= instance?: synchronized(this){
instance ?: FeedRepository(feedDao).also { instance=it }
}
}
private fun getFeedResponse() {
//TODO:Check to change parameter values after adding Paging Functionality
val call: Call<List<Feed>> = apiClient.getFeed(20, 0)
call.enqueue(object :Callback<List<Feed>>{
override fun onFailure(call: Call<List<Feed>>?, t: Throwable?) {
Log.e("FeedRepository","Feed Callback Failure")
Timber.d(t?.message)
}
override fun onResponse(call: Call<List<Feed>>?, response: Response<List<Feed>>?) {
if(response!!.isSuccessful){
Timber.i("Successful Response -> Adding to DB")
addResponseTODB(response.body()!!)
}else{
when(response.code()){
400 -> Timber.d("Not Found 400")
500 -> Timber.d("Not logged in or Server broken")
}
}
}
})
}
private fun addResponseTODB(items:List<Feed>){
Timber.d("Response --> DB Started")
object : AsyncTask<List<Feed>,Void,Boolean>(){
override fun doInBackground(vararg params: List<Feed>?): Boolean {
var needsUpdate:Boolean = false
for(item in params[0]!!.iterator()){
var inserted = feedDao.insert(item)
if(inserted.equals(-1)){
var updated = feedDao.update(item)
if(updated > 0){
needsUpdate = true
}
}else{
needsUpdate = true
}
}
return needsUpdate
}
override fun onPostExecute(result: Boolean?) {
if (result!!){
loadFromDB()
}
}
}.execute(items)
}
private fun loadFromDB(){
Timber.d("loadFromDB")
object :AsyncTask<Void,Void,LiveData<List<Feed>>>(){
override fun doInBackground(vararg params: Void?): LiveData<List<Feed>>? {
return feedDao.getAllFeed()
}
override fun onPostExecute(result: LiveData<List<Feed>>?) {
}
}.execute()
}
public fun fetchFeed(){
Timber.d("fetchFeed")
loadFromDB()
getFeedResponse()
}
}
ViewModel
class FeedViewModel private constructor(private val
feedDao:FeedDao):ViewModel(){
// TODO: Implement the ViewModel
private val feedRepository= FeedRepository.getInstance(feedDao)
public val feed = MediatorLiveData<List<Feed>>()
val value = MutableLiveData<Int>()
init {
feed.addSource(feed,feed::setValue)
}
companion object {
private const val NO_FEED = -1
}
fun getFeedLD() = feed
}
First, you don't need to insert all Feed objects one by one in the Database. You can create a method inside FeedDao which to insert List<Feed>
#Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(feed: List<Feed>):Long
Than, it is not necessary to call loadFromDB() inside the onPostExecute() method, because your fun getAllFeed(): LiveData<List<Feed>>() method from #FeedDao returns LiveData and you can add Observer to it and every time when the data inside the Database is changes the Observer will be triggered.
And for your Network/Database requests you always can use NetworkBoundResource https://github.com/googlesamples/android-architecture-components/blob/master/GithubBrowserSample/app/src/main/java/com/android/example/github/repository/NetworkBoundResource.kt
Hope I helped somehow :)