Android ViewModel 使用刀柄实例化两次

时间:2021-06-19 06:19:56

标签: android kotlin android-fragments fragment dagger-hilt

我有一个父片段,它使用 ViewModel 和 Retrofit 从 API 获取列表,ViewModel 注入了 Hilt。 获取列表后,父片段将传递给父片段内的子片段。 但问题是 ViewModel 在子片段中又被实例化了一次。

父片段

@AndroidEntryPoint
class ParentFragment : Fragment() {

    override val mViewModel: URLViewModel by viewModels()

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        mViewBinding = getViewBinding(inflater, container)

        mViewModel.liveData.observe(this, { data ->
            {
                childFragmentManager.beginTransaction().apply {

                    replace(
                        mViewBinding.fragmentContainer.id,
                        ChildFragment(data)
                    )
                }

                commit()
            } })


        mViewModel.getURL("TEST", "2021-06-18", "2021-07-18", 1 , 0 , -1, false)

    return mViewBinding.root
}

}

子片段

@AndroidEntryPoint
class ChildFragment(val data: List<Item>) : Fragment() {

    override val mViewModel: URLViewModel by viewModels()

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        mViewBinding = getViewBinding(inflater, container)

        // mViewModel is instantiated again and some all strings properties of it is null.


        return mViewBinding.root
    }

}

URLViewModel


@HiltViewModel
class URLViewModel @Inject constructor(private val urlApi: URLApi): ViewModel() {

    private val _urlLiveData = MutableLiveData<State<Any?>>()

    val urlLiveData: LiveData<State<Any?>> = _urlLiveData


    var urlName: String? = null

    var beginDate: String? = null

    var endDate: String? = null

    var adultCount = 0

    var childrenCount = 0

    var airportId = 0

    var isRoundTrip = false



    init {
        Log.e("URLViewModel", "iniialed again" )

    }

    @ExperimentalStdlibApi
    fun getUrl(urlName: String, beginDate: String, endDate: String, adultCount: Int, childCount: Int, airportId: Int, isRoundTrip: Boolean){

        Log.e("XXXXXX", "getUrl: called with url of " + urlName )


        this.urlName = urlName

        this.beginDate = beginDate

        this.endDate = endDate

        this.adultCount = adultCount

        this.childrenCount = childCount

        this.airportId = airportId

        this.isRoundTrip = isRoundTrip

        val mutableLiveData = MutableLiveData<State<Any?>>()

        mutableLiveData.value = State.loading()

        viewModelScope.launch {

            val res = urlApi.getURL(urlName,beginDate,endDate,adultCount,childCount,airportId,isRoundTrip)

            Log.e("URLVIewModel", "getUrl: response received" )
            _urlLiveData.value = res

        }
    }
}

当我想访问一些像beginDate这样的属性时,它们是空的,因为ViewModel又被实例化了,

2 个答案:

答案 0 :(得分:0)

您正在尝试使用共享视图模型。尝试以下代码以供参考。

  @AndroidEntryPoint
 class ParentFragment : Fragment() {
private lateinit var viewModel: URLViewModel

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    viewModel = ViewModelProvider(requireActivity()).get(URLViewModel::class.java)
}

}


@AndroidEntryPoint
class ChildFragment : Fragment() {
private lateinit var viewModel: URLViewModel

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    viewModel = ViewModelProvider(requireActivity()).get(URLViewModel::class.java)
}

}

答案 1 :(得分:0)

viewModels() 委托针对同一个实例创建视图模型,即在您的案例中的 Fragment 实例。您需要做的是创建一个共享的 View 模型。 ktx 库提供了辅助委托。

添加您已经拥有的 ktx 依赖项,我猜是 here

implementation "androidx.fragment:fragment-ktx:1.3.4"

并使用

创建视图模型
private val viewModel by activityViewModels<UrlViewModel>()