How do I releaze authorization function for android app? - android

I'm trying to releaze function for authorization my android app. I know there are many similar questions, but I'm interesting exectly to get function witch return "success" or "failure".
override fun authorization(login: String, password: String): String {
var result: String = "none"
val call: Call<UserAuthorizationDataModelRetrofit> =
dataBase.getWordApiRetrofit().userAuthorization(login, password)
call.enqueue(object : Callback<UserAuthorizationDataModelRetrofit> {
override fun onResponse(
call: Call<UserAuthorizationDataModelRetrofit>?,
response: Response<UserAuthorizationDataModelRetrofit>?
) {
val dataModel = response?.body()
result = dataModel?.result ?: "failure"
}
override fun onFailure(call: Call<UserAuthorizationDataModelRetrofit>?, t: Throwable?) {
result = "failure"
}
})
return result
}
I'm expecting function return "success" or "failure" acording to response of http request. But http request is asynchronous and every time I get "none". Can anybody help me?

The function .enqueue is asynchronous. You will always get a "none" because enqueue is always not finished when authorization returns.
You can do two things.
One is use enqueue and do all your work inside onResponse, open new Activity for the user, set your View or show a Toast, etc.
Another is use .execute. But you will have to call authorization in another Thread and do your work in there. Note that this is asynchronous and you should not do UI stuff here.

Related

why value inside retrofit response differ from value outside?

I saw many many stackoverflow about the same question but I still stuck in my circle.
I'm trying to return a user token from API. The response is success and I can LOG it. but I can't return it because the function will return before the enqueue completed (this is what I think)
Please see the code below:
fun login(username: String, password: String): Result<LoggedInUser> {
val client = OkHttpClient.Builder().build()
val retrofit = Retrofit.Builder()
.baseUrl("https:api/customers/")
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
val userApi= retrofit.create<UserApiKotlin>()
var pass: String ="SecYourPa$$"
val r= UserRequestModel("coding", pass)
var fakeUser = LoggedInUser(
java.util.UUID.randomUUID().toString(), "Jane Doe",
"gfgfgfgf", "trtrtr", "trtrtrtr", "abababababab"
)
var sss="1"
userApi.login(r).enqueue(object : Callback<LoggedInUser> {
override fun onResponse(
call: Call<LoggedInUser>, response: Response<LoggedInUser>
) {
fakeUser = LoggedInUser(
java.util.UUID.randomUUID().toString(), "Jane Doe",
"gfgfgfgf", "trtrtr", "trtrtrtr", "xcxcxcxc"
)
sss = "tttttt"
fakeUser = response.body()!!
Log.e("inside Retrofit", sss)
}
override fun onFailure(call: Call<LoggedInUser>, t: Throwable) {
Log.e("fauluer", "Unable to submit post to API.")
}
})
Log.e("outside retrofit", sss)
return Result.Success(fakeUser)
}
and this the output show that it will show and execute the function (and return the initial value) before api get the value from server
userApi.login(r).enqueue is an asynchronous call which will run on another thread. A network call can take severall milliseconds or seconds so you won't get the result immediately.
The code happens in this order:
You call userApi.login(r).enqueue -> Network call starts in parallel on another thread
You return Result.Success(fakeUser)
The network call finishes -> Code inside your callback in onResponse or onFailure is executed
As you see, you are returning a result before the network call even finishes. Your log shows the same.

How can Retrofit handle invalid responses from interceptor?

I've spent hours trying to figure this thing out, and I still can figure it out.
I'm trying to retrieve data from a website using JSON.
If the website is live and everything, it works, but if the website returns something else than the data, like a 403 error, or any other error, then it crashes. I tried to debug it, but I still don't understand what is going on here.
Here is my code:
I have a NetworkModule with an interceptor that is supposed to check is the response is valid or not, and from what I can tell it works, because my variable isDataRetrievable is false (the value by default):
val networkModule = module {
single {
val customGson =
GsonBuilder().registerTypeAdapter(Lesson::class.java, LessonDeserializer())
.create()
Retrofit.Builder()
.client(get())
.addConverterFactory(
GsonConverterFactory.create(customGson)
)
.baseUrl(BuildConfig.URL)
.build()
}
factory {
OkHttpClient.Builder()
.addInterceptor(Interceptor { chain ->
chain.withConnectTimeout(1,TimeUnit.SECONDS)
val request: Request = chain.request()
val response = chain.proceed(request)
if (response.isSuccessful){
networkStatus.isDataRetrievable = true
}
response
}).build()
}
factory {
get<Retrofit>().create(LessonApi::class.java)
}
}
Next, I have my API to get the data:
interface LessonApi {
#GET("/JSON/json_get_data.php")
suspend fun getLessons(): Call<Lesson>
}
Then, for some reason, I have a repository (I'm not the only one working on this code, I didn't do this part):
class LessonRepository(private val service: LessonApi) {
suspend fun getLessons() = service.getLessons()
}
Then, I have my splash screen view model, that is supposed to retrieve the data if possible:
if (networkStatus.isNetworkConnected && networkStatus.isWebsiteReachable) {
var tmp = repository.getLessons()
tmp.enqueue(object : Callback<Lesson> {
override fun onFailure(call: Call<Lesson>, t: Throwable) {
Log.d("DataFailure",t.message.toString())
nextScreenLiveData.postValue(false)
}
override fun onResponse(call: Call<Lesson>, response: Response<Lesson>) {
Log.d("DataFailure","Test")
}
})
}else{
nextScreenLiveData.postValue(false)
}
The problem is that when the program get to the line repository.getLessons(), it crashes with the error:
retrofit2.HttpException: HTTP 403
at retrofit2.KotlinExtensions$await$2$2.onResponse(KotlinExtensions.kt:49)
at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:129)
at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
So onFailure or onResponse are never called. I tried to run the debugger, to step in, but I cannot figure it out when it fails.
I thought it was because it was trying to deserialize invalid data, but I put breakpoints everywhere in my deserializer, and it never hits the breakpoints.
I'm not a professional android developer, but I'm very confused here.
What I'd like to do is that if the request is unsuccessful, just discard the response (do not deserialize it), and display a message or exit.
Please help, it's so frustrating. I'm not sure how to intercept errors or what to do if Interceptors get an unsuccessful request (for now I just set a variable but it's unused).
Thanks.
Edit: What I'm trying to do, is to retrieve data from a webserver. If it cannot (for any reason), I don't want the gson to parse data (because it will probably be garbage and will not correspond to my deserializer). However, I feel like this okhttp / retrofit is a pipeline, where okhttp get the response from the webserver and pass it to a gson converter. What I want to do is intercept this response, and if it's not successful, to NOT pass it to gson, set a variable, so that the rest of my application knows what to do. But the thing is, for now, it just crash even before it gets to the callback in enqueue. The interceptor works just fine, except I'd like him to drop the response if it's not successful. Is it possible?
I tried something like that, and it worked to handle bad codes (>400), but I also wanted to handle malformed JSON data, so I added the onResponse and onFailure callbacks, but it never worked, because when I receive a malformed JSON, it also trigger an exception, and then go in the catch before it can go on the 'enqueue', so I'm not sure what this is used for.
try {
val lessons = repository.getLessons().enqueue(object : Callback<List<Lesson>> {
override fun onResponse(call: Call<List<Lesson>>, response: Response<List<Lesson>>) {
networkStatus.isDataRetrievable = response.isSuccessful
Log.d("Retrofit", "Successful response")
nextScreenLiveData.postValue(response.isSuccessful)
}
override fun onFailure(call: Call<List<Lesson>>, t: Throwable) {
Log.d("Retrofit", "Failure response")
nextScreenLiveData.postValue(false)
}
})
nextScreenLiveData.postValue(true)
} catch (e: Exception) {
nextScreenLiveData.postValue(false)
}
Anyway, just this code works for everything in the end:
try {
val lessons = repository.getLessons().filter {
it.lesson.contains("video")
}.filter {
DataUtils.isANumber(it.id)
}
lessonDao.insertLessons(lessons)
networkStatus.isDataRetrievable = true
} catch (e: Exception) {
networkStatus.isDataRetrievable = false
}
But in my API, I don't return callbacks, I directly return the objects, as such:
#GET("/JSON/json_get_dat.php")
suspend fun getLessons(): List<Lesson>
I don't know if this is the right way to do it, but it works. I hope this might help others.

Suspend Coroutine Hangs

Trying to get a deeper into coroutines. I have a suspendCancellableCoroutine that is supposed to fetch a network response. I can see in Charles that the network call is dispatched and returns successfully. However, my app just hangs on the network request line.
private suspend fun fetchVisualElementsFromServer(clubId: String): VisualElements {
return suspendCancellableCoroutine { cont ->
visualElementsService.fetchVisualElementsForClub(clubId)
.enqueue(object : Callback<ResultVisualElements> {
override fun onResponse(
call: Call<ResultVisualElements>,
response: Response<ResultVisualElements>
) {
if (response.isSuccessful) {
response.body()?.let {
if (it.result == RESULT_SUCCESS) {
saveVisualElementsResponseInSharedPreferences(it.visual_elements)
cont.resume (it.visual_elements)
} else {
cont.cancel() //edit
}
} ?: cont.cancel() //edit
} else {
cont.cancel(IOException("${response.code()}: ${response.errorBody()}"))
}
}
override fun onFailure(call: Call<ResultVisualElements>, t: Throwable) {
Timber.e(t, "visual elements fetch failed")
cont.cancel() // edit
}
})
}
}
This where it hangs:
VisualElementsService.kt
fun fetchVisualElementsForClub(clubId: String): Call<ResultVisualElements> {
return dataFetcherService.getVisualElementsForClub(clubId)
}
What am I missing here? I tried to make the fetchVisualElementsForClub() a suspend function, but that just makes the suspendCancellableCoroutine throw a Suspension functions can only be called within coroutine body error. But I thought that his was within a coroutine body?
Any help appreciated. Thanks.
EDIT
I response to Rene's answer below, I want to add a few things.
You are right, I am missing three cont.cancel() calls. I've modified the OP. Good points.
I have breakpoints all over the suspendCancellableCoroutine such that any possible scenario (success, failure, etc.) will be hit. But that callback never registers.
Wondering if there is something missing in fetchVisualElementsForClub() that is needed to pass the callback up to the suspendCancellableCoroutine. That seems to be where this is hanging.
You must call cont.resume() or cont.cancel() on every branch in your callback handling.
But in your example at least three cases are missing.
If the response is successful but no body is provided, you call nothing.
If the response is successful, the body is not null, but the it.result is not RESULT_SUCCESS you call nothing.
If something goes wrong in onFailure, you call nothing.
As long as neither resume or cancel is invoked, the coroutine will stay suspended, means hangs.
when you use suspend keyword your are telling that function shoud be called inside a coroutine bode, for example:
suspend fun abc(){
return
}
when you want to call above function you have to call it inside coroutines such as below:
GlobalScope.launch {
abc()
}

Getting data from API but can't do anything with it

I'm trying to learn to code android apps with kotlin. I want to display some data that i get from the api(https://api.kuroganehammer.com/api/characters). I generated the data classes using the JSON to kotlin plugin in IntelliJ.
Logcat shows that i'm getting the Data however it doesn't trigger the onResponse method.
I tried debugging but can't really find anything other than for some reason the onFailure and onResponse methods are ignored.
ApiRequest:
fun fetchAllCharacters(): Call<CharacterApiRequest>
CharacterApiRequest:
#SerializedName("character")
val characters: List<Character>
)
Characters(Fragment, gets called in onViewCreated()):
.enqueue(object : Callback<CharacterApiRequest> {
override fun onFailure(call: Call<CharacterApiRequest>, t: Throwable) {
//Display an error to the user, because there was a io exception
}
override fun onResponse(call: Call<CharacterApiRequest>, response: Response<CharacterApiRequest>) {
//We got a response
if (response.isSuccessful) {
//Bind the data only when we have it
response.body()?.apply {
adapter.setData(this.characters as MutableList<Character>)
}
} else {
//Display an error
}
}
})
Changing the callback's generic parameter from CharacterApiRequest to List worked, as stated in the comment from #MateuszHerych

Function return null

This function get data from API.
I don't understand its process.
Here the problem,
it returned weatherModel first and then it execute onResponse later.
It show in logcate Log.d("data", "Here") first
My function
fun get(city: String?): WeatherModel? {
var weatherModel: WeatherModel? = WeatherModel()
val retrofit =
retrofit2.Retrofit.Builder().baseUrl(Util.BASE_URL).addConverterFactory(GsonConverterFactory.create())
.build()
val service = retrofit.create(WeatherInterface::class.java)
val call = service.searchCity(city!!, "metric", "789af9673b393eb97f2acdea022f2005")
call.enqueue(object : Callback<WeatherModel> {
override fun onFailure(call: Call<WeatherModel>, t: Throwable) {
Log.d("data", "error ${t.message}")
}
override fun onResponse(call: Call<WeatherModel>, response: Response<WeatherModel>) {
Log.d("data", "success ${response.body()!!.weather!!.size}")
weatherModel = response.body()
data!!.getDataTrigger(weatherModel!!)
}
})
Log.d("data", "Here")
return weatherModel
}
So what is the problem?
It show in logcate Log.d("data", "Here") first
call.enqueue is a asynchronous call, which means it doesn't run on main thread, that's the reason you will get Log.d("data", "Here") first.
This is the reason you are getting null returned
If you are using this method asynchronously or in some service and you want to execute synchronously then kindly use call.execute but this will only work if you are using in other thread apart from Main thread as network operation are not allowed on main thread, in this case this method wont return null.
Either you should use live data or an interface for the callback to receive WeatherModel.

Categories

Resources