Kotlin : handle timeout connection okhttp - kotlin

i am beginner in Kotlin . i try to build app using the OkHttp for make web requests to a rapidapi API downloading files when i click on button . i am already added setting connect Timeout , but i want to handle connect Timeout connection, when connect Timeout i want show to user a dialog to try again or cancel .
private fun StartDwonload() {
val url = editText.text.toString()
val client = OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build()
val requests = Request.Builder()
.url("xxxxxxx?igurl="+url)
.get()
.addHeader("x-rapidapi-host", "xxxxx")
.addHeader("x-rapidapi-key", "xxxx")
.build()
client.newCall(requests).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {}
override fun onResponse(call: Call, response: Response){
val responseData = response.body()?.string()
runOnUiThread{
try {
var json = JSONObject(responseData)
println("Request Successful!!")
println(json)
val responseObject = json.get("downloadurl").toString()
val request = DownloadManager.Request(Uri.parse((responseObject)))
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI or DownloadManager.Request.NETWORK_MOBILE)
request.setTitle("Download")
request.setDescription("Dwonloading ...")
request.allowScanningByMediaScanner()
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS,"${System.currentTimeMillis()}.mp4")
print(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
val manager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
manager.enqueue((request))
progressDialog.dismiss()
} catch (e: JSONException) {
e.printStackTrace()
}
}
}
})
}
any idea please ?

On your OnFailure() method you can check whether your error is SocketTimeoutException like this
override fun onFailure(call: Call, e: IOException) {
if(e is SocketTimeoutException){
//do your work
}
}

Related

What is the best way to get data from an API using retrofit when something needs to wait on that data?

I have a retrofit API call but am having trouble getting the data out of it:
This is in a class file that's not a viewModel or Fragment. It's called from the apps main activity view model. I need to be able to get the data from the API and wait for some processing to be done on it before returning the value back the view model. Newer to kotlin and struggling with all the watchers and async functions. The result of this an empty string is the app crashes, because it's trying to access data before it has a value.
From class getData which is not a fragment
private lateinit var data: Data
fun sync(refresh: Boolean = false): List<String> {
var info = emptyList<String>
try {
getData(::processData, ::onFailure)
info = data.info
} catch(e: Throwable){
throw Exception("failed to get data")
}
}
}
return info
}
fun getData(
onSuccess: KFunction1<ApiResponse>?, Unit>,
onFailed: KFunction1<Throwable, Unit>
) {
val client = ApiClient().create(Service.RequestData)
val request = client.getData()
request.enqueue(object : Callback<ApiResponse> {
override fun onResponse(
call: Call<ApiResponse>,
response: Response<ApiResponse>
) {
onSuccess(response.body())
}
override fun onFailure(call: Call<RegistryResponse<GlobalLanguagePack>>, t: Throwable) {
onFailed(Exception("failed to get data"))
}
})
}
private fun processData(body: ApiResponse?) {
requireNotNull(body)
data = body.data
}
```
From appViewModel.kt:
```
fun setUpStuff(context: Context, resources: AppResources) = viewModelScope.launch {
val stuff = try {
getData.sync()
} catch (e: Exception) {
return#launch
}
if (stuff.isEmpty()) return#launch
}
```

How to use Coroutines with Retrofit2?

I am fetching data from an api to display in a RecycerView using Retrofit2 and Kotlin Coroutines. I have just started learning Retrofit and Coroutines and at the moment the data is not displaying and I'm not sure how to solve it! I think the issue may be with the Coroutines code. Please can someone give me some help?
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var recyclerView: RecyclerView = findViewById(R.id.rockets_list)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = RecyclerAdapter(List<RocketData>())
CoroutineScope(IO).launch {
val response = ApiInterface.getApi().getRockets()
Log.i("code",response.toString())
withContext(Dispatchers.Main) {
try {
if (response.isSuccessful) {
recyclerView.adapter
} else {
Toast.makeText(this#MainActivity, "Error ${response.code()}", Toast.LENGTH_SHORT).show()
}
} catch (e: HttpException) {
Toast.makeText(this#MainActivity, "Exception ${e.message}", Toast.LENGTH_SHORT).show()
}
}
}
}
}
interface ApiInterface {
#GET("rockets")
suspend fun getRockets(): Response<List<RocketData>>
companion object {
fun getApi(): ApiInterface = Retrofit.Builder()
.baseUrl("https://api.spacexdata.com/v3/")
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(ApiInterface::class.java)
}
}
What are you doing after response.isSuccessful check??
try setting response in Adapter then notifyDataSetChanged
recyclerView.adapter.items = response
recyclerView.adapter.notifyDataSetChanged()
your Retrofit and Coroutine implementation works fine,
you just didn't update your adapter list after successful response check
if (response.isSuccessful) {
recyclerView.adapter.list = response.body
recyclerView.adapter.notifyDataSetChanged()
}
also you can't display Toast from within a Coroutine, wrap it inside runOnUiThread{}

What is the parameter for request and response body okhttp in kotlin

I start to code the app in kotlin with okhttp3. I get response body but how I can get the info that I need? For example, I use Google example. I just want to get the "name:". How I can tell my request that I what get only "name:"? Can you help with some code example or some source with instruction and description about OkHTTP? I read official documentation but didn't find something or just didn't understand.
fun run(url: String){
val request = Request.Builder().url(url).build()
//client.authenticator()
val client = OkHttpClient()
.newBuilder()
.addInterceptor { chain ->
val originalRequest = chain.request()
val builder = originalRequest
.newBuilder()
val name = request.header("name")
//.header("Authorization",
// Credentials.basic("login", "password"))
val newRequest = builder.build()
chain.proceed(newRequest)
}.build()
client.newCall(request).enqueue(object : Callback{
override fun onFailure(call: Call, e: IOException) {
toast("fail")
e.printStackTrace()
}
override fun onResponse(call: Call, response: Response) {
textView3.setText(response.body()?.string())
}
})
I tried to use .header("name") but it was red and I think I make some mistake.
Thank for every suggestions
Here is a way to do it with Jackson ObjectMapper
as per your example, let's say you receive the following content in the response:
{
"login": "defunkt",
"id": 2,
"name": "Chris Wanstrath",
"company": null,
"blog": "http://chriswanstrath.com/"
}
but you are only interested to in the name field, therefore you define a User class:
public class User { private String name; }
and then using the ObjectMapper configured to ignore the missing properties:
// content = response.body().string();
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
try {
User user = mapper.readValue(content, User.class);
System.out.println(user.getName()); // Chris Wanstrath
} catch (JsonProcessingException e) {
e.printStackTrace();
}

RxJava : How to maintain Observable alive even after getting error in onError() or ReSubscribe the same Observable

Actually I have created a RxSearch type configuration. In which I have attached an Edittext textChangeListener with the PublishSubject. Using the events to send the characters to the Observable which is being used as input for the retrofit API call.
Problem
Only issue I m facing is sometime I got the error from API "unexpected end of stream" inside onError() callback of observable. Once I got the error, Observable stops working.
Observable shuts down, not able to get the characters from PublishSubject's onNext().
Look at RxSearchObservable
class RxSearchObservable {
companion object {
fun fromView(editText: EditText): Observable<String> {
val subject = PublishSubject.create<String>()
editText.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
//subject.onComplete()
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
//subject.onNext(s.toString())
}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
if (s.isNotEmpty()) subject.onNext(s.toString())
}
})
return subject
}
}
}
How I subscribing and Making an Retrofit API call in side SwitchMap.
RxSearchObservable.fromView(edtToolSearch)
.debounce(700, TimeUnit.MILLISECONDS)
.distinctUntilChanged()
.retryWhen { t -> t.delay(3, TimeUnit.SECONDS) }
.switchMap { searchTerm ->
runOnUiThread { progressBar.visibility = View.VISIBLE }
apiManager.getSearchUnits(searchTerm)
}
.onErrorResumeNext(Observable.empty())
.subscribe({ response ->
Log.i("Called subscribe", ":::::::::::+++++++++++++++ GONE")
progressBar.visibility = View.GONE
if (response.isSuccessful) {
val units = response.body()
val searchedDatasets = units?.dataset
if (searchedDatasets?.size!! > 0) {
val searchAdapter = SearchAdapter(this#MapActivity, searchedDatasets, false)
listSearch.visibility = View.VISIBLE
listSearch.adapter = searchAdapter
} else {
toast("No items found !!!")
}
} else {
apiError = ErrorUtils.parseError(response)
toast(apiError.msg)
}
}, { t: Throwable? ->
progressBar.visibility = View.GONE
toast(t?.message.toString())
}))
Any Idea, Help, Suggestion will be appreciated. Thanks in advance.
A stream which errors is terminated. You can retry() the subscription, but this should be done conditionally only. Maybe with timeout, maybe only a few times, maybe on certain errors only.
In your case you should consider handling the error of the API call within the switchMap. Like this the error doesn't reach the main stream.
.switchMap { searchTerm ->
runOnUiThread { progressBar.visibility = View.VISIBLE }
apiManager.getSearchUnits(searchTerm)
.onErrorResumeNext(Observable.empty())
}

How to make a sync call using RxJava

I need to make a sync call to reauthenticate the user and get a new token, but I haven't found a way that works. The code below blocks the thread and it is never unblocked, ie. I have an infinite loop
class ApolloAuthenticator(private val authenticated: Boolean) : Authenticator {
#Throws(IOException::class)
override fun authenticate(route: Route, response: Response): Request? {
// Refresh your access_token using a synchronous api request
if (response.request().header(HEADER_KEY_APOLLO_AUTHORIZATION) != null) {
return null //if you've tried to authorize and failed, give up
}
synchronized(this) {
refreshTokenSync() // This is blocked and never unblocked
val newToken = getApolloTokenFromSharedPreference()
return response.request().newBuilder()
.header(HEADER_KEY_APOLLO_AUTHORIZATION, newToken)
.build()
}
private fun refreshTokenSync(): EmptyResult {
//Refresh token, synchronously
val repository = Injection.provideSignInRepository()
return repository
.signInGraphQL()
.toBlocking()
.first()
}
fun signInGraphQL() : Observable<EmptyResult> =
sharedPreferencesDataSource.identifier
.flatMap { result -> graphqlAuthenticationDataSource.getAuth(result) }
.flatMap { result -> sharedPreferencesDataSource.saveApolloToken(result) }
.onErrorReturn { EmptyResult() }
}
---------- Use of it
val apollAuthenticator = ApolloAuthenticator(authenticated)
val okHttpBuilder =
OkHttpClient.Builder()
.authenticator(apollAuthenticator)
I haven't found a way to make a sync call using RxJava, but I can make it by using kotlin coutorine runBlocking, which will block the thread until the request is finished:
synchronized(this) {
runBlocking {
val subscription = ApolloReauthenticator.signInGraphQl() // await until it's finished
subscription.unsubscribe()
}
}
fun signInGraphQl(): Subscription {
return repository.refreshToken()
.subscribe(
{ Observable.just(EmptyResult()) },
{ Observable.just(EmptyResult()) }
)
}