简体   繁体   中英

Android Volley synchronous request not working

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(Na tive 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?

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!

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() {

                }
            });

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM