Reputation: 676
I need to send a series of requests to a server. Doing that in a console java application is super simple, as it's just executing blocking requests that wait for the result, evaluate the result, then issue next request...
However, I don't know how to best port that to Android, as I need to process though a series of actions which are all handled in threads and I can't "Wait" for them to complete. And the volley code is nice, but the result would be a HUGE block if nested code in nested code in nested code.
The normal sequence would be:
In Android the code for a request now looks as follows:
JsonObjectRequest jsonRequest = new JsonObjectRequest
(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
try {
int myResult = response.getInt("result");
if (myResult!=0) {
throw new RuntimeException("Bad foo!");
} else {
/* ...and here there's my problem. Here I would
normally create the next step (= the next
"jsonRequest = new JsonObjectRequest..." including the
entailed anonymous handler routine. But that would
lead to a giantly nested code. For you code-gurus:
How to solve that and still produce beautiful
readable code? */
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
}
});
MyApplication.getInstance().getRequestQueue().add(jsonRequest);
Also: In a console-app it's easy to pack the login-code into a function and call it for multiple purposes. e.g.
private void RenameDaFiles() {
LoginToServer(); // performs an internet request, in volley that starts a thread
GetData();
RenameFiles();
}
private void DeleteDaFiles() {
LoginToServer();
GetSomeOtherData();
DeleteFiles();
}
Using volley I can't use that procedural approach as calling volley returns immediately even though the request is not yes completed. How do I do that best?
Thanks a lot in advance!
Upvotes: 1
Views: 958
Reputation: 3037
I asked myself the same and here is what I came up with: I wrote a private method for each request in my request chain. The first method calls the second one when it's done which calls the third one when it's done itself and so on.
The last method calls the result handler that is passed on to the next method each time.
So, actually it does the same that the nested anonymous classes do. But I think it's better readable.
Calling Code:
sendFirstRequest(new Consumer<JSONObject>() {
@Override
public void accept(JSONObject result) {
Log.d(TAG, "accept: result = " + result);
}
});
Request Chain:
private void sendFirstRequest(final Consumer<JSONObject> resultConsumer) {
requestQueue.add(new JsonObjectRequest(
Request.Method.GET,
"www.google.de",
null,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
sendSecondRequest(resultConsumer); // <-- chaining
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
}
}
));
}
private void sendSecondRequest(final Consumer<JSONObject> resultConsumer) {
requestQueue.add(new JsonObjectRequest(
Request.Method.GET,
"www.google.de",
null,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
sendLastRequest(resultConsumer); // <-- chaining
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
}
}
));
}
private void sendLastRequest(final Consumer<JSONObject> resultConsumer) {
requestQueue.add(new JsonObjectRequest(
Request.Method.GET,
"www.google.de",
null,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
resultConsumer.accept(response); // <-- final result
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
}
}
));
}
I use that pattern whenever I have that "asynchronous chain problem".
To simplify the example I assume that the RequestQueue
is defined as a field.
Also, note that the Java 8 functional interfaces like Consumer
are only available since API level 24. However, one could define a simple version oneself:
public interface Consumer<T> {
void accept(T t);
}
Upvotes: 1