【问题标题】:Android: Variable gets uninitialized in ViewModel after being initialized in the FragmentAndroid:在片段中初始化后,变量在 ViewModel 中未初始化
【发布时间】:2020-12-26 12:29:59
【问题描述】:

我的片段中有一个回调方法,该方法从它的 ViewModel 中调用。它在fragment的OnCreateView()方法中初始化变量,但是当ViewModel调用它使用它时,它是null。

我认为这可能与虚拟机以某种方式重新创建有关?我就是想不通。

我正在关注this answer's VM 如何驱动 UI。他们提供 Google 的 sample 正在创建的回调接口 (TasksNavigator.java),覆盖视图中的方法 (TasksActivity.java),然后从 VM (TasksViewModel.java) 调用该方法,但似乎没有为我工作。

片段

class SearchMovieFragment : Fragment(), SearchNavigator {

    companion object {
        fun newInstance() = SearchMovieFragment()
    }

    private lateinit var searchMovieFragmentViewModel: SearchMovieFragmentViewModel
    private lateinit var binding: SearchMovieFragmentBinding
    private lateinit var movieRecyclerView: RecyclerView

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

        searchMovieFragmentViewModel = ViewModelProvider(this).get(SearchMovieFragmentViewModel::class.java)
        binding = DataBindingUtil.inflate(inflater, R.layout.search_movie_fragment, container, false)
        binding.viewmodel = searchMovieFragmentViewModel
        searchMovieFragmentViewModel.setNavigator(this)

        setUpRecyclerView(container!!.context)
        return binding.root
    }

    private fun setUpRecyclerView(context: Context) {
        movieRecyclerView = binding.searchMovieFragmentRecyclerView.apply {
            this.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
        }
        val adapter = MovieListAdapter()
        binding.searchMovieFragmentRecyclerView.adapter = adapter
        searchMovieFragmentViewModel.movieList.observe(viewLifecycleOwner, Observer {
            adapter.submitList(it)
        })
    }

    override fun openDetails() {
        Log.d("TEST", "opening details")
    }
}

视图模型


class SearchMovieFragmentViewModel : ViewModel(), MovieSearchItemViewModel {

    private lateinit var searchNavigator: SearchNavigator
    val editTextContent = MutableLiveData<String>()
    var movieList = Repository.getMovieList("batman")

    fun setNavigator(_searchNavigator: SearchNavigator) {
        this.searchNavigator = _searchNavigator
        if (searchNavigator != null) {
            Log.d("TEST", "its not null $searchNavigator") // Here it is not null
        }
    }

    private fun getMovieDetail(movieId: String) {
        val movie = Repository.getMovieDetail(movieId)

        Log.d("TEST", "checking ${this.searchNavigator}") // Here is where I call it but it is null
//        searchNavigator.openDetails()
    }
    private fun getMovieList(movieSearch: String): MutableLiveData<List<Movie>> = Repository.getMovieList(movieSearch)

    override fun displayMovieDetailsButton(movieId: String) {
        Log.d("TEST", "button clicked $movieId")
        getMovieDetail(movieId)
    }
}

回调接口

interface SearchNavigator {
    fun openDetails()
}

【问题讨论】:

    标签: android kotlin mvvm viewmodel


    【解决方案1】:

    在fragment的下面方法中初始化ViewModel

    override onActivityCreated(@Nullable final Bundle savedInstanceState){
        searchMovieFragmentViewModel = ViewModelProvider(this).get(SearchMovieFragmentViewModel::class.java)
    }
    

    我建议使用实时数据在 ViewModel 和 Fragment 之间创建连接,这将是更安全和正确的方法。

    根据实时数据中的触发器触发 openDetails。禁止将视图 (context) 实例发送到 ViewModel,即使您将其包装起来,因为内存泄漏的可能性很高。

    但是如果您仍然想遵循这种方法,那么您应该在您的 ViewModel 中注册和取消注册片段实例(保留 SearchNavigator 的列表)onStop()onStart()

    并遍历它们以调用 openDetails

    【讨论】:

    • 感谢您的快速回复!我的 MVVM 是基于 this 的更新答案,它说明了在 Google 的 sample 中 VM 如何控制导航。
    • 我看不到 Fragment 将其实例传递给 ViewModel。但是,如果您仔细查看示例,他们会在 Activity(onCreate) 中注册 Navigator 并取消注册 onDestroy。另一方面,TaskViewModel 只是一个 Observable 而不是 ViewModel(Android)。 @efoc
    • 啊,谢谢!我没注意到!那我这样就不行了?
    • 如果你真的想走这条路,你只需要在 onStart 和 onStop 中注册和取消注册你的片段。 @efoc。我将建议您遵循示例或使用将传送片段的实时数据进行导航。因此无需将片段实例注册到 VM。
    • @Anmol 你能帮忙吗 --> stackoverflow.com/questions/64421965/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-19
    • 2021-04-04
    • 2017-02-23
    • 1970-01-01
    • 2016-07-31
    • 1970-01-01
    相关资源
    最近更新 更多