androidkotlinmvvmandroid-livedatamutablelivedata

In Kotlin Android, viewModel has no observer


I made a toolbar in a BaseActivity to implement a common and the code is as follows.

// BaseActivity
abstract class BaseActivity<T : ViewBinding> : AppCompatActivity() {
    lateinit var cartCnt: TextView

    private val viewModel by lazy {
        ViewModelProvider(this, CartViewModelFactory())[CartViewModel::class.java]
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, layoutId)
        mContext = this

        viewModel.cartItemList.observe(this){
            cartCnt.text = it.size.toString()
        }

        supportActionBar?.let {
            setCustomActionBar()
        }

    }
    open fun setCustomActionBar() {

        val defActionBar = supportActionBar!!
        defActionBar.elevation = 0F
        defActionBar.displayOptions = ActionBar.DISPLAY_SHOW_CUSTOM
        defActionBar.setCustomView(R.layout.custom_action_bar)

        val toolbar = defActionBar.customView.parent as Toolbar
        toolbar.setContentInsetsAbsolute(0, 0)
        cartCnt = defActionBar.customView.findViewById(R.id.cartCnt)

    }

}

In BaseActivity, the text of TextView called cartCnt (the number of products currently in the shopping cart) is observed from MutableLiveData in the CartView Model.

Is as follows : cartviewmodel

// CartViewModel
class CartViewModel() : ViewModel() {

    private val list = mutableListOf<Cart>()
    private val _cartItemList: MutableLiveData<List<Cart>> = MutableLiveData()
    val cartItemList: LiveData<List<Cart>> get() = _cartItemList


    private val repository by lazy {
        CartRepository.getInstance()
    }

    init {
        getAllCartItems()
    }


    fun getAllCartItems() {
        viewModelScope.launch {
            repository!!.getRequestMyCartList {
                if (it is Result.Success) {
                    list.addAll(it.data.data!!.carts)
                    _cartItemList.value = list
                }
            }

        }
    }

    fun addToCartItem(id: Int) {
        viewModelScope.launch {
            repository!!.postRequestAddCart(id) {
                if (it is Result.Success) {
                    list.add(it.data.data!!.cart)
                    _cartItemList.value = list
                }
            }

        }
    }
}

The observer of the View Model existed only in SplashActivity, which first inherited BaseActivity. (verified as a function hasObservers.).

When I clicked on the shopping basket button on the product list page, I communicated with the server and confirmed that the shopping basket data was normally put in the server table, and I also confirmed that the 200 status code was returned normally.

However, when Fragment, which has a product list page, declared cartViewModel and called the addToCartItem function, there was no observer attached to the cartViewModel. This is the part confirmed through the hasObservers function.

The view structure roughly has MainActivity inherited from BaseActivity, and TodayFragment exists in MainActivity.

And, TodayFragment's code is as follows.

// TodayFragment
class TodayFragment : BaseFragment<FragmentTodayBinding>() {

    override val layoutId: Int = R.layout.fragment_today
    private lateinit var bannerViewPager: BannerRecyclerviewAdapter
    private lateinit var productAdapter: ProductHorizonRecyclerviewAdapter

    private val cartViewModel by lazy {
        ViewModelProvider(this, CartViewModelFactory())[CartViewModel::class.java]
    }

    override fun init() {

    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        initProductRecyclerview()
        setValues()
    }

    override fun setValues() {
        HomeViewModel.currentPosition.observe(viewLifecycleOwner) {
            binding.bannerViewpager.currentItem = it
        }
    }


    private fun initProductRecyclerview(){
        binding.productRecyclerView.apply {
            productAdapter = ProductHorizonRecyclerviewAdapter(){
                cartViewModel.addToCartItem(it.id)
            }
            adapter = productAdapter
            layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)
        }

    }

}

In other words, when the cartViewModel's addToCartItem function is called through the product list page in TodayFragment, the mutableLiveData of the cartViewModel changes, and the cartCnt TextView of BaseActivity is observing this change.

In this situation, I wonder why the first SplashActivity, which appears in the activity stack structure, has observer, and then disappears in the Today Fragment.

Somebody help me.


Solution

  • You are recreating cartViewModel in TodayFragment by passing it a factory which is why it doesn't have the BaseActivity observer. Try this from within TodayFragment

    private val cartViewModel: CartViewModel by activityViewModels()
    

    or

    private val cartViewModel by lazy {
        ViewModelProvider(requireActivity())[CartViewModel::class.java]
    }
    

    Then if you call cartViewModel.addToCartItem() in TodayFragment it should call the observer in BaseActivity.