Reputation: 3234
I am trying to make a synchronous request to the server using RequestFuture but it's not working . The same request when done using asynchronous works fine.
This is my code:
public void fetchModules(){
JSONObject response = null;
RequestQueue requestQueue = Volley.newRequestQueue(getContext());
RequestFuture<JSONObject> future = RequestFuture.newFuture();
JsonObjectRequest request = new JsonObjectRequest(Url.ALL_MODULES_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,"interupted");
} catch (ExecutionException e) {
Log.d(TAG,"execution");
} catch (TimeoutException e) {
e.printStackTrace();
}
Log.d(TAG,response.toString());
}
I am getting a nullpointerexception:
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String org.json.JSONObject.toString()' on a null object reference at com.maths.app.AllModules.fetchModules(AllModules.java:85) at com.maths.app.AllModules.onCreateView(AllModules.java:51) at android.support.v4.app.Fragment.performCreateView(Fragment.java:2080) at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1108) at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1290) at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:801) at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1677) at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:536) at android.os.Handler.handleCallback(Handler.java:746) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5443) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:728) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
it's returning a null response. How can I solve this?
Upvotes: 2
Views: 9396
Reputation: 432
We can use RxJava.
Lets assume that the fetchModules method returns JSONObject
Observable<JSONObject> moduleObservable = Observable.defer(new Callable<ObservableSource<? extends JSONObject>>() {
@Override
public ObservableSource<? extends JSONObject> call() throws Exception {
return Observable.just(fetchModules());
}
});
In MainActivity
moduleObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new io.reactivex.Observer<JSONObject>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(JSONObject response) {
// Your response is here
}
@Override
public void onError(Throwable e) {
showAlert(context, e.getMessage());
}
@Override
public void onComplete() {
}
});
Upvotes: 1
Reputation: 737
After researching this question myself, you CAN'T (at the moment of writing) do it in the main thread
I want Volley to do it in the main thread because I need to block until Volley finish getting response. And this is not possible, synchronous request on Volley must be in an async thread, not in the main thread.
The work around that I have done is, doing the subsequent codes inside the onResponse method of Volley request
Upvotes: 0
Reputation: 3994
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 Log
the 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!
Upvotes: 10