Using cookies with Android volley library
Asked Answered
L

11

62

Does anybody know how to attach a session cookie to the request using com.android.volley library? When I log in to a web site it gives me a session cookie. Browser would send that cookie back with any subsequent request. Volley does not seem to do that, at least not automatically.

Thanks.

Lingcod answered 21/5, 2013 at 22:41 Comment(5)
That should be a function of the underlying HTTP transport layer (e.g., HttpUrlConnection, HttpClient), not Volley itself. Have you tried using one of those directly?Condyle
CommonsWare, I haven't. Isn't whole purpose of volley to hide that complexity? I went through volley choice and couldn't find any access to the underlying layer. Do you have any suggestions how to access the bottom layer? Thanks.Lingcod
"Isn't whole purpose of volley to hide that complexity?" -- as I wrote, AFAIK session cookies are handled by the HTTP transport code. My suggestion of trying one of those transports directly was to see if your problem persisted, in which case it is not Volley's problem, but something either in Android or in your server. "Do you have any suggestions how to access the bottom layer?" -- put Volley aside for the moment and write to the "bottom layer" yourself. HttpUrlConnection, in particular, has been around for ~15 years, and there are plenty of examples of how to use it.Condyle
Since cookie is just a usual http header, you can save this header using NetworkResponse.headers field and then attach it to every subsequent request by overriding Request.getHeaders methodHaver
For those following this thread, I have a follow up question. #18414346Benzvi
L
41

vmirinov is right!

Here is how I solved the problem:

Request class:

public class StringRequest extends com.android.volley.toolbox.StringRequest {

    private final Map<String, String> _params;

    /**
     * @param method
     * @param url
     * @param params
     *            A {@link HashMap} to post with the request. Null is allowed
     *            and indicates no parameters will be posted along with request.
     * @param listener
     * @param errorListener
     */
    public StringRequest(int method, String url, Map<String, String> params, Listener<String> listener,
            ErrorListener errorListener) {
        super(method, url, listener, errorListener);

        _params = params;
    }

    @Override
    protected Map<String, String> getParams() {
        return _params;
    }

    /* (non-Javadoc)
     * @see com.android.volley.toolbox.StringRequest#parseNetworkResponse(com.android.volley.NetworkResponse)
     */
    @Override
    protected Response<String> parseNetworkResponse(NetworkResponse response) {
        // since we don't know which of the two underlying network vehicles
        // will Volley use, we have to handle and store session cookies manually
        MyApp.get().checkSessionCookie(response.headers);

        return super.parseNetworkResponse(response);
    }

    /* (non-Javadoc)
     * @see com.android.volley.Request#getHeaders()
     */
    @Override
    public Map<String, String> getHeaders() throws AuthFailureError {
        Map<String, String> headers = super.getHeaders();

        if (headers == null
                || headers.equals(Collections.emptyMap())) {
            headers = new HashMap<String, String>();
        }

        MyApp.get().addSessionCookie(headers);

        return headers;
    }
}

and MyApp:

public class MyApp extends Application {
    private static final String SET_COOKIE_KEY = "Set-Cookie";
    private static final String COOKIE_KEY = "Cookie";
    private static final String SESSION_COOKIE = "sessionid";

    private static MyApp _instance;
  private RequestQueue _requestQueue;
  private SharedPreferences _preferences;

    public static MyApp get() {
        return _instance;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        _instance = this;
            _preferences = PreferenceManager.getDefaultSharedPreferences(this);
        _requestQueue = Volley.newRequestQueue(this);
    }

    public RequestQueue getRequestQueue() {
        return _requestQueue;
    }


    /**
     * Checks the response headers for session cookie and saves it
     * if it finds it.
     * @param headers Response Headers.
     */
    public final void checkSessionCookie(Map<String, String> headers) {
        if (headers.containsKey(SET_COOKIE_KEY)
                && headers.get(SET_COOKIE_KEY).startsWith(SESSION_COOKIE)) {
                String cookie = headers.get(SET_COOKIE_KEY);
                if (cookie.length() > 0) {
                    String[] splitCookie = cookie.split(";");
                    String[] splitSessionId = splitCookie[0].split("=");
                    cookie = splitSessionId[1];
                    Editor prefEditor = _preferences.edit();
                    prefEditor.putString(SESSION_COOKIE, cookie);
                    prefEditor.commit();
                }
            }
    }

    /**
     * Adds session cookie to headers if exists.
     * @param headers
     */
    public final void addSessionCookie(Map<String, String> headers) {
        String sessionId = _preferences.getString(SESSION_COOKIE, "");
        if (sessionId.length() > 0) {
            StringBuilder builder = new StringBuilder();
            builder.append(SESSION_COOKIE);
            builder.append("=");
            builder.append(sessionId);
            if (headers.containsKey(COOKIE_KEY)) {
                builder.append("; ");
                builder.append(headers.get(COOKIE_KEY));
            }
            headers.put(COOKIE_KEY, builder.toString());
        }
    }

}
Lingcod answered 24/5, 2013 at 21:1 Comment(8)
it should be noted, that the header could return as "Set-Cookie" as you have it, OR "set-cookie" - in which case this code would break.Defeatism
For those following the question, I have a follow up question. #18414346Benzvi
Also what is the advantages of storing cookie is shared preferences. Why not store it as static class member?Benzvi
@TestBest, storing in preferences makes the cookie to survive the app exit. Your choice if you want to session cookie to last longer then one app run.Lingcod
Note that if there's more than one cookie, there will be multiple 'Set-Cookie' headers and as response headers is a Map, you'll only get the first one! Yep, this Volley library is missing some things. Discussed over at this question: #18998861Espagnole
this is just for string request right? How about for JSON object & array request.Klarrisa
I have just changed the content of SESSION_COOKIE to be equal to "PHPSESSID" and its work.Doan
why do you use SharedPreferences to save cookie and not using CookieManager?Colombia
U
68

Volley doesn't actually make HTTP requests itself, and thus doesn't manage Cookies directly. It instead uses an instance of HttpStack to do this. There are two main implementations:

  • HurlStack: Uses HttpUrlConnection under the hood
  • HttpClientStack: uses Apache HttpClient under the hood

Cookie management is the responsibility of those HttpStacks. And they each handle Cookies differently.

If you need to support < 2.3, then you should use the HttpClientStack:

Configure an HttpClient instance, and pass that to Volley for it to use under the hood:

// If you need to directly manipulate cookies later on, hold onto this client
// object as it gives you access to the Cookie Store
DefaultHttpClient httpclient = new DefaultHttpClient();

CookieStore cookieStore = new BasicCookieStore();
httpclient.setCookieStore( cookieStore );

HttpStack httpStack = new HttpClientStack( httpclient );
RequestQueue requestQueue = Volley.newRequestQueue( context, httpStack  );

The advantage with this vs manually inserting cookies into the headers is that you get actual cookie management. Cookies in your store will properly respond to HTTP controls that expire or update them.

I've gone a step further and sub-classed BasicCookieStore so that I can automatically persist my cookies to disk.

HOWEVER! If you don't need to support older versions of Android. Just use this method:

// CookieStore is just an interface, you can implement it and do things like
// save the cookies to disk or what ever.
CookieStore cookieStore = new MyCookieStore();
CookieManager manager = new CookieManager( cookieStore, CookiePolicy.ACCEPT_ALL );
CookieHandler.setDefault( manager  );

// Optionally, you can just use the default CookieManager
CookieManager manager = new CookieManager();
CookieHandler.setDefault( manager  );

HttpURLConnection will query the CookieManager from that implicitly. HttpUrlConnection is also more performant and a bit cleaner to implement and work with IMO.

Unseat answered 21/1, 2014 at 23:40 Comment(3)
This worked for me. I wound up just using the default CookieManager. As a not, calling on CookieHandler.getDefault().put() requires a Map<String, List<String>>. The default implementation of put() parses the cookies out of the map and requires that the key is either (case insensitive) "set-cookie" or "set-cookie2". Had to examine the source to find that out.Nuno
@Adam, Thanks, I also used the default CookieManager, the headers are now correct. But when I redirect to another activity, those headers are not part of the request anymore even if I set cookiemanager onCreate of the activity, is there a way to make it persistent between activities?Hermetic
wish you had also shared with us, your MyCookieStore classDraftsman
L
41

vmirinov is right!

Here is how I solved the problem:

Request class:

public class StringRequest extends com.android.volley.toolbox.StringRequest {

    private final Map<String, String> _params;

    /**
     * @param method
     * @param url
     * @param params
     *            A {@link HashMap} to post with the request. Null is allowed
     *            and indicates no parameters will be posted along with request.
     * @param listener
     * @param errorListener
     */
    public StringRequest(int method, String url, Map<String, String> params, Listener<String> listener,
            ErrorListener errorListener) {
        super(method, url, listener, errorListener);

        _params = params;
    }

    @Override
    protected Map<String, String> getParams() {
        return _params;
    }

    /* (non-Javadoc)
     * @see com.android.volley.toolbox.StringRequest#parseNetworkResponse(com.android.volley.NetworkResponse)
     */
    @Override
    protected Response<String> parseNetworkResponse(NetworkResponse response) {
        // since we don't know which of the two underlying network vehicles
        // will Volley use, we have to handle and store session cookies manually
        MyApp.get().checkSessionCookie(response.headers);

        return super.parseNetworkResponse(response);
    }

    /* (non-Javadoc)
     * @see com.android.volley.Request#getHeaders()
     */
    @Override
    public Map<String, String> getHeaders() throws AuthFailureError {
        Map<String, String> headers = super.getHeaders();

        if (headers == null
                || headers.equals(Collections.emptyMap())) {
            headers = new HashMap<String, String>();
        }

        MyApp.get().addSessionCookie(headers);

        return headers;
    }
}

and MyApp:

public class MyApp extends Application {
    private static final String SET_COOKIE_KEY = "Set-Cookie";
    private static final String COOKIE_KEY = "Cookie";
    private static final String SESSION_COOKIE = "sessionid";

    private static MyApp _instance;
  private RequestQueue _requestQueue;
  private SharedPreferences _preferences;

    public static MyApp get() {
        return _instance;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        _instance = this;
            _preferences = PreferenceManager.getDefaultSharedPreferences(this);
        _requestQueue = Volley.newRequestQueue(this);
    }

    public RequestQueue getRequestQueue() {
        return _requestQueue;
    }


    /**
     * Checks the response headers for session cookie and saves it
     * if it finds it.
     * @param headers Response Headers.
     */
    public final void checkSessionCookie(Map<String, String> headers) {
        if (headers.containsKey(SET_COOKIE_KEY)
                && headers.get(SET_COOKIE_KEY).startsWith(SESSION_COOKIE)) {
                String cookie = headers.get(SET_COOKIE_KEY);
                if (cookie.length() > 0) {
                    String[] splitCookie = cookie.split(";");
                    String[] splitSessionId = splitCookie[0].split("=");
                    cookie = splitSessionId[1];
                    Editor prefEditor = _preferences.edit();
                    prefEditor.putString(SESSION_COOKIE, cookie);
                    prefEditor.commit();
                }
            }
    }

    /**
     * Adds session cookie to headers if exists.
     * @param headers
     */
    public final void addSessionCookie(Map<String, String> headers) {
        String sessionId = _preferences.getString(SESSION_COOKIE, "");
        if (sessionId.length() > 0) {
            StringBuilder builder = new StringBuilder();
            builder.append(SESSION_COOKIE);
            builder.append("=");
            builder.append(sessionId);
            if (headers.containsKey(COOKIE_KEY)) {
                builder.append("; ");
                builder.append(headers.get(COOKIE_KEY));
            }
            headers.put(COOKIE_KEY, builder.toString());
        }
    }

}
Lingcod answered 24/5, 2013 at 21:1 Comment(8)
it should be noted, that the header could return as "Set-Cookie" as you have it, OR "set-cookie" - in which case this code would break.Defeatism
For those following the question, I have a follow up question. #18414346Benzvi
Also what is the advantages of storing cookie is shared preferences. Why not store it as static class member?Benzvi
@TestBest, storing in preferences makes the cookie to survive the app exit. Your choice if you want to session cookie to last longer then one app run.Lingcod
Note that if there's more than one cookie, there will be multiple 'Set-Cookie' headers and as response headers is a Map, you'll only get the first one! Yep, this Volley library is missing some things. Discussed over at this question: #18998861Espagnole
this is just for string request right? How about for JSON object & array request.Klarrisa
I have just changed the content of SESSION_COOKIE to be equal to "PHPSESSID" and its work.Doan
why do you use SharedPreferences to save cookie and not using CookieManager?Colombia
C
20

The default HTTP transport code for Volley is HttpUrlConnection. If I am reading the documentation correctly, you need to opt into automatic session cookie support:

CookieManager cookieManager = new CookieManager();
CookieHandler.setDefault(cookieManager);

See also Should HttpURLConnection with CookieManager automatically handle session cookies?

Condyle answered 21/5, 2013 at 23:6 Comment(7)
The default transport implementation actually depends on the android version of the particular device (HttpUrlConnection for Gingerbread and above and HttpClient for the previous versions) and you shouldn't rely on itHaver
That's what my understanding was. Commons Ware's answer is correct in case volley opts for HttpUrlConnection. That's why I would expect volley handle it. I'm away from computer for couple days. I'll check if some inheritance can help.Lingcod
Note the default transport is specified in the Volley helper class, android.googlesource.com/platform/frameworks/volley/+/master/… , if you don't explicitly pass a HttpStack to newRequestQueue. Would be simple to write your own version of that newRequestQueue helper method that does the right thing regardless of whether a HurlStack or a HttpClientStack is used.Salesin
Ok. So is there a way to opt in to session cookies for both types of clients HttpURLConnection and HttpClient? See my follow up question: #18414346Benzvi
The percentage of Android users below Gingerbread is currently 2.2%. I personally don't care about them. So I agree with CommonsWare and just added CookieHandler.setDefault(new CookieManager( null, CookiePolicy.ACCEPT_ALL ) ); in the HurlStack constructor. Works great.Espagnole
login web service call its working after login next subsequence web service call i want to send the session id to the server using volley library how can i able to send ?? could you help meMohandas
@kuldeep add this CookieHandler.setDefault(new CookieManager( null, CookiePolicy.ACCEPT_ALL ) ); in your application class.. it will work !Ending
N
16

Guys try this in onCreate method of your AppController.java

  CookieHandler.setDefault(new CookieManager());

Hope it'll save time of developers. I have wasted four hours in debugging and searching appropriate solution.

Nigrify answered 22/11, 2016 at 20:3 Comment(0)
G
10

@Rastio solution doesn't work if there are multiple 'Set-Cookie' headers. I wrapped the default CookieManager cookie store and before adding a cookie I saved it in SharedPreferences using Gson to serialize the cookie.

This is an example of the cookie store wrapper:

import android.content.Context;
import android.net.Uri;
import android.util.Log;

import com.google.gson.Gson;

import java.net.CookieManager;
import java.net.CookieStore;
import java.net.HttpCookie;
import java.net.URI;
import java.util.List;

/**
 * Class that implements CookieStore interface. This class saves to SharedPreferences the session
 * cookie.
 *
 * Created by lukas.
 */
public class PersistentCookieStore implements CookieStore {

    private CookieStore mStore;
    private Context mContext;
    private Gson mGson;

    public PersistentCookieStore(Context context) {
        // prevent context leaking by getting the application context
        mContext = context.getApplicationContext();
        mGson = new Gson();

        // get the default in memory store and if there is a cookie stored in shared preferences,
        // we added it to the cookie store
        mStore = new CookieManager().getCookieStore();
        String jsonSessionCookie = Prefs.getJsonSessionCookie(mContext);
        if (!jsonSessionCookie.equals(Prefs.DEFAULT_STRING)) {
            HttpCookie cookie = mGson.fromJson(jsonSessionCookie, HttpCookie.class);
            mStore.add(URI.create(cookie.getDomain()), cookie);
        }
    }

    @Override
    public void add(URI uri, HttpCookie cookie) {
        if (cookie.getName().equals("sessionid")) {
            // if the cookie that the cookie store attempt to add is a session cookie,
            // we remove the older cookie and save the new one in shared preferences
            remove(URI.create(cookie.getDomain()), cookie);
            Prefs.saveJsonSessionCookie(mContext, mGson.toJson(cookie));
        }

        mStore.add(URI.create(cookie.getDomain()), cookie);
    }

    @Override
    public List<HttpCookie> get(URI uri) {
        return mStore.get(uri);
    }

    @Override
    public List<HttpCookie> getCookies() {
        return mStore.getCookies();
    }

    @Override
    public List<URI> getURIs() {
        return mStore.getURIs();
    }

    @Override
    public boolean remove(URI uri, HttpCookie cookie) {
        return mStore.remove(uri, cookie);
    }

    @Override
    public boolean removeAll() {
        return mStore.removeAll();
    }
}

Then, to use the cookie store just set in the CookieManager and that's it!

CookieManager cookieManager = new CookieManager(new PersistentCookieStore(mContext),
    CookiePolicy.ACCEPT_ORIGINAL_SERVER);
CookieHandler.setDefault(cookieManager);
Gerdes answered 19/8, 2014 at 16:51 Comment(2)
In the constructor of PersistentCookieStore, I think we need to get a list of cookies stored (not only one cookie) from SharedPreferences. What do you think? Also, we need to update SharedPreferences everytime we update mStore too.Marou
@Gerdes Can you elaborate your example? I mean how are you saving these multiple Cookies in shared pref?Municipal
S
5

I know the post and a little old, but we went through this recent problem, we need to share the session of a logged User between servers, and server side solution began to require a value to be provided by the client side, through cookie. One solution we found was to add a parameter to RequestQueue object, the code snippet in the method getRequestQueue before instantiating the RequestQueue found on the link below, and solve the problem, not sure how, but it started to work.

Visit http://woxiangbo.iteye.com/blog/1769122

public class App extends Application {

    public static final String TAG = App.class.getSimpleName();

    private static App         mInstance;

    public static synchronized App getInstance() {
        return App.mInstance;
    }

    private RequestQueue mRequestQueue;

    public <T> void addToRequestQueue( final Request<T> req ) {
        req.setTag( App.TAG );
        this.getRequestQueue().add( req );
    }

    public <T> void addToRequestQueue( final Request<T> req, final String tag ) {
        req.setTag( TextUtils.isEmpty( tag ) ? App.TAG : tag );
        this.getRequestQueue().add( req );
    }

    public void cancelPendingRequests( final Object tag ) {
        if ( this.mRequestQueue != null ) {
            this.mRequestQueue.cancelAll( tag );
        }
    }

    public RequestQueue getRequestQueue() {

        if ( this.mRequestQueue == null ) {


            DefaultHttpClient mDefaultHttpClient = new DefaultHttpClient();

            final ClientConnectionManager mClientConnectionManager = mDefaultHttpClient.getConnectionManager();
            final HttpParams mHttpParams = mDefaultHttpClient.getParams();
            final ThreadSafeClientConnManager mThreadSafeClientConnManager = new ThreadSafeClientConnManager( mHttpParams, mClientConnectionManager.getSchemeRegistry() );

            mDefaultHttpClient = new DefaultHttpClient( mThreadSafeClientConnManager, mHttpParams );

            final HttpStack httpStack = new HttpClientStack( mDefaultHttpClient );

            this.mRequestQueue = Volley.newRequestQueue( this.getApplicationContext(), httpStack );
        }

        return this.mRequestQueue;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        App.mInstance = this;
    }
}

//set token value

ObjectRequest.setHeader( "Cookie", "JSESSIONID=" + tokenValueHere );
Spatterdash answered 14/7, 2014 at 12:14 Comment(2)
Where are you adding cookies ?Paediatrician
Thank you very much ObjectRequest.setHeader( "Cookie", "JSESSIONID=" + tokenValueHere ); this line saved my day@AndersonKGuard
S
2

Use this method to use Volley with cookies to:

  1. Use only very well tested code licenced under Apache 2 license
  2. Make as many requests as you'd like at the same time
  3. Make sure cookies persist on the device
  4. Not having to reinvent the wheel

My server uses cookies to authenticate and obviously I wanted to ensure that cookies persist on the device. So my solution was to use PersistentCookieStore and SerializableCookie classes from Asynchronous Http Client for Android.

First, in order to enable concurrent requests, an Apache HttpClient v4.3 port for Android is needed - one that comes with the system is outdated. More info here. I use Gradle, so this is how I imported it:

dependencies {
    compile group: 'org.apache.httpcomponents' , name: 'httpclient-android' , version: '4.3.3'
}

Function to get RequestQueue (in my class that extends Application):

private RequestQueue mRequestQueue;
private CloseableHttpClient httpClient;

...

public RequestQueue getRequestQueue() {
    if (mRequestQueue == null) {
        httpClient = HttpClients.custom()
            .setConnectionManager(new PoolingHttpClientConnectionManager())
            .setDefaultCookieStore(new PersistentCookieStore(getApplicationContext()))
            .build();
        mRequestQueue = Volley.newRequestQueue(getApplicationContext(), new HttpClientStack(httpClient));
    }
    return mRequestQueue;
}

This is how I queue up a Request

public <T> void addToRequestQueue(Request<T> req, String tag) {
    req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
    getRequestQueue().add(req);
}

That's it!

Stagnant answered 31/7, 2014 at 20:55 Comment(2)
setDefaultCookieStore cannot take PersistentCookieStore as a parameterDekaliter
Not sure what you mean - that's the exact code I was running. It was working well. This answer might pertain to earlier versions of Volley thoughStagnant
M
2

gingerbread+ Android Versions:

there is another easy way to maintain cookies session and that is to add this line in a class that is extended with APPLICATION class:

CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL));
Mosqueda answered 27/11, 2018 at 6:31 Comment(0)
D
1

If you have already started implementing your application by using the Loopj library, you will notice that you can't use new HttpClient instance in Volley.newRequestQUeue() because you will get various errors about not closing your previous connection etc.

Errors like:

java.lang.IllegalStateException: No wrapped connection

Invalid use of SingleClientConnManager: connection still allocated.

Now sometimes it takes time to refactor all your old API calls and rewrite them using volley, but you can use volley and loopj at the same time and share a cookiestore between those two until you write everything in volley (use volley instead of loopj, it is much better :) ).

This is how you can share HttpClient and CookieStore from loopj with volley.

// For example you initialize loopj first
private static AsyncHttpClient client = new AsyncHttpClient();
sCookieStore = new PersistentCookieStore(getSomeContextHere());
client.setTimeout(DEFAULT_TIMEOUT);
client.setMaxConnections(12);
client.setCookieStore(sCookieStore);
client.setThreadPool(((ThreadPoolExecutor) Executors.newCachedThreadPool()));

public static RequestQueue getRequestQueue(){
    if(mRequestQueue == null){

    HttpClient httpclient = KkstrRestClient.getClient().getHttpClient();

    ((AbstractHttpClient) httpclient).setCookieStore( ApplicationController.getCookieStore() );

    HttpStack httpStack = new HttpClientStack(httpclient);

    mRequestQueue = Volley.newRequestQueue(getContext(), httpStack);
    }

    return mRequestQueue;
}

This happened to me, we started using loopj. After 50 000 lines of code and discovery that loopj doesn't always work like expected we decided to switch to Volley.

Day answered 8/4, 2014 at 8:25 Comment(1)
cheers, you set me on the right path to fix an infuriating bug. i use both loopj and volley in my app and need both (don't ask!). your exact answer didn't work but this one did, same idea, just getting a threadsafe DefaultHttpClient for Volley cookies: https://mcmap.net/q/190168/-exception-using-httprequest-execute-invalid-use-of-singleclientconnmanager-connection-still-allocatedEspagnole
O
0

The answer of @CommonsWare is the one I'd use. However, it looks like KitKat is having some bugs when that's done (When you create a CookieManager with custom CookieStore which you need if you want persistent Cookies). Given the fact that regardless of the implementation of the CookieStore that is used, Volley would throw a NullpointerException, I had to create my own CookieHandler... use it if you find it helpful.

public class MyCookieHandler extends CookieHandler {

private static final String VERSION_ZERO_HEADER = "Set-cookie";

private static final String VERSION_ONE_HEADER = "Set-cookie2";
private static final String COOKIE_HEADER = "Cookie";

private static final String COOKIE_FILE = "Cookies";
private Map<String, Map<String, HttpCookie>> urisMap;

private Context context;

public MyCookieHandler(Context context) {

    this.context = context;
    loadCookies();

}

@SuppressWarnings("unchecked")
private void loadCookies() {
    File file = context.getFileStreamPath(COOKIE_FILE);
    if (file.exists())
        try {

            FileInputStream fis = context.openFileInput(COOKIE_FILE);
            BufferedReader br = new BufferedReader(new InputStreamReader(
                    fis));
            String line = br.readLine();
            StringBuilder sb = new StringBuilder();
            while (line != null) {
                sb.append(line);
                line = br.readLine();
            }
            Log.d("MyCookieHandler.loadCookies", sb.toString());
            JSONObject jsonuris = new JSONObject(sb.toString());
            urisMap = new HashMap<String, Map<String, HttpCookie>>();
            Iterator<String> jsonurisiter = jsonuris.keys();

            while (jsonurisiter.hasNext()) {
                String prop = jsonurisiter.next();
                HashMap<String, HttpCookie> cookiesMap = new HashMap<String, HttpCookie>();
                JSONObject jsoncookies = jsonuris.getJSONObject(prop);
                Iterator<String> jsoncookiesiter = jsoncookies.keys();
                while (jsoncookiesiter.hasNext()) {
                    String pprop = jsoncookiesiter.next();
                    cookiesMap.put(pprop,
                            jsonToCookie(jsoncookies.getJSONObject(pprop)));
                }
                urisMap.put(prop, cookiesMap);

            }

        } catch (Exception e) {

            e.printStackTrace();
        }
    else {
        urisMap = new HashMap<String, Map<String, HttpCookie>>();
    }
}

@Override
public Map<String, List<String>> get(URI arg0,
        Map<String, List<String>> arg1) throws IOException {
    Log.d("MyCookieHandler.get",
            "getting Cookies for domain: " + arg0.getHost());
    Map<String, HttpCookie> cookies = urisMap.get(arg0.getHost());
    if (cookies != null)
        for (Entry<String, HttpCookie> cookie : cookies.entrySet()) {
            if (cookie.getValue().hasExpired()) {
                cookies.remove(cookie.getKey());
            }
        }

    if (cookies == null || cookies.isEmpty()) {
        Log.d("MyCookieHandler.get", "======");
        return Collections.emptyMap();
    }
    Log.d("MyCookieHandler.get",
            "Cookie : " + TextUtils.join("; ", cookies.values()));
    Log.d("MyCookieHandler.get", "======");
    return Collections.singletonMap(COOKIE_HEADER, Collections
            .singletonList(TextUtils.join("; ", cookies.values())));
}

@Override
public void put(URI uri, Map<String, List<String>> arg1) throws IOException {
    Map<String, HttpCookie> cookies = parseCookies(arg1);
    Log.d("MyCookieHandler.put",
            "saving Cookies for domain: " + uri.getHost());

    addCookies(uri, cookies);
    Log.d("MyCookieHandler.put",
            "Cookie : " + TextUtils.join("; ", cookies.values()));
    Log.d("MyCookieHandler.put", "======");

}

private void addCookies(URI uri, Map<String, HttpCookie> cookies) {
    if (!cookies.isEmpty()) {
        if (urisMap.get(uri.getHost()) == null) {
            urisMap.put(uri.getHost(), cookies);
        } else {
            urisMap.get(uri.getHost()).putAll(cookies);
        }
        saveCookies();
    }
}

private void saveCookies() {
    try {
        FileOutputStream fos = context.openFileOutput(COOKIE_FILE,
                Context.MODE_PRIVATE);

        JSONObject jsonuris = new JSONObject();
        for (Entry<String, Map<String, HttpCookie>> uris : urisMap
                .entrySet()) {
            JSONObject jsoncookies = new JSONObject();
            for (Entry<String, HttpCookie> savedCookies : uris.getValue()
                    .entrySet()) {
                jsoncookies.put(savedCookies.getKey(),
                        cookieToJson(savedCookies.getValue()));
            }
            jsonuris.put(uris.getKey(), jsoncookies);
        }
        fos.write(jsonuris.toString().getBytes());
        fos.close();
        Log.d("MyCookieHandler.addCookies", jsonuris.toString());
    } catch (Exception e) {
        e.printStackTrace();
    }
}

private static JSONObject cookieToJson(HttpCookie cookie) {
    JSONObject jsoncookie = new JSONObject();
    try {
        jsoncookie.put("discard", cookie.getDiscard());
        jsoncookie.put("maxAge", cookie.getMaxAge());
        jsoncookie.put("secure", cookie.getSecure());
        jsoncookie.put("version", cookie.getVersion());
        jsoncookie.put("comment", cookie.getComment());
        jsoncookie.put("commentURL", cookie.getCommentURL());
        jsoncookie.put("domain", cookie.getDomain());
        jsoncookie.put("name", cookie.getName());
        jsoncookie.put("path", cookie.getPath());
        jsoncookie.put("portlist", cookie.getPortlist());
        jsoncookie.put("value", cookie.getValue());

    } catch (JSONException e) {

        e.printStackTrace();
    }

    return jsoncookie;
}

private static HttpCookie jsonToCookie(JSONObject jsonObject) {
    HttpCookie httpCookie;
    try {
        httpCookie = new HttpCookie(jsonObject.getString("name"),
                jsonObject.getString("value"));
        if (jsonObject.has("comment"))
            httpCookie.setComment(jsonObject.getString("comment"));
        if (jsonObject.has("commentURL"))
            httpCookie.setCommentURL(jsonObject.getString("commentURL"));
        if (jsonObject.has("discard"))
            httpCookie.setDiscard(jsonObject.getBoolean("discard"));
        if (jsonObject.has("domain"))
            httpCookie.setDomain(jsonObject.getString("domain"));
        if (jsonObject.has("maxAge"))
            httpCookie.setMaxAge(jsonObject.getLong("maxAge"));
        if (jsonObject.has("path"))
            httpCookie.setPath(jsonObject.getString("path"));
        if (jsonObject.has("portlist"))
            httpCookie.setPortlist(jsonObject.getString("portlist"));
        if (jsonObject.has("secure"))
            httpCookie.setSecure(jsonObject.getBoolean("secure"));
        if (jsonObject.has("version"))
            httpCookie.setVersion(jsonObject.getInt("version"));
        return httpCookie;
    } catch (JSONException e) {

        e.printStackTrace();
    }
    return null;

}

private Map<String, HttpCookie> parseCookies(Map<String, List<String>> map) {
    Map<String, HttpCookie> response = new HashMap<String, HttpCookie>();

    for (Entry<String, List<String>> e : map.entrySet()) {
        String key = e.getKey();
        if (key != null
                && (key.equalsIgnoreCase(VERSION_ONE_HEADER) || key
                        .equalsIgnoreCase(VERSION_ZERO_HEADER))) {
            for (String cookie : e.getValue()) {
                try {
                    for (HttpCookie htpc : HttpCookie.parse(cookie)) {
                        response.put(htpc.getName(), htpc);
                    }
                } catch (Exception e1) {

                    Log.e("MyCookieHandler.parseCookies",
                            "Error parsing cookies", e1);
                }
            }

        }
    }
    return response;

}
}

This answer hasn't been thoroughly tested. I used JSON to serialize Cookies, because well, that class don't implement Serializable and it's final.

Ovoviviparous answered 28/7, 2014 at 1:10 Comment(0)
B
0

In my project CookieManager is resolved as android.webkit.CookieManager. I have to set the handler like this below to make Volley auto handle cookies.

CookieManager cookieManager = new java.net.CookieManager(); CookieHandler.setDefault(cookieManager);

Breadwinner answered 6/12, 2018 at 6:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.