My understanding of Call.Enqueue is that the OnFailure method is supposed to handle all failed responses including OnSocketTimeOutException, however that doesn't seem to be the case for me.
The code below helps to call data from the NewsApi services as well as adding an interceptor to track requests/response status. I added .connectTimeout(10,TimeUnit.MILLISECONDS).readTimeout(10,TimeUnit.MILLISECONDS) to test how my app will react in an event of a OnSocketTimeOutException
object RetrofitHelper {
private const val BASE_URL = "https://newsapi.org/v2/"
private fun httpclient(): OkHttpClient {
val builder = OkHttpClient.Builder().connectTimeout(10,TimeUnit.MILLISECONDS).readTimeout(10,TimeUnit.MILLISECONDS)
builder.addInterceptor(HttpLoggingInterceptor().apply {
setLevel(HttpLoggingInterceptor.Level.BODY)
builder.addInterceptor(
ChuckerInterceptor.Builder(application_class.context)
.collector(ChuckerCollector(application_class.context))
.maxContentLength(250000L)
.redactHeaders(emptySet())
.alwaysReadResponseBody(false)
.build()
)
})
return builder.build()
}
fun getInstance(): Retrofit {
return Retrofit.Builder()
.client(httpclient())
.baseUrl(BASE_URL)
.addConverterFactory(MoshiConverterFactory.create())
.build()
}
object NewsApiCall {
val api by lazy {
RetrofitHelper.getInstance().create(NewsApi::class.java)
}
}
}
The code below belongs to my Repository where the logic of determining whether a response is a success, error or failure. The api key provided below is not the real API key, I edited it. I suspect that the error has something to do with the OnFailure method.
fun getNewsCall(country: String, Category: String?): MutableLiveData<MutableList<Article>> {
val call = RetrofitHelper.NewsApiCall.api.getNews(
country,
Category,
"0169333ed3074dccbacd369ae6a3c4"
)
var Newlist = MutableLiveData<MutableList<Article>>()
call.enqueue(object : Callback<NewsDataFromJson> {
override fun onResponse(
call: Call<NewsDataFromJson>,
response: Response<NewsDataFromJson>
) {
if (response.isSuccessful) {
val body = response.body()
if (body != null) {
Newlist.value = body.articles
}
} else {
val jsonObj: JSONObject?
jsonObj = response.errorBody()?.string().let { JSONObject(it) }
if (jsonObj != null) {
MainActivity.apiRequestError = true
MainActivity.errorMessage = jsonObj.getString("message")
Newlist.value = mutableListOf<Article>()//
}
}
}
override fun onFailure(call: Call<NewsDataFromJson>, t: Throwable) {
MainActivity.apiRequestError = true
MainActivity.errorMessage = t.localizedMessage as String
Log.d("err_msg", "msg" + t.localizedMessage)
}
})
return Newlist
}
The final part is the MainActivity, this is where I display the responses, if it is a success It will show the news articles, if it is an error or a failure, an error message is supposed to show up
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
lifecycleScope.launch(Dispatchers.Main) {
requestNews(GENERAL, generalNews, "us")
requestNews(TECHNOLOGY, TechNews, "us")
requestNews(HEALTH, healthNews, "us")
requestNews(SPORTS, SportsNews, "us")
requestNews(ENTERTAINMENT, EntertainmentNews, "us")
requestNews(SCIENCE, ScienceNews, "us")
requestNews(BUSINESS, BusinessNews, "us")
}
}
suspend private fun requestNews(newsCategory: String, newsData: MutableList<Article>,country:String) {
viewModel.getNews(category = newsCategory, Country = country)?.observe(this) {
newsData.addAll(it)
totalRequestCount += 1
lifecycleScope.launch(Dispatchers.Main) {
if (ScienceNews.isNotEmpty() && BusinessNews.isNotEmpty() && EntertainmentNews.isNotEmpty() && generalNews.isNotEmpty() && healthNews.isNotEmpty() && SportsNews.isNotEmpty() && TechNews.isNotEmpty()) {
if (apiRequestError == false) {
ProgresBar.visibility = View.GONE
FragmentContainer.visibility = View.VISIBLE
} else if (apiRequestError == true) {
ProgresBar.visibility = View.GONE
FragmentContainer.visibility = View.GONE
val showError: TextView = findViewById(R.id.display_error)
showError.text = errorMessage
showError.visibility = View.VISIBLE
}
} else if (apiRequestError == true) {
ProgresBar.visibility = View.GONE
FragmentContainer.visibility = View.GONE
val showError: TextView = findViewById(R.id.display_error)
showError.text = errorMessage
showError.visibility = View.VISIBLE
}
}
}
}
companion object{
//var generalNews changed from ArrayList<NewsModel>
var ScienceNews: MutableList<Article> = mutableListOf()
var EntertainmentNews: MutableList<Article> = mutableListOf()
var SportsNews: MutableList<Article> = mutableListOf()
var BusinessNews: MutableList<Article> = mutableListOf()
var healthNews: MutableList<Article> = mutableListOf()
var generalNews: MutableList<Article> = mutableListOf()
var TechNews: MutableList<Article> = mutableListOf()
var apiRequestError = false
var errorMessage = "error"
}
}
When I run the app this is what I see, the loading screen is not disappearing which is weird, which leads me to believe that the error may lie with the OnFailure method in the Repository.

