I have the following implementation of a Retrofit Client:
private val retrofitClient: Retrofit.Builder by lazy {
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.HEADERS
val okHttpClient = OkHttpClient.Builder().addNetworkInterceptor(interceptor)
.cookieJar(SessionCookieJar())
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
Retrofit.Builder()
.baseUrl(URLWebConstants.BASE_URL)
.client(okHttpClient.build())
.addConverterFactory(GsonConverterFactory.create())
}
val postYotiApiService: PostYotiAPIService by lazy {
retrofitClient.build().create(PostYotiAPIService::class.java)
}
interface PostYotiAPIService {
@POST("*url*")
fun postYoti(): Call<PostYotiResponse>
}
Then I have a MainActivityRepository class with the call contents:
class MainActivityRepository() {
val client = RetrofitClient
var postYotiLiveData = MutableLiveData<YotiData?>()
fun postYoti(): MutableLiveData<YotiData?> {
val myCall = client.postYotiApiService.postYoti()
myCall.enqueue(object : Callback<PostYotiResponse> {
override fun onFailure(call: Call<PostYotiResponse>, t: Throwable) {
Log.d("Retrofit", "Something went wrong", t)
}
override fun onResponse(
call: Call<PostYotiResponse>,
response: Response<PostYotiResponse>
) {
if (response.isSuccessful) {
postYotiLiveData.postValue(response.body()!!.data)
} else {
postYotiLiveData.postValue(null)
}
}
})
return postYotiLiveData
}
Then in my Fragment's ViewModel I call the function like this:
val repository = MainActivityRepository()
fun postYoti(): MutableLiveData<YotiData?> {
return repository.postYoti()
}
And finally in my Fragment I make the call like this:
btnYoti.setOnClickListener {
viewModel.postYoti().observe(viewLifecycleOwner, {
println("Hello World")
}
})
}
The first time I make the call, everything works fine and I have no issues, Hello world is printed once. The second time I make the call Hello world gets printed twice, for a total of 3 times. The third time Hello World is printed 3 times and when I put a breakpoint I can see println gets hit 3 times. The call needs to happen only once per click.
You're observing the livedata in onClickListener! It means everytime you click on that button, a new observer (with viewLifecycleOwner) will attach to the liveData and keeps being active as long as the fragment's view is running. So every click will add another observer which will never be removed, unless the view is destroyed. Use observe when there are no actions (e.g. you're observing a live data in onViewCreated which will always get notified of any changes), on actions (like clicks) simply get the value of LiveData and do something with it. Like this
override fun onViewCreated(){
//...
btnYoti.setOnClickListener {
viewModel.postYoti()
}
//Add a getter to your VM to prevent direct access to rep
viewModel.repository.postYotiLiveData.observe(viewLifecycleOwner, {
println("Hello World")
}
}
}
On the other hand, you can use a LiveEvent to prevent redundant prints caused by resuming the fragment