How to Do a Synchronous Request With Volley

Can I do a synchronous request with volley?

It looks like it is possible with Volley's RequestFuture class. For example, to create a synchronous JSON HTTP GET request, you can do the following:

RequestFuture<JSONObject> future = RequestFuture.newFuture();
JsonObjectRequest request = new JsonObjectRequest(URL, new JSONObject(), future, future);
requestQueue.add(request);

try {
JSONObject response = future.get(); // this will block
} catch (InterruptedException e) {
// exception handling
} catch (ExecutionException e) {
// exception handling
}

Android Volley synchronous request not working

tl;dr;
You got deceived by the try-catch

Explanation:
Because the RequestFuture.get()is probably running on the UI thread you are really getting a java.util.concurrent.TimeoutException behind the scenes. That is the default behaviour when the calls gets executed on the main thread.

The try catch stops the app from crashing, nevertheless the response is still a null reference which crashes the app when you try to Logthe result.
If you comment the following line you will see that the app doesn't crash (there) anymore.

Log.d(TAG,response.toString());

Fix: Making the RequestFuture network call on another thread!

One way to do it:

    public class TestVolley {

private String TAG = "SO_TEST";
private String url = "http://pokeapi.co/api/v2/pokemon-form/1/";


public JSONObject fetchModules(Context ctx){
JSONObject response = null;
RequestQueue requestQueue = Volley.newRequestQueue(ctx);


RequestFuture<JSONObject> future = RequestFuture.newFuture();
JsonObjectRequest request = new JsonObjectRequest(url,null,future,future);
requestQueue.add(request);


try {
response = future.get(3, TimeUnit.SECONDS); // Blocks for at most 10 seconds.
} catch (InterruptedException e) {
Log.d(TAG,"interrupted");
} catch (ExecutionException e) {
Log.d(TAG,"execution");
} catch (TimeoutException e) {
e.printStackTrace();
}

Log.d(TAG,response.toString());

return response;
}
}

The AsyncTask which will make the network call :

public class MyVolleyAsyncTask extends AsyncTask<String,String, JSONObject> {

private Context ctx;

public MyVolleyAsyncTask(Context hostContext)
{
ctx = hostContext;
}

@Override
protected JSONObject doInBackground(String... params) {

// Method runs on a separate thread, make all the network calls you need
TestVolley tester = new TestVolley();

return tester.fetchModules(ctx);
}


@Override
protected void onPostExecute(JSONObject result)
{
// runs on the UI thread
// do something with the result
}
}

Main Activity:

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// this is your old code which will crash the app
//TestVolley tester = new TestVolley();
//tester.fetchModules(this);

// Works!
new MyVolleyAsyncTask(this).execute();
}
}

result:

com.so.henriquems.testvolleyfuture D/SO_TEST: {"id":1,"pokemon":{"url":"http:\/\/pokeapi.co\/api\/v2\/pokemon\/1\/","name":"bulbasaur"},[...]

Hope this helps

cheers!

Android - Volley - synchronous and asynchronous http requests

Honestly, i dont use this method anymore. It would be better to use Callback instead of sync requests queue, it'll make your code easier to update, easier to other dev to understand. Do not hesitate to add an extra Callback parametre, ex: request_1 (callback_1, ...), callback_1 calls request_2(callback_2,...) in listener events >> etc.

Below is the old answer:

I post my solution here (it's not very clean but it's my work so far):

  • Using the same Volley Request Queue for both async and sync requests.
  • Creating new class SyncRequestLoader implements Response.ErrorListener, Response.Listener.

My class:

public class SyncRequestLoader implements Response.ErrorListener, Response.Listener {
public final String TAG = "SyncRequestLoader";

private RequestQueue mRequestQueue;

private Response.Listener mListener;
private Response.ErrorListener mErrorListener;

private LinkedList<StringRequest> mSyncRequests;

private Handler.Callback mCallback;

public SyncRequestLoader(RequestQueue mRequestQueue) {
this.mRequestQueue = mRequestQueue;
mSyncRequests = new LinkedList<>();
}

synchronized public void add(StringRequest request) {
mSyncRequests.add(request);

if (size() == 1)
transceive();
}

@Override
synchronized public void onResponse(Object response) {
mListener.onResponse(response);

//do anything here if u want

removeCompletedRequest();

continueIfPossible();
}

@Override
synchronized public void onErrorResponse(VolleyError error) {
mErrorListener.onErrorResponse(error);

//do anything here if u want

removeCompletedRequest();

continueIfPossible();
}

synchronized private void transceive() {
StringRequest request = mSyncRequests.getFirst();
mListener = request.getListener();
mErrorListener = request.getErrorListener();

StringRequest new_request = new StringRequest(request.getUrl(), this, this);

mRequestQueue.add(new_request);
}

synchronized private void removeCompletedRequest() {
mSyncRequests.removeFirst();
}

synchronized private void continueIfPossible() {
if (size() > 0)
transceive();
else if (isOnCallback())
mCallback.handleMessage(Message.obtain(null, 1));
}

public boolean isOnCallback() {
return (mCallback != null);
}

public void setCallback(Handler.Callback callback) {
this.mCallback = callback;
}

}

I'm using SyncRequestLoader mCallback for notify that Sync Request Queue has finished. I store all sync request in a Linkedlist then add into volley queue one by one. Each request will be injected to Volley request queue since we got the response of previous request. I "tricked" here by making a new request with local variable mListener and mErrorListener, you can see I parse the response to the "true" listeners after.

How to make volley calls synchronous

Call OAuthsevice firstly on login button click and OnResponse method call login Api

OAuth service call:

public class AuthAuthentication {
private static final String TAG = AuthAuthentication.class.getSimpleName();
private TinyDB tinyDB;
private Context context;

public AuthAuthentication(TinyDB tinyDB, Context context){
this.tinyDB = tinyDB;
this.context = context;
}

public void getAuthToken() {

String tag_json_obj = "json_obj_req";
String url = https://abc.xyz.com/Services/oauth/token?grant_type=password&client_id=restapp&client_secret=restapp&username=admin&password=admin";


JsonObjectRequest jsonObjReq = new JsonObjectRequest(Request.Method.GET,
url, "",
new Response.Listener<JSONObject>() {

@Override
public void onResponse(JSONObject response) {

try {
Log.d(TAG, " Response" + response.toString());
tinyDB.putString(Constants.MY_SHARED_PREF_AUTH_TOKEN, "" + response.getString(AppTags.TAG_AUTH_TOKEN));
onClickLogin();/// here to peform login

} catch (JSONException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {

@Override
public void onErrorResponse(VolleyError error) {

VolleyLog.d(TAG, "Error: " + error.getMessage());
Toast.makeText(context, context.getResources().getString(R.string.unable_to_process), Toast.LENGTH_SHORT).show();
}
});

// Adding request to request queue
AppController.getInstance().addToRequestQueue(jsonObjReq, tag_json_obj);

}

UPDATE

if OAUTH will call multiple time we need is OAUTH key for diferent API...

Create This class in utility folder

public interface VolleyResponse {
void processFinish(String output);
}

just change you class constructor like this..

public class AuthAuthentication {
private static final String TAG = AuthAuthentication.class.getSimpleName();
private TinyDB tinyDB;
private Context context;
private VolleyResponse delegate;

public AuthAuthentication(TinyDB tinyDB, Context context,VolleyResponse delegate){
this.tinyDB = tinyDB;
this.context = context;
this.delegate= delegate;
}
--------
-------
}

In OnResponse method of AuthAuthentication class

@Override
public void onResponse(JSONObject response) {

try {
Log.d(TAG, " Response" + response.toString());
tinyDB.putString(Constants.MY_SHARED_PREF_AUTH_TOKEN, "" + response.getString(AppTags.TAG_AUTH_TOKEN));
//send response of volley
delegate.processFinish(tinyDB); //it will broadcast your response

} catch (JSONException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}

and use this as you want. suppose you have to use in login click

login.setOnClickListerner(new View.OnClickListenr(){
@Override
public void onClick(View view){
AuthAuthentication auth= new AuthAuthentication(tinyDB,mContext,new VolleyResponse() {
@Override
public void processFinish(String output) {
//output conatins response
loginApicall();
}

}.getAuthToken(); ///if not work then auth.getAuthToken

}
});


Related Topics



Leave a reply



Submit