Search code examples
androidmvvmviewmodelandroid-livedataobserver-pattern

Live Data Observer called only once. It is not updating the data from server when api is called again to update UI


I looked for many articles and tried to understand how Live Data is observe changes when MVVM architecture is used.

I have a Fragment A, ViewModel and Repository class. ViewModel is initiated in onCreateView() method of the fragment. Api call is initiated just after that in onCreateView() method of fragment. Data from the Server is observed in onViewCreated method of the fragment.

For the first, it is running perfectly fine. But When I update the user name from another Fragment B and come back to Fragment A.

Api is called again in onResume() method of Fragment A to update UI. But here my Live Data is not observed again and UI is not updated

I didn't understand what I am doing wrong? Why observer is not triggering second time?

Below is the code

class FragmentA : Fragment(){

    private lateinit var dealerHomeViewModel: DealerHomeViewModel
  
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val view = inflater.inflate(R.layout.fragment_home_dealers, container, false)

        val dealerHomeFactory = DealerHomeFactory(token!!)
        dealerHomeViewModel = ViewModelProvider(this,dealerHomeFactory).get(DealerHomeViewModel::class.java)

      dealerHomeViewModel.getDealerHomeData()
        return view
    }

 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
          dealerHomeViewModel.dealerInfoLiveData.observe(viewLifecycleOwner, androidx.lifecycle.Observer {dealerInfo ->
            // Update UI
            tvDealerName.text = dealerInfo.name
        
        })
    }

 override fun onResume() {
        super.onResume()
        dealerHomeViewModel.getDealerHomeData()
    }
}

//=========================== VIEW MODEL ===================================//
class DealerHomeViewModel(val token:String) : ViewModel() {

    var dealerInfoLiveData:LiveData<DealerInfo>

    init {
        dealerInfoLiveData = MutableLiveData()
    }

    fun getDealerHomeData(){
        dealerInfoLiveData = DealerHomeRepo().getDealerHomePageInfo(token)
    }
}

//======================== REPOSITORY ================================//
class DealerHomeRepo {

    fun getDealerHomePageInfo(token:String):LiveData<DealerInfo>{
        val responseLiveData:MutableLiveData<DealerInfo> = MutableLiveData()
        val apiCall: ApiCall? = RetrofitInstance.getRetrofit()?.create(ApiCall::class.java)
        val dealerInfo: Call<DealerInfo>? = apiCall?.getDealerInfo(Constants.BEARER+" "+token,Constants.XML_HTTP)

        dealerInfo?.enqueue(object : Callback<DealerInfo>{
            override fun onFailure(call: Call<DealerInfo>, t: Throwable) {
                Log.d(Constants.TAG,t.toString())
            }

            override fun onResponse(call: Call<DealerInfo>, response: Response<DealerInfo>) {
                if(response.isSuccessful){
                    when(response.body()?.status){
                        Constants.SUCCESS -> {
                            responseLiveData.value = response.body()
                        }

                        Constants.FAIL -> {

                        }
                    }
                }
            }
        })

        return responseLiveData
    }
}

Solution

  • I think your problem is that you are generating a NEW mutableLiveData each time you use your getDealerHomePageInfo(token:String method.

    First time you call getDealerHomePageInfo(token:String) you generate a MutableLiveData and after on onViewCreated you observe it, it has a value.

    In onResume, you call again getDealerHomePageInfo(token:String) that generates a NEW MutableLiveData so your observer is pointing to the OLD one.

    What would solve your problem is to pass the reference of your viewModel to your repository so it updates the MutableLiveData with each new value, not generate a new one each time.

    Edited Answer:

    I would do something like this for ViewModel:

    class DealerHomeViewModel(val token:String) : ViewModel() {
        private val _dealerInfoLiveData:MutableLiveData<DealerInfo> = MutableLiveData()
        val dealerInfoLiveData:LiveData = _dealerInfoLiveData
    
    
        fun getDealerHomeData(){
            DealerHomeRepo().getDealerHomePageInfo(token, _dealerInfoLiveData)
        }
    }
    

    And this for the DealerHomeRemo

    class DealerHomeRepo{
    
        fun getDealerHomePageInfo(token:String, liveData: MutableLiveData<DealerInfo>){
            val apiCall: ApiCall? = RetrofitInstance.getRetrofit()?.create(ApiCall::class.java)
            val dealerInfo: Call<DealerInfo>? = apiCall?.getDealerInfo(Constants.BEARER+" "+token,Constants.XML_HTTP)
    
            dealerInfo?.enqueue(object : Callback<DealerInfo>{
                override fun onFailure(call: Call<DealerInfo>, t: Throwable) {
                    Log.d(Constants.TAG,t.toString())
                }
    
                override fun onResponse(call: Call<DealerInfo>, response: Response<DealerInfo>) {
                    if(response.isSuccessful){
                        when(response.body()?.status){
                            Constants.SUCCESS -> {
                                liveData.value = response.body()
                            }
    
                            Constants.FAIL -> {
    
                            }
                        }
                    }
                }
            })
        }
    

    For Observers, use the LiveData as before:

    dealerHomeViewModel.dealerInfoLiveData.observe(viewLifecycleOwner, androidx.lifecycle.Observer {dealerInfo ->
                // Update UI
                tvDealerName.text = dealerInfo.name
            
            })