I need to send the first POST request to the server, insert the received response into the second POST request and send it back to the server. How can I implement this with RxJava? At the moment, everything works, but the second question does not have time to get an answer from the first request and the imageMediaId field is sent empty.
My function:
fun uploadNewImageAndContact(toPath: String?, newContactApi: ContactsApi) {
val file = File(toPath)
val fileReqBody: RequestBody = file.asRequestBody("image/*".toMediaTypeOrNull())
val part: MultipartBody.Part =
MultipartBody.Part.createFormData("upload", file.name, fileReqBody)
// First Request
imagesService.postImage(part).enqueue(object : Callback<ResponseBody> {
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
}
override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
if (response.code() == 201) {
// Get Image response Id
val gson = Gson()
val imageResponse =
gson.fromJson(response.body()?.string(), ImageResponse::class.java)
imageMediaId = imageResponse.ids[0]
// Second Request
contactsService.postNewContact(newContactApi)
.enqueue(object : Callback<ContactsApi> {
override fun onFailure(call: Call<ContactsApi>, t: Throwable) {
}
override fun onResponse(call: Call<ContactsApi>, response: Response<ContactsApi>) {
}
})
}
}
})
}
My ViewModel function:
val newContactsApi = ContactsApi(id = "", firstName = mFirstName, lastName = mLastName, phone = mPhone,
email = mEmail, notes = mNotes, images = "https://mydb.site.io/media/${apiRepository.imageMediaId}")
apiRepository.uploadNewImageAndContact(toPath, newContactsApi)
Related
This is my code, I'm getting success code 200 but I need to wait until the final response is fetched.
val retroService =
RetroInstance().getRetroInstance("$url/")
.create(ApiService::class.java)
val call = retroService.getResponse(
"Token *****"
)
call.enqueue(object : Callback<MyResponse> {
override fun onResponse(
call: Call<MyResponse>,
response: Response<MyResponse>
) {
if (response.isSuccessful) {
//After the first response it stopped. How to wait until complete response is fetched?
} else {
response.errorBody()
}
}
override fun onFailure(call: Call<MyResponse>, t: Throwable) {
}
})
}
I have to wait until the final response is fetched. I tried it in postman the final output is displayed.
I'm trying to do a POST request using Retrofit but I'm unable to make it work. It does work on Postman. I specified the header "Content-Type: application/json" and set my "email" and "password" parameters in the body and it works well.
But it doesn't on Android. Here are my codes :
private fun login() {
val user = User("test#gmail.com", "dsea2EcFI32\\\"af'xn")
this.service.login(user).enqueue(object : Callback<LoginResponse> {
override fun onResponse(call: Call<LoginResponse>, response: Response<LoginResponse>) {
if (response.code() == 200) {
// TODO
}
}
override fun onFailure(call: Call<LoginResponse>, t: Throwable) {
// TODO
println(t.message)
}
})
}
The request :
#Headers("Content-Type: application/json")
#POST("/api/authentication/login")
fun login(#Body body: User): Call<LoginResponse>
User model
data class User(val email: String, val password: String)
LoginResponse :
class LoginResponse {
#SerializedName("user")
val user : UserResponse? = null
}
class UserResponse {
#SerializedName("id") val still : String = null
#SerializedName("firstName") val running : String = null
#SerializedName("lastName") val bicycle : String = null
#SerializedName("email") val walking : String = null
#SerializedName("token") val vehicle : String = null
}
In case the auth is a failure, the server sends me back an HTML page so the only error I have is
Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $
I already set it to true and it keeps saying me that the GSON parsed object isn't a JSON object but I know there's an Android code here
Can someone helps me finding it ?
PS : I even tried to send the body as a JSON object but same error
PS2 : might this be due to the password even If I added enough backspace to accept the special characters ? the real string is dsea2EcFI32"af'xn
EDIT :
As asked, here is my retrofit builder with the HTTPInterceptor
val client = OkHttpClient()
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY
client.interceptors().add(interceptor)
val retrofit = Retrofit.Builder()
.baseUrl(BuildConfig.API_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
this.service = retrofit.create(LoginResponse::class.java)
I found the solution.
The issue was the password because it had backslashes and quotes inside of it.
Kotlin was doing a wrong parsing.
Convert your fun login object like below one.
#Headers("Content-Type: application/json")
#POST("/api/authentication/login")
fun login(#Body requestBody: RequestBody): Call<LoginResponse>
then create a fun like this
fun makeGSONRequestBody(jsonObject: Any?): RequestBody {
return RequestBody.create(MediaType.parse("multipart/form-data"), Gson().toJson(jsonObject))
}
you need to pass your User object like below
private fun login() {
val user = User("test#gmail.com", "dsea2EcFI32\\\"af'xn")
this.service.login(makeGSONRequestBody(user)).enqueue(object : Callback<LoginResponse> {
override fun onResponse(call: Call<LoginResponse>, response: Response<LoginResponse>) {
if (response.code() == 200) {
// TODO
}
}
override fun onFailure(call: Call<LoginResponse>, t: Throwable) {
// TODO
println(t.message)
}
})
}
I am new android developer, how can I get result form this snippet, what way does exist, because it doesn't return anything, because of I'm adding element inside onResponse, but using only kotlin module:
private fun foo(list: ArrayList<CurrencyModel> = ArrayList()): ArrayList<CurrencyModel> {
val request = Request.Builder().url(BASE_URL_YESTERDAY).build()
val client = OkHttpClient()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
}
override fun onResponse(call: Call, response: Response) {
val body = response.body?.string()
val data = Gson().fromJson(body, Currencies::class.java)
list.add(CurrencyModel("USD", data.rates.USD, 0.0))
list.add(CurrencyModel("SEK", data.rates.SEK, 0.0))
list.add(CurrencyModel("EUR", data.rates.EUR, 0.0))
}
})
return list
}
}
You can give your function a callback parameter that's called when the response is receieved. And you shouldn't have an input list in this case, because if you have multiple sources modifying it at unpredictable future moments, it will be difficult to track.
The function can look like this:
private fun getCurrencyModels(callback: (ArrayList<CurrencyModel>) {
val request = Request.Builder().url(BASE_URL_YESTERDAY).build()
val client = OkHttpClient()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
}
override fun onResponse(call: Call, response: Response) {
val body = response.body?.string()
val data = Gson().fromJson(body, Currencies::class.java)
val list = arrayListOf(
CurrencyModel("USD", data.rates.USD, 0.0)),
CurrencyModel("SEK", data.rates.SEK, 0.0)),
CurrencyModel("EUR", data.rates.EUR, 0.0))
)
callback(list)
}
})
}
And then to use it:
getCurrencyModels { modelsList ->
// do something with modelsList when it arrives
}
An alternative is to use coroutines, which allow you to do asynchronous actions without callbacks. Someone has already created a library that lets you use OkHttp requests in coroutines here. You could write your function as a suspend function like this:
private suspend fun getCurrencyModels(): ArrayList<CurrencyModel> {
val request = Request.Builder().url(BASE_URL_YESTERDAY).build()
val client = OkHttpClient()
val response = client.newCall(request).await()
val body = response.body?.string()
val data = Gson().fromJson(body, Currencies::class.java)
return arrayListOf(
CurrencyModel("USD", data.rates.USD, 0.0)),
CurrencyModel("SEK", data.rates.SEK, 0.0)),
CurrencyModel("EUR", data.rates.EUR, 0.0))
)
}
and then use it like this:
lifecycleScope.launch {
try {
val currencyModels = getCurrencyModels()
// do something with currencyModels
} catch (e: IOException) {
// request failed
}
}
Coroutines make it really easy to avoid leaking memory when your asynchronous calls outlive your Activity or Fragment. In this case, if your Activity closes while the request is going, it will be cancelled automatically and references to your Activity will be removed so the garbage collector can release your Activity.
The onResponse() function is only called when the HTTP response is successfully returned by the remote server. Since this response doesn't happen immediately, you can't use the result in your code immediately. What you could do is use a ViewModel and LiveData variable and add the values to that variable in onResponse(). Something like:
private fun foo(list: ArrayList<CurrencyModel> = ArrayList()) {
val request = Request.Builder().url(BASE_URL_YESTERDAY).build()
val client = OkHttpClient()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
}
override fun onResponse(call: Call, response: Response) {
val body = response.body?.string()
val data = Gson().fromJson(body, Currencies::class.java)
val list: ArrayList<CurrencyModel> = arrayListOf()
list.add(CurrencyModel("USD", data.rates.USD, 0.0))
list.add(CurrencyModel("SEK", data.rates.SEK, 0.0))
list.add(CurrencyModel("EUR", data.rates.EUR, 0.0))
viewModel.list.postValue(list)
}
})
}
I tried to use retrofit to get the response data of web api, but the result of response data seems not sync as the same.
fun fetchData(): LiveData<String> {
val auth = Credentials.basic(name, pass)
val request: Call<JsonElement> = webApi.fetchData()
val response: MutableLiveData<String> = MutableLiveData()
request.enqueue(object : Callback<JsonElement> {
override fun onFailure(call: Call<JsonElement>, t: Throwable) {
Log.e(TAG, "Failed to fetch token", t)
}
override fun onResponse(call: Call<JsonElement>, response: Response<JsonElement>) {
response.value = response.body()
Log.d(TAG, "response: ${response.value}") // I can get the result of response
}
})
return response // But the function return with the null
}
You might need handler.
The enqueue method doesn´t wait to the response so is normal the null result in your return response.
To solve this, you doesn´t need to return nothing, only put your livedata in the scope class and update the value:
class YourClass {
private var responseMutableLiveData: MutableLiveData<String> = MutableLiveData()
val responseLiveData: LiveData<String>
get() = responseMutableLiveData
fun fetchData() {
webApi.fetchData().enqueue(object : Callback<JsonElement> {
override fun onFailure(call: Call<JsonElement>, t: Throwable) {
Log.e(TAG, "Failed to fetch token", t)
}
override fun onResponse(call: Call<JsonElement>, response: Response<JsonElement>) {
responseMutableLiveData.postValue(response.body())
Log.d(TAG, "response: ${response.value}")
}
})
}
}
The livedata is observed and, when the value changes, then the other class reacts to it.
when I use retrofit2 with no coroutine, the result is null. but when using that with coroutine, the result is right. I think it's the problem of syncronization. but I found something strange
using mutablelivedata, the result is right.
retrofit2 with coroutine
override suspend fun getRetrofit(id : Int): DetailEntity {
withContext(ioDispatcher){
val request = taskNetworkSource.searchItem(id)
val response = request.await()
if(response.body !=null){
Log.d("TAG",""+response.toString())
data = response
}
}
return data
}
good result
D/TAG: DetailEntity(body=DetatilItem(oily_score=6, full_size_image=url, price=54840, sensitive_score=76, description=description, id=5, dry_score=79, title=title), statusCode=200)
retrofit2 with no coroutine
override suspend fun getRetrofit(id : Int): DetailEntity {
taskNetworkSource.searchItem(id).enqueue(object: Callback<DetailEntity> {
override fun onFailure(call: Call<DetailEntity>, t: Throwable) {
}
override fun onResponse(call: Call<DetailEntity>, response: Response<DetailEntity>){
if(response.body()!=null) {
Log.d("TAG",response.toString())
data = response.body()!!
}
}
})
return data
}
bad result
D/TAG: Response{protocol=h2, code=200, message=, url=https://6uqljnm1pb.execute-api.ap-northeast-2.amazonaws.com/prod/products/5}
strange result with mutablelivedata(another project code)
lateinit var dataSet : DetailModel
var data = MutableLiveData<DetailModel>()
fun getDetailRetrofit(id:Int) : MutableLiveData<DetailModel>{
Retrofit2Service.getService().requestIndexItem(id).enqueue(object:
Callback<DetailResponse> {
override fun onFailure(call: Call<DetailResponse>, t: Throwable) {
}
override fun onResponse(call: Call<DetailResponse>, response: Response<DetailResponse>) {
if(response.body()!=null) {
var res = response.body()!!.body
dataSet = DetailModel( res.get(0).discount_cost,
res.get(0).cost,
res.get(0).seller,
res.get(0).description+"\n\n\n",
res.get(0).discount_rate,
res.get(0).id,
res.get(0).thumbnail_720,
res.get(0).thumbnail_list_320,
res.get(0).title
)
data.value = dataSet
}
}
})
return data
}
and this another project code result is right. comparing this code to retrofit2 with no coroutine code, the difference is only mutablelivedata or not. do I have to use asyncronouse library or livedata?
added
data class DetailEntity(val body: DetatilItem,
val statusCode: Int = 0)
data class DetatilItem(val oily_score: Int = 0,
val full_size_image: String = "",
val price: String = "",
val sensitive_score: Int = 0,
val description: String = "",
val id: Int = 0,
val dry_score: Int = 0,
val title: String = "")
retrofit with no coroutine it seem to be no problem.
But, respnose at your code written to log are the completely different object.
with coroutine, response is DetailEntity
with no coroutine, response is Response<DetailEntity>
if you want same log print, try as below
override fun onResponse(call: Call<DetailEntity>, response: Response<DetailEntity>){
if(response.body()!=null) {
Log.d("TAG",response.body()!!.toString())
data = response.body()!!
}
}
Reference
Retrofit - Response<T>