Android Volley synchronous request not working

廉价感情. 提交于 2019-12-10 22:45:27

问题


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?


回答1:


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!




回答2:


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

                }
            });



回答3:


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



来源:https://stackoverflow.com/questions/41724692/android-volley-synchronous-request-not-working

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!