Reputation: 4616
I need to return the value uId
. I am getting the correct value in the first log statement inside the onResponse()
function. But when it comes to the return statement it returns null.
I think that onResponse() is running on another thread. If so, how can I make the getNumber() function wait till the onResponse() function finishes execution.(Like thread.join())
Or is there any other solution?
code :
String uId;
public String getNumber() {
ApiInterface apiInterface = ApiClient.getClient().create(ApiInterface.class);
Call<TopLead> call = apiInterface.getTopLead();
call.enqueue(new Callback<TopLead>() {
@Override
public void onResponse(Call<TopLead> call, Response<TopLead> response) {
String phoneNumber;
TopLead topLead = response.body();
if (topLead != null) {
phoneNumber = topLead.getPhoneNumber().toString();
uId = topLead.getUId().toString();
//dispaly the correct value of uId
Log.i("PHONE NUMBER, UID", phoneNumber +", " + uId);
onCallCallback.showToast("Calling " + phoneNumber);
} else {
onCallCallback.showToast("Could not load phone number");
}
}
@Override
public void onFailure(Call<TopLead> call, Throwable t) {
t.printStackTrace();
}
});
//output: Return uid null
Log.i("Return"," uid" + uId);
return uId;
Upvotes: 6
Views: 15047
Reputation: 3708
Since I could not understand the answer of @David I had to come up with a solution myself. I had to receive data from the network using the library OkHttp3
. To solve this problem I used LiveData
.
fun getText(): LiveData<String> {
val liveData = MutableLiveData<String>()
var result = ""
val client = OkHttpClient()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
e.printStackTrace()
result = e.toString()
liveData.postValue(result)
}
override fun onResponse(call: Call, response: Response) {
result = response.body.toString()
liveData.postValue(result)
}
})
return liveData
}
Once you have the LiveData object we can observe it:
getText().observe(viewLifecycleOwner) {
Toast.makeText(requireContext(), "Received $it", Toast.LENGTH_SHORT).show()
}
Upvotes: 2
Reputation: 1001
The problem seems to be that you wanted uId and you are not getting it. Your function getNumber()
executes TopDown but the request you are making is Asychronous, runs on a different thread. So at the time that you are returning uId
, there is no value in uId
. Out of the callbacks that call.enqueue
has, and I mean onResponse()
and onFailure()
, there is no way you can get uId
in onFailure
, that is obvious but it is not so obvious that you will get uId
in `onResponse()'.
From the javadoc for Retrofit:
onResponse
void onResponse(Call<T> call, Response<T> response)
Invoked for a received HTTP response. Note: An HTTP response may still indicate an application-level failure such as a 404 or 500. Call Response.isSuccessful() to determine if the response indicates success.
So in onResponse
you will still need to put some code to ensure you are getting uId
and then only return it. And do not return the uId
directly, set to some string (global) and access it after making sure it is available.
So change your code like::
public void getNumber() {
ApiInterface apiInterface = ApiClient.getClient().create(ApiInterface.class);
Call<TopLead> call = apiInterface.getTopLead();
call.enqueue(new Callback<TopLead>() {
@Override
public void onResponse(Call<TopLead> call, Response<TopLead> response) {
if(response.isSuccesful(){
String phoneNumber;
TopLead topLead = response.body();
if (topLead != null) {
phoneNumber = topLead.getPhoneNumber().toString();
uId = topLead.getUId().toString();
//dispaly the correct value of uId
Log.i("PHONE NUMBER, UID", phoneNumber +", " + uId);
onCallCallback.showToast("Calling " + phoneNumber);
//output: Return uid
Log.i("Return"," uid" + uId);
} else {
onCallCallback.showToast("Could not load phone number");
}
} else{
Log.e("in ", " response is not successful" )
}
}
@Override
public void onFailure(Call<TopLead> call, Throwable t) {
t.printStackTrace();
}
});
This still runs on a different thread, so uId
will be set after some time. Once set you can use the value. You can also use Synchronous
request if you want to run on the main thread and get the uId
using call.execute().body();
which returns TopLead
object and then you can get uId
from ther e using topLead.getUId.toString()
.
I hope it helps, do ask in comments if you need clarifications.
Upvotes: 1
Reputation: 361
Your method performs an async request. So the action "return uId; " doesn't wait until your request finishes because they are on different threads.
There are several solutions I can suggest
Use an interface callback
public void getNumber(MyCallback callback) {
...
phoneNumber = topLead.getPhoneNumber().toString();
callback.onDataGot(phoneNumber);
}
Your callback interface
public interface MyCallback {
void onDataGot(String number);
}
And finally, calling the method
getNumber(new MyCallback() {
@Override
public void onDataGot(String number) {
// response
}
});
When using Kotlin (I think it's time for you to use Kotlin instead of Java :))
fun getNumber(onSuccess: (phone: String) -> Unit) {
phoneNumber = topLead.getPhoneNumber().toString()
onSuccess(phoneNumber)
}
Calling the method
getNumber {
println("telephone $it")
}
Upvotes: 9
Reputation: 215
Don't return the value. Because AsyncTask is a separate one runs outside the normal process.
So please create a method with the parameters and pass your values to that method.
Upvotes: 0