Android 带有viewmodel和live数据的ViewPager,所有6个选项卡数据都将替换为最后一个选项卡数据

Android 带有viewmodel和live数据的ViewPager,所有6个选项卡数据都将替换为最后一个选项卡数据,android,android-viewpager,android-livedata,android-viewmodel,Android,Android Viewpager,Android Livedata,Android Viewmodel,我正在使用一个带有6个选项卡的查看页面,其中只有一个片段TimesListFragment 根据传递给TimesListFragment的参数,它调用api eg;科学、技术、旅游等 我的应用程序遵循了谷歌的GithubBrowserSample 我有TimesListFragment->TimesViewModel->TimesRepository 有6个选项卡,当我点击api时,所有选项卡都显示相同的结果,如果是最后一个参数 StoriesPagerAdapter.kt class Stor

我正在使用一个带有6个选项卡的
查看页面
,其中只有一个片段
TimesListFragment

根据传递给
TimesListFragment
的参数,它调用api eg;科学、技术、旅游等

我的应用程序遵循了谷歌的
GithubBrowserSample

我有
TimesListFragment
->
TimesViewModel
->
TimesRepository

有6个选项卡,当我点击api时,所有选项卡都显示相同的结果,如果是最后一个
参数

StoriesPagerAdapter.kt

class StoriesPagerAdapter(fragmentManager: FragmentManager?)
    :FragmentStatePagerAdapter(fragmentManager){
    private val sections= arrayListOf("science","technology","business","world","movies","travel")
    override fun getItem(position: Int): Fragment {
        return TimesListFragment.newInstance(sections[position])
    }
    override fun getCount(): Int {
        return sections.size
    }
    override fun getPageTitle(position: Int): CharSequence? {
        return sections[position]
    }
}
class TimesRepository @Inject constructor(private val apiService: ApiService,
                                          private val timesDao: TimesDao,
                                          private val appExecutors: AppExecutors) {

    private val repoListRateLimit = RateLimiter<String>(10, TimeUnit.MINUTES)

    fun loadStories(section:String): LiveData<Resource<TimesStoriesResponse>> {
        return object : NetworkBoundResource<TimesStoriesResponse, TimesStoriesResponse>(appExecutors) {
            override fun saveCallResult(item: TimesStoriesResponse) {
                timesDao.insert(item)
            }

            override fun shouldFetch(data: TimesStoriesResponse?): Boolean {
                return data == null  || repoListRateLimit.shouldFetch(section)
            }

            override fun loadFromDb() = timesDao.load()

            override fun createCall() = apiService.getTopStories(section,ConfigConstant.TIMES_KEY)

            override fun onFetchFailed() {
                repoListRateLimit.reset(section)
            }
        }.asLiveData()
    }
interface ApiService {
    @GET("svc/topstories/v2/{section}.json?")
    fun getTopStories(@Path ("section") section:String,@Query("api-key") apiKey:String)
            :LiveData<ApiResponse<TimesStoriesResponse>>
}
private fun initViewModel() {

        viewModel = ViewModelProviders.of(this, viewModelFactory).get(section,TimesViewModel::class.java)

    }

    private fun initData() {
        viewModel?.fetchStories(section)?.observe(this, Observer {

            when (it?.status) {

                Status.LOADING -> {
                    showLoading(true)
                    showError(false,null)
                }


                Status.SUCCESS -> {
                    showLoading(false)
                    showError(false,null)
                    showSuccessData(it.data)
                }

                Status.ERROR -> {
                    showLoading(false)
                    showError(true,it.message)
                }

            }
        })
    }
问题:所有选项卡都显示
旅行
参数的数据

TimesViewModel

class TimesViewModel @Inject constructor(private val timesRepository: TimesRepository) :
        ViewModel() {
    lateinit var data: LiveData<Resource<TimesStoriesResponse>>
    fun fetchStories(section:String): LiveData<Resource<TimesStoriesResponse>> {
        data = timesRepository.loadStories(section)
        return data
    }
}

注意:这两个方法都是在
TimeListFragmnet的onViewCreated()中调用的

这应该是由于您的
视图模型的缘故

通常,由于
ViewModel
的工作方式,每个
活动或
片段都有一个
ViewModel
。使用
ViewModel
的一个主要好处是,它的生命周期与
片段的生命周期完全分离,因此可以多次销毁和重新创建
片段,并且您仍然能够恢复存储在
ViewModel
中的当前数据

因此,这意味着使用从
ViewModelProviders
获取
ViewModel
的典型代码,您将获取完全相同的
ViewModel

通常,这不会导致问题,但在您的
ViewPager
中,您正在重用相同的
TimesListFragment
,它很可能调用相同的
ViewModel
,因此导致每个片段显示相同的数据

解决方案是使用:

ViewModelProviders.of(this).get(KEY, TimesViewModel::class.java)

请注意用于区分需要获取的ViewModels的键。因此,通过使用
ViewPager
中的位置作为唯一键,您应该能够为每个
TimesListFragment
拥有唯一的
ViewModel
,您需要告诉
ViewPager
不要加载当前未显示的页面。我想,默认情况下,它至少在左侧加载一页,在右侧加载一页。

你能发布ApiService的代码吗?发布的ApiService代码…是的,我也有同样的问题,因为默认情况下,
ViewPager
来回缓存一页。因此,一种解决方案是使用
ViewPager
片段中的
setUserVisibleHint()
方法来管理片段在
ViewPager
中是否可见。@Jeel Vankhede:没有任何区别。我认为这与LiveDataTimeListFragment有关,每次调用TimeListFragment的onCreate()时,这意味着每次创建fragment的新实例时,都会创建TimeViewModel的新实例。我是对的吗?不,这个.get(TimesViewModel::class.java)
的工作方式是,如果它不存在,它将创建一个新的
TimesViewModel
实例。但是如果它已经存在,那么它将返回现有的一个。因此,所有6个
TimesListFragment
都将使用相同的
ViewModel
。但是如果您使用
ViewModelProviders.of(this).get(KEY,TimesViewModel::class.java)
来指定
KEY
,那么您将能够创建一个属于每个
TimesListFragment
的唯一
ViewModel
。尝试在KEY中传递选项卡标题(科学、技术等)以区分ViewModel,不起作用:(.viewModel=ViewModelProviders.of(this,viewModelFactory).get(section,TimesViewModel::class.java)有什么建议吗?对我来说不起作用,我有两个不同的片段(从同一父级继承)生活在FragmentPagerAdapter中,在我实例化视图模型的每个片段中(与viewmodel类相同,但有2个实例)。chatRoomViewModel=ViewModelProviders.of(this).get(“KEY1”,chatRoomViewModel.class);chatRoomViewModel=ViewModelProviders.of(this).get(“KEY2”,chatRoomViewModel.class);即使我正在更改片段1中的数据,片段2中的observer方法也会被调用,有办法将其作废吗?