Tienda de cookies persistente con okhttp 2 en Android
En mi aplicación de Android, estoy tratando de cambiar de android-async-http
a okhttp
que soporta async networking sind versión 2.0. Mientras que el primero se envía con una implementación de un almacenamiento de cookies persistente , no sé cómo implementar algo como esto para okhttp
.
En mi aplicación tengo una rutina de inicio de sesión durante la cual se envía una solicitud de get
que, si tiene éxito, debe establecer una cookie. Esta cookie se debe anexar a todas las solicitudes de red subsiguientes y debe sobrevivir a los reinicios de la aplicación.
- ¿Qué causa "java.io.IOException: se restableció el flujo: CANCELAR" con okhttp y spdy?
- Android Habilitar TLSv1.2 en OKHttp
- Android OkHttp, actualizar símbolo expirado
- Okhttp NoClassDefFoundError
- Respuesta de lectura por push de servidor con OkHttp
Encontré la siguiente respuesta en SO, indicando que el siguiente fragmento de código, si se ejecuta en algún lugar de la aplicación, activa la gestión persistente de cookies y el okhttp
lo usaría:
CookieManager cookieManager = new CookieManager(); cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL); CookieHandler.setDefault(cookieManager);
Sin embargo, para mí no funciona. ¿Es el CookieManager
el camino correcto? ¿Cómo puedo supervisar qué cookies se han configurado y almacenado para depurar el problema?
- Cargue archivos en el cubo AWS S3 usando OkHttp o Retrofit
- Picasso detectó un OkHttp no admitido en el classpath
- OkHTTP Actualiza la interfaz de usuario desde la devolución de llamada en cola
- Hacer la solicitud HTTP usando la biblioteca OkHttp dentro de doInBackground () de AsyncTask bloquea la operación de la interfaz de usuario
- Volley + OkHttp en Android da error en la respuesta de estado 200
- El uso de la dirección IP para el nombre común en el certificado del servidor no funciona en Android?
- ¿Cuál es la razón de este error? Java.io.IOException: Content-Length y stream length disagree
- Diferencia entre okhttp y httpurlconnection?
Puede configurar su CookieStore en el cliente OkHttp con el siguiente código:
OkHttpClient client = new OkHttpClient(); client.setCookieHandler(new CookieManager( new PersistentCookieStore(getApplicationContext()), CookiePolicy.ACCEPT_ALL));
Hice una idea con mi implementación de una persistente CookieStore tratando de mejorar la respuesta de janoliver en dos puntos:
- En base a esta respuesta, el URI no debe utilizarse si los atributos de dominio y ruta de las cookies están disponibles.
- En la clase SerializableHttpCookie, el atributo HTTPOnly de HttpCookie no está serializado (no tiene accesores ni mutadores). Mi solución fue usar la reflexión para poder acceder a este atributo privado.
Poner el código en la función onCreate
una Application
personalizada resuelve el problema. Ahora funciona.
public class MyApplication extends Application { public void onCreate() { super.onCreate(); // enable cookies java.net.CookieManager cookieManager = new java.net.CookieManager(); cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL); CookieHandler.setDefault(cookieManager); } }
Me las arreglé para obtener cookies persistentes con okhttp
con la CookieStore
siguiente, que se copió parcialmente de la de android-async-http
. Funciona con API lvl 9 y tal vez menos.
import android.content.Context; import android.content.SharedPreferences; import android.text.TextUtils; import android.util.Log; import java.io.*; import java.net.CookieStore; import java.net.HttpCookie; import java.net.URI; import java.net.URISyntaxException; import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** * A persistent cookie store which implements the Apache HttpClient CookieStore interface. * Cookies are stored and will persist on the user's device between application sessions since they * are serialized and stored in SharedPreferences. Instances of this class are * designed to be used with AsyncHttpClient#setCookieStore, but can also be used with a * regular old apache HttpClient/HttpContext if you prefer. */ public class PersistentCookieStore implements CookieStore { private static final String LOG_TAG = "PersistentCookieStore"; private static final String COOKIE_PREFS = "CookiePrefsFile"; private static final String COOKIE_NAME_PREFIX = "cookie_"; private final HashMap<String, ConcurrentHashMap<String, HttpCookie>> cookies; private final SharedPreferences cookiePrefs; /** * Construct a persistent cookie store. * * @param context Context to attach cookie store to */ public PersistentCookieStore(Context context) { cookiePrefs = context.getSharedPreferences(COOKIE_PREFS, 0); cookies = new HashMap<String, ConcurrentHashMap<String, HttpCookie>>(); // Load any previously stored cookies into the store Map<String, ?> prefsMap = cookiePrefs.getAll(); for(Map.Entry<String, ?> entry : prefsMap.entrySet()) { if (((String)entry.getValue()) != null && !((String)entry.getValue()).startsWith(COOKIE_NAME_PREFIX)) { String[] cookieNames = TextUtils.split((String)entry.getValue(), ","); for (String name : cookieNames) { String encodedCookie = cookiePrefs.getString(COOKIE_NAME_PREFIX + name, null); if (encodedCookie != null) { HttpCookie decodedCookie = decodeCookie(encodedCookie); if (decodedCookie != null) { if(!cookies.containsKey(entry.getKey())) cookies.put(entry.getKey(), new ConcurrentHashMap<String, HttpCookie>()); cookies.get(entry.getKey()).put(name, decodedCookie); } } } } } } @Override public void add(URI uri, HttpCookie cookie) { String name = getCookieToken(uri, cookie); // Save cookie into local store, or remove if expired if (!cookie.hasExpired()) { if(!cookies.containsKey(uri.getHost())) cookies.put(uri.getHost(), new ConcurrentHashMap<String, HttpCookie>()); cookies.get(uri.getHost()).put(name, cookie); } else { if(cookies.containsKey(uri.toString())) cookies.get(uri.getHost()).remove(name); } // Save cookie into persistent store SharedPreferences.Editor prefsWriter = cookiePrefs.edit(); prefsWriter.putString(uri.getHost(), TextUtils.join(",", cookies.get(uri.getHost()).keySet())); prefsWriter.putString(COOKIE_NAME_PREFIX + name, encodeCookie(new SerializableHttpCookie(cookie))); prefsWriter.commit(); } protected String getCookieToken(URI uri, HttpCookie cookie) { return cookie.getName() + cookie.getDomain(); } @Override public List<HttpCookie> get(URI uri) { ArrayList<HttpCookie> ret = new ArrayList<HttpCookie>(); if(cookies.containsKey(uri.getHost())) ret.addAll(cookies.get(uri.getHost()).values()); return ret; } @Override public boolean removeAll() { SharedPreferences.Editor prefsWriter = cookiePrefs.edit(); prefsWriter.clear(); prefsWriter.commit(); cookies.clear(); return true; } @Override public boolean remove(URI uri, HttpCookie cookie) { String name = getCookieToken(uri, cookie); if(cookies.containsKey(uri.getHost()) && cookies.get(uri.getHost()).containsKey(name)) { cookies.get(uri.getHost()).remove(name); SharedPreferences.Editor prefsWriter = cookiePrefs.edit(); if(cookiePrefs.contains(COOKIE_NAME_PREFIX + name)) { prefsWriter.remove(COOKIE_NAME_PREFIX + name); } prefsWriter.putString(uri.getHost(), TextUtils.join(",", cookies.get(uri.getHost()).keySet())); prefsWriter.commit(); return true; } else { return false; } } @Override public List<HttpCookie> getCookies() { ArrayList<HttpCookie> ret = new ArrayList<HttpCookie>(); for (String key : cookies.keySet()) ret.addAll(cookies.get(key).values()); return ret; } @Override public List<URI> getURIs() { ArrayList<URI> ret = new ArrayList<URI>(); for (String key : cookies.keySet()) try { ret.add(new URI(key)); } catch (URISyntaxException e) { e.printStackTrace(); } return ret; } /** * Serializes Cookie object into String * * @param cookie cookie to be encoded, can be null * @return cookie encoded as String */ protected String encodeCookie(SerializableHttpCookie cookie) { if (cookie == null) return null; ByteArrayOutputStream os = new ByteArrayOutputStream(); try { ObjectOutputStream outputStream = new ObjectOutputStream(os); outputStream.writeObject(cookie); } catch (IOException e) { Log.d(LOG_TAG, "IOException in encodeCookie", e); return null; } return byteArrayToHexString(os.toByteArray()); } /** * Returns cookie decoded from cookie string * * @param cookieString string of cookie as returned from http request * @return decoded cookie or null if exception occured */ protected HttpCookie decodeCookie(String cookieString) { byte[] bytes = hexStringToByteArray(cookieString); ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); HttpCookie cookie = null; try { ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); cookie = ((SerializableHttpCookie) objectInputStream.readObject()).getCookie(); } catch (IOException e) { Log.d(LOG_TAG, "IOException in decodeCookie", e); } catch (ClassNotFoundException e) { Log.d(LOG_TAG, "ClassNotFoundException in decodeCookie", e); } return cookie; } /** * Using some super basic byte array <-> hex conversions so we don't have to rely on any * large Base64 libraries. Can be overridden if you like! * * @param bytes byte array to be converted * @return string containing hex values */ protected String byteArrayToHexString(byte[] bytes) { StringBuilder sb = new StringBuilder(bytes.length * 2); for (byte element : bytes) { int v = element & 0xff; if (v < 16) { sb.append('0'); } sb.append(Integer.toHexString(v)); } return sb.toString().toUpperCase(Locale.US); } /** * Converts hex values from strings to byte arra * * @param hexString string of hex-encoded values * @return decoded byte array */ protected byte[] hexStringToByteArray(String hexString) { int len = hexString.length(); byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character.digit(hexString.charAt(i + 1), 16)); } return data; } }
El SerializableHttpCookie.java
:
import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.net.HttpCookie; public class SerializableHttpCookie implements Serializable { private static final long serialVersionUID = 6374381323722046732L; private transient final HttpCookie cookie; private transient HttpCookie clientCookie; public SerializableHttpCookie(HttpCookie cookie) { this.cookie = cookie; } public HttpCookie getCookie() { HttpCookie bestCookie = cookie; if (clientCookie != null) { bestCookie = clientCookie; } return bestCookie; } private void writeObject(ObjectOutputStream out) throws IOException { out.writeObject(cookie.getName()); out.writeObject(cookie.getValue()); out.writeObject(cookie.getComment()); out.writeObject(cookie.getCommentURL()); out.writeObject(cookie.getDomain()); out.writeLong(cookie.getMaxAge()); out.writeObject(cookie.getPath()); out.writeObject(cookie.getPortlist()); out.writeInt(cookie.getVersion()); out.writeBoolean(cookie.getSecure()); out.writeBoolean(cookie.getDiscard()); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { String name = (String) in.readObject(); String value = (String) in.readObject(); clientCookie = new HttpCookie(name, value); clientCookie.setComment((String) in.readObject()); clientCookie.setCommentURL((String) in.readObject()); clientCookie.setDomain((String) in.readObject()); clientCookie.setMaxAge(in.readLong()); clientCookie.setPath((String) in.readObject()); clientCookie.setPortlist((String) in.readObject()); clientCookie.setVersion(in.readInt()); clientCookie.setSecure(in.readBoolean()); clientCookie.setDiscard(in.readBoolean()); } }
- ¿Es posible crear múltiples PendingIntents con el mismo requestCode y diferentes extras?
- No se permite cargar recursos locales: file: /// android_asset phonegap