I am trying to fetch data from the server using retrofit get request, in the ViewModel, I initiate the request, OnResponse method of the request shows the data successfully being retrieved from the server but the observer in the fragment doesn't get updated. I am sharing the code below.
RETROFIT API CALL
fun getGenres(application: Context,callback:(List<Genre>)->Unit){
val baseURL = "https://listen-api.listennotes.com/"
var genreList: MutableLiveData<List<Genre>> =MutableLiveData()
val logging = HttpLoggingInterceptor()
// set your desired log level
logging.level = HttpLoggingInterceptor.Level.BODY
val httpClient = OkHttpClient.Builder()
// add your other interceptors …
// add logging as last interceptor
httpClient.addInterceptor(logging)
val gson = GsonBuilder()
.setLenient()
.create()
val retrofit = Retrofit.Builder()
.baseUrl(baseURL)
.addConverterFactory(GsonConverterFactory.create(gson))
.client(httpClient.build())
.build()
val api: ApiFunctions = retrofit.create(ApiFunctions::class.java)
val call: Call<PodcastGetCategoryResponse> = api.getGenres()
call.enqueue(object : Callback<PodcastGetCategoryResponse> {
override fun onFailure(call: Call<PodcastGetCategoryResponse>, t: Throwable) {
Toast.makeText(application, "failure" + t.stackTrace, Toast.LENGTH_SHORT).show()
}
override fun onResponse(
call: Call<PodcastGetCategoryResponse>,
response: Response<PodcastGetCategoryResponse>
) {
response.body().apply {
callback(this?.genres!!) }
Toast.makeText(application, "success: ${response.body()?.genres?.size}", Toast.LENGTH_SHORT).show()
}
})
return genreList;
}
This runs successfully and retrieves a list of Genres from the server.
VIEWMODEL CLASS
class CategoryViewModel(application: Application) : AndroidViewModel(application) {
private val rootJob = SupervisorJob()
private val coroutineContext: CoroutineContext
get() = Dispatchers.Main + rootJob
var listOfCategory: MutableLiveData<List<Genre>> = MutableLiveData()
init {
getistOfCategory()
}
// this method is responsible for retrieving data from the result of the API call method using a coroutine.
fun getistOfCategory() {
CoroutineScope(coroutineContext).launch() {
ApiFunctions.getGenres(getApplication<Application>(), { l -> listOfCategory.value = l } )
listOfCategory.postValue(listOfCategory.value)
}
}
I assume that issue lies in this class but I can't figure it out.
**FRAGMENT Class **
class MainFragment : Fragment() {
private lateinit var viewModel: CategoryViewModel
private lateinit var binding: FragmentMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProviders.of(this).get(CategoryViewModel::class.java)
viewModel.getistOfCategory()
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding =DataBindingUtil.inflate(inflater, R.layout.fragment_main, container, false)
var myView: View = binding.root
viewModel.listOfCategory.observe(this,
Observer { list ->
Toast.makeText(
context,
"Data Changed" + list?.size,
Toast.LENGTH_LONG
).show()
})
return myView
}
What I want is that when new values are retrieved from the server it should be saved in LiveData in ViewModel class and the fragment should successfully observe them.
according to your current implementation the value of listOfCategory
keeps changing with each update. this means when your list is updated the observers are not being notified cause they're observing another LiveData object (which was assigned to your reference before the update occurred)
You need to make sure that you instantiate the liveData object only once and update its value whenever you get an update.
Your code should look something like this
fun getGenres(application: Context, callback:(List<Genre>)->Unit){
...
response.body().apply {
callback(this?.genres!!)
}
...
}
fun getistOfCategory(): {
CoroutineScope(coroutineContext).launch() {
ApiFunctions.getGenres(getApplication<Application>(), l -> listOfCategory.value = l)
}
}