Ajeeli
Ajeeli

Reputation: 373

RecyclerView not updating items

MainFragment contains a RecyclerView of paths, when the user selects a path it navigates to CourseFragment which contains a RecyclerView of relevant courses. However, if the user goes back to choose a different path, the same course items are shown every time.

Fragments and recyclerviews

Course Fragment

class CourseFragment : Fragment(),
    CourseRecyclerAdapter.CourseItemListener {

    private lateinit var viewModel: CourseViewModel
    private lateinit var recyclerView: RecyclerView
    private lateinit var navController: NavController

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        Log.i(LOG_TAG, "Course Fragment onCreateView(): selectedItem = $globalSelectedPath")

        val view = inflater.inflate(R.layout.fragment_course, container, false)
        recyclerView = view.findViewById(R.id.courseRecyclerView)
        navController = Navigation.findNavController(requireActivity(), R.id.nav_host )
        viewModel = ViewModelProvider(requireActivity()).get(CourseViewModel::class.java)
        viewModel.courseData.observe(viewLifecycleOwner, Observer {
            val adapter =
                CourseRecyclerAdapter(
                    requireContext(),
                    it,
                    this
                )
            recyclerView.adapter = adapter
        } )
        return view
    }

CourseViewModel

class CourseViewModel(app: Application): AndroidViewModel(app) {

    private val courseDataRepository = CourseRepository(app)
    val courseData = courseDataRepository.courseData
    val selectedCourse = MutableLiveData<Course>()
}

Course Repository

class CourseRepository(val app: Application) {

    val courseData = MutableLiveData<List<Course>>()

    init {
        CoroutineScope(Dispatchers.IO).launch {
            callWebService()
        }
    }

    @WorkerThread
    suspend fun callWebService() {
        if (Utility.networkAvailable(app)) {

            val retrofit = Retrofit.Builder().baseUrl(WEB_SERVICE_URL).addConverterFactory(MoshiConverterFactory.create()).build()
            val service = retrofit.create(CourseService::class.java)
            val serviceData = service.getCourseData(globalSelectedPath).body() ?: emptyList()
            courseData.postValue(serviceData)
        }
        else
            Toast.makeText(app, Resources.getSystem().getString(R.string.noConnectivity), Toast.LENGTH_LONG).show()
    }
}

After logging it shows that CourseRepository is only called once, hence, the callWebService() is only triggered once, and new data is not retrieved.

2020-05-02 12:24:02.520 I/mylog: Course Fragment onCreateView(): selected path = MOB001
2020-05-02 12:24:02.529 I/mylog: Course Repository callWebService(): selected path = MOB001
-------------------------------
2020-05-02 12:24:35.009 I/mylog: Course Fragment onCreateView(): selected path = WEB999

To make sure i made an Okhttp interceptor log of the callWebService() which confirmed that data was only fetched once:

    12:24:02.551 D/OkHttp: --> GET https://.../mobile/feed/course_data.php?pathName=MOB001
    12:24:02.551 D/OkHttp: --> END GET
    12:24:03.007 D/OkHttp: <-- 200 https://.../mobile/feed/course_data.php?pathName=MOB001 (455ms)
    12:24:03.007 D/OkHttp: date: Sat, 02 May 2020 09:24:03 GMT
    12:24:03.007 D/OkHttp: server: Apache
    12:24:03.007 D/OkHttp: x-powered-by: PHP/5.6.40
    12:24:03.007 D/OkHttp: vary: Accept-Encoding
    12:24:03.007 D/OkHttp: content-type: text/html; charset=UTF-8
    12:24:03.009 D/OkHttp: [{"courseName":"Android App Development Essentials","instructor":"John Lennon","courseDescription":"Description ...","courseImage":"android_development_essentials.jpg","instructorImage":"john.jpg"}]
    12:24:03.009 D/OkHttp: <-- END HTTP (206-byte body)

Upvotes: 0

Views: 172

Answers (3)

Ajeeli
Ajeeli

Reputation: 373

It turns out that in the following line:

viewModel = ViewModelProvider(requireActivity()).get(CourseViewModel::class.java)

I had used requireActivity() which will scope my fragment's viewmodel to the activity lifecycle, so the RecyclerView will update correctly when the app (and therefore activity) first starts but does not update again when navigating forth and back between fragments.

So to solve this issue i replaced requireActivity() with this which means the viewmodel is scoped to the fragment it is in only and therefore the RecyclerView will update whenever the user enters and re-enters the fragment

viewModel = ViewModelProvider(this).get(CourseViewModel::class.java)

Upvotes: 0

user13461991
user13461991

Reputation:

It is most likely that is issue is cause because the observer is not getting updated. You could use Fragment's view lifecycle through getViewLifeCycleOwner() or getViewLifeCycleLiveData() so that LiveData will remove observers every single time the fragment is destroyed.

Upvotes: 1

M.ekici
M.ekici

Reputation: 763

ViewModelProvider(requireActivity()) cause the retrieve the previous view model because the view model store keeps reference to previously created view model then serve it if you want again. You should give the fragment as a view model store owner rather than activity. Additionally I think your repository design is not correct if you don't need same course list in another place, you should call the method with corresponding argument whenever the course list fragment is created so you retrieve the appropriate data from server.

Upvotes: 0

Related Questions