Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: Volley HTTP Patch Request

I just started porting from my existing network library to Android's Volley. So far I have successfully implemented Volleys ImageLoader where applicable. Now, I am trying to get my first http call up and running, but am finding this error.

Note: I intentionally started with a PATCH request because I will be using them frequently. Also, my version of Volley DOES support patch: https://android.googlesource.com/platform/frameworks/volley/+/master/src/com/android/volley/Request.java https://android.googlesource.com/platform/frameworks/volley/+/master/src/com/android/volley/toolbox/HurlStack.java

Stacktrace:

E/InputDialogFragment(27940): VolleyError: java.net.ProtocolException: Connection already established
D/Volley  (27940): [1] MarkerLog.finish: (67   ms) [ ] https://mobile.example.com/m/api/v1/user/ 0xb33a3c8d NORMAL 2
D/Volley  (27940): [1] MarkerLog.finish: (+0   ) [ 1] add-to-queue
D/Volley  (27940): [1] MarkerLog.finish: (+0   ) [544] cache-queue-take
D/Volley  (27940): [1] MarkerLog.finish: (+0   ) [544] cache-miss
D/Volley  (27940): [1] MarkerLog.finish: (+0   ) [545] network-queue-take
D/Volley  (27940): [1] MarkerLog.finish: (+14  ) [545] post-error
D/Volley  (27940): [1] MarkerLog.finish: (+53  ) [ 1] done

PATCH Request

    HashMap<String, Object> values = new HashMap<String, Object>();
    values.put(mParam, val);
    JsonObjectRequest request = new JsonObjectRequest(Request.Method.PATCH, APIConstants.URL_USER, new JSONObject(values),
        new Response.Listener<JSONObject>(){
            @Override
            public void onResponse(JSONObject response){
               // Blah do stuff here 
                mProgressDialog.dismiss();
            }
        },
        new Response.ErrorListener(){
            @Override
            public void onErrorResponse(VolleyError error){
                Log.e(TAG, "VolleyError: " + error.getMessage());
            }
        }){
            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                HashMap<String, String> headers = new HashMap<String, String>();
                VolleySingleton.getInstance().addCookie(headers);
                return headers;
            }
    };
    VolleySingleton.getInstance().getRequestQueue().add(request);

Yes, I plan to eventually build out classes for StringRequest, JsonObjectRequest, etc... but currently I just want to get one up and running.

Also, if you are wondering about addCookie, for now I prefer to save my cookie in preferences as I am not as familiar with CookieManager.

VolleySingleton

public class VolleySingleton {
    private static final String COOKIE_KEY = "Cookie";
    private static VolleySingleton mInstance = null;
    private RequestQueue mRequestQueue;
    private ImageLoader mImageLoader;
    private SharedPreferences mPreferences;
    private VolleySingleton(){
        mRequestQueue = Volley.newRequestQueue(MyApplication.getAppContext());
        mImageLoader = new ImageLoader(this.mRequestQueue, new ImageLoader.ImageCache() {
            private final LruCache<String, Bitmap> mCache = new LruCache<String, Bitmap>(10);
            public void putBitmap(String url, Bitmap bitmap) {
                mCache.put(url, bitmap);
            }
            public Bitmap getBitmap(String url) {
                return mCache.get(url);
            }
        });
        mPreferences = MyApplication.getAppContext().getSharedPreferences(PrefConstants.PREFERENCES, 0);
    }

    public static VolleySingleton getInstance(){
        if(mInstance == null){
            mInstance = new VolleySingleton();
        }
        return mInstance;
    }

    public RequestQueue getRequestQueue(){
        return this.mRequestQueue;
    }

    public ImageLoader getImageLoader(){
        return this.mImageLoader;
    }

    public final void addCookie(Map<String, String> headers) {
        String cookie = mPreferences.getString(PrefConstants.PREF_COOKIE, null);
        if(cookie != null){
            headers.put(COOKIE_KEY, cookie);
        }
    }
}
like image 906
Brice Avatar asked Mar 14 '14 17:03

Brice


3 Answers

Problem:

Volleys HurlStack (HttpUrlConnection) does have supporting code for PATCH. However, it appears to still throw the exception posted in my title and stacktrace whenever you try to issue a PATCH request.

Hack solution:

1) Force Volley to use HttpClientStack.

Below is an updated version of my VolleySingleton constructor. This 'works', but clearly wastes the Hurl implementation which is considered better if (Build.VERSION.SDK_INT >= 9). Not too mention Google plans to move on from the apache HttpClient altogether in future.

private VolleySingleton(){
    mPreferences = MyApplication.getAppContext().getSharedPreferences(PrefConstants.PREFERENCES, 0);

    String userAgent = "volley/0";
    try {
        String packageName = MyApplication.getAppContext().getPackageName();
        PackageInfo info = MyApplication.getAppContext().getPackageManager().getPackageInfo(packageName, 0);
        userAgent = packageName + "/" + info.versionCode;
    } catch (NameNotFoundException e) {}

    HttpStack httpStack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
    mRequestQueue = Volley.newRequestQueue(MyApplication.getAppContext(), httpStack);
    mImageLoader = new ImageLoader(this.mRequestQueue, new ImageLoader.ImageCache() {
        private final LruCache<String, Bitmap> mCache = new LruCache<String, Bitmap>(10);
        public void putBitmap(String url, Bitmap bitmap) {
            mCache.put(url, bitmap);
        }
        public Bitmap getBitmap(String url) {
            return mCache.get(url);
        }
    });
}

2) Continue to use the above VolleySingleton ONLY for patch; rename it VolleySingletonPatch() and then of course create a Default VolleySingleton() for ALL other NON-PATCH calls. (better than 1, but still not optimal)

3) Solve the exception being thrown in HurlStack despite the fact that Volley has implemented PATCH. This would be BEST, but I prefer to avoid tinkering with Volley directly or extending my own HttpStacks unnecessarily.

I am leaving this unanswered as I would greatly appreciate any insight and of course better options than the ones I have come up with here.

like image 174
Brice Avatar answered Nov 16 '22 17:11

Brice


I had this problem too, my solution was to just give up on the underlying inbuilt Apache HTTP components and use OkHttp library from Square using this HttpStack implementation for Volley that uses OkHttp as its transport. Works great.

like image 2
akent Avatar answered Nov 16 '22 18:11

akent


I know this question is old, but providing solution could help someone who is yet to encounter same challenge

JSONObject param = new JSONObject();
    try {
        param.put(KEY 1, VALUE 1);
        param.put(KEY 2, VALUE 2);

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

        //ID represents the id for the record to update
    JsonObjectRequest jsonObjReq = new JsonObjectRequest(Request.Method.PATCH, YOUR URL+ "?ID=" + ID, param,
            new Response.Listener<JSONObject>() {
                @Override
                public void onResponse(JSONObject response) {
                 //call handler to display response (optional)

                }
            }, new Response.ErrorListener() {

        @Override
        public void onErrorResponse(VolleyError error) {
            //call handler to display response (optional)

        }

    }) {

        //            Passing header
        @Override
        public Map<String, String> getHeaders() throws AuthFailureError {
            HashMap<String, String> headers = new HashMap<String, String>();
            headers.put("Content-Type", "application/json; charset=utf-8");
            headers.put("x-api-key", "YOUR API KEY");
            return headers;
        }

        @Override
        protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {

           //use this to check positive response code returned else remove this override part

            return super.parseNetworkResponse(response);
        }
    };
    //and then you pass json data to volley
    VolleySingleton.getInstance(getActivity()).addToRequestQueue(jsonObjReq);
like image 1
user2341596 Avatar answered Nov 16 '22 19:11

user2341596