Java dealing with callback inside a function - java

I have a Java (Android) code that has callback, now I want to extract that code to its separate function for reusability, here is what I have:
InternQuery query = InternQuery.builder().build();
ApolloCall<InternQuery.Data> apolloCall = new HoraApolloClient(this.context).getHoraApolloClient().query(query);
apolloCall.enqueue(new ApolloCall.Callback<InternQuery.Data>() {
#Override
public void onResponse(#Nonnull Response<InternQuery.Data> response) {
InternQuery.Data rData = response.data();
// some error handling code ...
InternQuery.Intern intern = rData.intern();
}
#Override
public void onFailure(#Nonnull ApolloException e) { }
});
Here you can see I am using Android Apollo client for GraphQL. So I have a function .enqueue which has a callback, in callback onResponse I can get my intern object.
Now I want to take this code into seperate method in a InternApi class, so code will loke something like that:
public InternsQuery.Intern getIntern() {
// the code above goes here ...
}
The problem here is that How can I return from that function my intern object, as .enqueue is asynchronous, and my getIntern function will return earlier, I need to make it wait my callback.
Generally I guess, I need something like async/await. So I expect my code to be something like that:
public InternsQuery.Intern getIntern() {
InternsQuery.Intern intern = await apolloCall.enqueue();
return intern;
}
Is there async/await in java, or any other way of solving it?
Ideally I want it in a functional way, so my getIntern method returns intern. I believe I could pass reference to intern object that I create earlier and assign it in my callback, but it seems to be a bad solution.

Related

How to return an object using Retrofit 2

I am trying to build a Rest client with Retrofit 2. This is the first time using this library.
Most of the examples that I have seen normally use a callback function in Android activity or view.
http://www.vogella.com/tutorials/Retrofit/article.html
https://github.com/MeetMe/TwitchTvClient/tree/master/src/com/wdonahue/twitchtvclient
I was wondering if, in the code below, one could return an object of type Appver instead of void?
Thank you!
public void GetAppver()
{
Call<Appver> call = endPoint.GetAppver();
call.enqueue(new Callback<Appver>()
{
#Override
public void onResponse(Call<Appver> call, Response<Appver> response)
{
if (response.isSuccessful())
{
Appver appver = response.body();
}
}
#Override
public void onFailure(Call<Appver> call, Throwable t) { }
});
}
I guess you want to have Appver because you need to use this object to possibly poulate the UI for instance inside a RecyclerView, in this case I guess you cannot avoid to not use void, because you are extending the library. But for sure you can use Dagger2 or a Singleton although there are some contraindications to maintain an instance of Appver so that you can use it in an Adapter for instance
Also in real life is rarely used Retrofit2 in this way, usually you implement it with RXJava2 or with Google Architecture components as ViewModel,LiveData and LifeCycle
You can call Retrofit service methods synchronously.
public AppVer GetAppVer() {
Call<AppVer> call = endPoint.GetAppver();
Response<AppVer> response;
try {
response = call.execute();
} catch (IOException e) {
// Handle network communication errors here
}
if (!response.isSuccessful()) {
// Handle REST service errors here
}
return response.body();
}
However, as you know, you can't do network communication on the main thread in Android. You have to wrap the call in e.g. a Thread, AsyncTask or Service.
Have a look at the following links how to structure your app.
Android Architecture Blueprints: https://github.com/googlesamples/android-architecture
Android Architecture Components: https://developer.android.com/topic/libraries/architecture

Exiting out of the iteratable Observable upon successful response

I have a scenario in which I've to bridge the nonreactive code with Reactive Code.
Consider the following scenario.
I have a list of 3 URLs in an ArrayList. I want to call each URL in the order they are inside the ArrayList. I can call only 1 URL at a time. If the first URL returns a successful Response, I want to call onComplete() and don't wanna execute the remaining URL. However, if the response is an error, I want to execute the next URL in the list. I don't want RxJava to call flatMap for the next URL unless I get an error response for the previous URL. Due to my primitive understanding of RxJava, I couldn't figure out a way to achieve this.
What I planned to do something like this:
Observable.fromIteratable(urlList)
.subscribeOn(Schedulars.io())
.flatMap(new Func(String url, String data) {
SomeNetworkLibrary.getData(url)
.OnResponse(new NewResponse() {
public void onSuccess(String dataFromInternet) {return dataFromInternet;}
public void onError(String errorMessage) {return errorMessage;)
})
// wait until we have response from the network call above and then return
// I don't know what will be the cleanest and efficient way of waiting here.
});
TLDR;
I don't want flatMap() to be called before the results from the previous flatMap() have been returned.
How can I do that?
You can turn the network api call into an Observable and then use take(1) after the flattening:
Observable.fromIteratable(urlList)
.subscribeOn(Schedulars.io())
.concatMapDelayError((String url, String data) -> {
return Observable.create(emitter -> {
SomeNetworkLibrary.getData(url)
.OnResponse(new NewResponse() {
public void onSuccess(String dataFromInternet) {
emitter.onNext(dataFromInternet);
// don't call emitter.onComplete() so that
// concatMapDelayError doesn't switch to the next source
}
public void onError(String errorMessage) {
emitter.onError(errorMessage);
}
);
});
// wait until we have response from the network call above and then return
// I don't know what will be the cleanest and efficient way of waiting here.
})
.take(1);

Android - Retrofit web service value problems

I have phone contact numbers list stored in an array and called contactsString[]
and in an online database registered users numbers
I want to count how many registered users are there
and there is my code
for (i=0;i<contactsString.length-1;i++){
Phone phone=new Phone();
phone.phone=contactsString[i]
WebService.getInstance().getApi().checkNumber(phone).enqueue(new Callback<MainResponse>() {
#Override
public void onResponse(Call<MainResponse> call, Response<MainResponse> response) {
if (response.body().status==1){
availableUsers++;
}
}
#Override
public void onFailure(Call<MainResponse> call, Throwable t) {
}
});
}
my problem is the web service response is delayed so it don't count and availableUsers is printed it's initial value which is 0
I would try better sending an array of Phone objects. In this way you would get the correct answer in 1 call.
I would never do this in the way you implemented: imagine you have 500 contacts: you will be doing 500 calls to your server. Now imagine you have 100000 users with 500 contacts each
Try to customize your api call in this format. Which uses async task class.
private void phoneContact() {
new AsyncTask<String,Void,String>() {
#Override
protected String doInBackground(String ... params) {
try {
Platform http = Url_Contacts;
JSONObject resp = http.search(what,where);
Log.d(TAG, "Response: " + resp.toString());
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
return "";
}
}.execute();
}
Make sure that your service works well and the format of json with value status in there.
In onResponse, run on UIThread to update your View with the availableUsers.
The enqueue method is asynchronous. So your code should respect the multithreaded nature of it.
There are many approaches you can take:
Replace enqueue() method with execute(). But that makes all the calls synchronous. If you call it in UI Thread then whole app can stutter. Probably you will get NetworkOnMainThreadException. Not a good approach anyway.
Use RxAndroid or RxJava with Observer pattern.
Simple solution. Create a variable int callsFinished = 0;. In onResponse increment that variable. Then if that callsFinished == contactsString.length that means all calls have been done.
In your activity add a listener
void onAllCallsFinished(int availableUsers) {
//do what you want with availableUsers information
}
Call onAllCallsFinished(availableUsers) when callsFinished == contactsString.length.
There you can do what you want with that data. Update a view, call another service.

Wrapping a Callback Function

In an Android app that I'm writing, much of the app involves performing web requests to a specific API. Since the format of the data ends up being the same, I wanted to centralize many of the functions that I end up performing every request, rather than rewriting them every time.
For example, I perform the same error checking routine every time I make a web request:
JSONObject jo = new JSONObject(response);
boolean success = jo.getBoolean("success");
if(success) {
//Do work
} else {
//Display error
}
My thought was to make this some sort of class that implements Response.Listener, but I couldn't determine an effective way of handling errors and such. My question is is there an easy way to perform repeated functions in callbacks? I'm looking for an elegant solution, not necessarily the easiest.
You can achieve this by passing one or two interfaces to your method which is cumbersome. Java 8 brings you lambda which makes working with callback much more elegant. To use lambdas in Android you can use retrolambda: https://github.com/orfjackal/retrolambda
Your generic method could look like this
parseResponse(String response,
Consumer<JSONObject> successConsumer,
Consumer<String> errorConsumer) {
JSONObject jo = new JSONObject(response);
boolean success = jo.getBoolean("success");
if(success) {
successConsumer.accept(jo);
} else {
errorConsumer.accept("error");
}
}
You would use this method like this:
class MyClass {
void onResponse(String response) {
....
parseResponse(response, this::handleData, this::handleError);
}
void handleData(JSONObject object) {....}
void handleError(String object) {....}
}

Should there be any logic on the activity class?

I was recently reading about design patterns and especially about low coupling and delegation.
I was wondering, whether there should be any logic on the Activity class or if it only serves the view.
E.g. I have an activity called BattleActivity and that is supposed to work as some kind of session between two players. A lot of Push Notifications happen there, also the class works as an Observer, so there is a lot of comminication going on there.
Right now I am trying to figure out what logic could I move to a separated object(and whether I should) and then just work with the activity.
Example of one of my methods on the activity:
private void postCastedSpell(final int spellId) {
Call call = StaticGlobalContainer.api.postSpellToBattle(Integer.parseInt(battleId), Integer.parseInt(MainActivity.CURRENT_USER_ID), spellId, 100);
call.enqueue(new Callback<User>() {
#Override
public void onResponse(Response<User> response, Retrofit retrofit) {
User user = response.body();
if (response.code() == 202) {
// 200
Log.i("Posting spell to battle", "Success");
Boolean affectedUserIsOpponent = isUserOpponent(user);
if (affectedUserIsOpponent && user.currentHp<1){
StaticGlobalContainer.battleOnResult(Constants.WON, getApplicationContext());
}else {
updateBattleLog(affectedUserIsOpponent, user, spellId);
}
// TODO: do something here
} else {
// 404 or the response cannot be converted to User.
Log.e("Posting spell to battle", "Error:" + response.errorBody());
}
}
#Override
public void onFailure(Throwable t) {
Log.i("HttpRequest-Post spell", "Failure");
}
});
}
It's not specifically bad to put a lot of logic in Activities, but you're right to try to keep it only view related things. If the app is relatively small, it might not be worth moving the logic out. Also, there is some overhead to using abstractions.
if your abstractions aren't supplying a significant benefit, you should avoid them
I try to keep any big data objects in a manager class, so given your example, it might worthwhile to create a Battle manager class to hold all the logic involved in it, like this postCastedSpell function. This way all the Battle information is self contained, and also can be used elsewhere in other activities.
Just keep in mind if you're use data manager classes and you want them to prompt some sort of interation with the UI, you'll have to use Callbacks or the Bus pattern since the Battle manager won't have access to your UI. For example, to call the postCastedSpell the call would look like:
BattleActivity:
BattleManager bm = BattleManager.getInstance(user1, user2);
onSpellClicked() {
bm.castSpell(spellId, user1, callback)
}
BasicCallback callback = new BasicCallback() {
#Override
onComplete() {
if (MyInfoFragment.this.isVisible()) {
[Update UI]
}
}
};
NOTE: When using callbacks like my example, when it finally gets called the activity may have already gone out of view and have been already garbage collected. So in the callback function you need to first make sure it is still visible before trying to modify the UI that possibly no longer exists.

Categories

Resources