日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

OkHttp3之Cookies管理及持久化

 小飛苑 2017-02-24

okHttp3正式版剛發(fā)布了沒幾天,正好重構(gòu)之前的代碼,于是第一時(shí)間入坑了。對(duì)okHttp3的一些改變,會(huì)陸續(xù)寫下來,這是第一篇cookies管理及持久化。

Cookies管理

OkHttp的源碼過于復(fù)雜,感興趣的同學(xué)可以自行閱讀,這里只針對(duì)HttpEngineer類進(jìn)行分析,從字面意思即可看出這個(gè)類負(fù)責(zé)http請(qǐng)求的request、response等等操作的處理,而cookies管理也是隨著http請(qǐng)求的request、response來處理。

3.0之前

先看NetworkRequest方法,在里面通過client.getCookieHandler()函數(shù)獲得了CookieHandler對(duì)象,通過該對(duì)象拿到cookie并設(shè)置到請(qǐng)求頭里,請(qǐng)求結(jié)束后取得響應(yīng)后通過networkResponse.headers()函數(shù)將請(qǐng)求頭獲得傳入receiveHeaders函數(shù),并將取得的cookie存入getCookieHandler得到的一個(gè)CookieHandler對(duì)象中去

private Request networkRequest(Request request) throws IOException {
  Request.Builder result = request.newBuilder();

  //例行省略....

  CookieHandler cookieHandler = client.getCookieHandler();
  if (cookieHandler != null) {
    // Capture the request headers added so far so that they can be offered to the CookieHandler.
    // This is mostly to stay close to the RI; it is unlikely any of the headers above would
    // affect cookie choice besides "Host".
    Map<String, List<String>> headers = OkHeaders.toMultimap(result.build().headers(), null);

    Map<String, List<String>> cookies = cookieHandler.get(request.uri(), headers);

    // Add any new cookies to the request.
    OkHeaders.addCookies(result, cookies);
  }

  //例行省略....

  return result.build();
}
public void readResponse() throws IOException {
  //例行省略....

  receiveHeaders(networkResponse.headers());

  //例行省略....
}
public void receiveHeaders(Headers headers) throws IOException {
  CookieHandler cookieHandler = client.getCookieHandler();
  if (cookieHandler != null) {
    cookieHandler.put(userRequest.uri(), OkHeaders.toMultimap(headers, null));
  }
}

CookieHandler對(duì)象是OkHttpClient類中的一個(gè)屬性,傳入了這個(gè)對(duì)象,那么OkHttp就會(huì)對(duì)cookie進(jìn)行自動(dòng)管理

private CookieHandler cookieHandler;
public OkHttpClient setCookieHandler(CookieHandler cookieHandler) {
  this.cookieHandler = cookieHandler;
  return this;
}

public CookieHandler getCookieHandler() {
  return cookieHandler;
}
OkHttpClient client = new OkHttpClient();
client.setCookieHandler(CookieHandler cookieHanlder);

3.0之后

而在OkHttp3中,對(duì)cookie而言,新增了兩個(gè)類Cookiejar、Cookie兩個(gè)類,在了解這兩個(gè)類之前,先去看一下HttpEngine關(guān)于cookie管理的變化

private Request networkRequest(Request request) throws IOException {
    Request.Builder result = request.newBuilder();

    //例行省略....
    
    List<Cookie> cookies = client.cookieJar().loadForRequest(request.url());
    if (!cookies.isEmpty()) {
      result.header("Cookie", cookieHeader(cookies));
    }

    //例行省略....

    return result.build();
  }
private String cookieHeader(List<Cookie> cookies) {
    StringBuilder cookieHeader = new StringBuilder();
    for (int i = 0, size = cookies.size(); i < size; i++) {
      if (i > 0) {
        cookieHeader.append("; ");
      }
      Cookie cookie = cookies.get(i);
      cookieHeader.append(cookie.name()).append('=').append(cookie.value());
    }
    return cookieHeader.toString();
  }
public void receiveHeaders(Headers headers) throws IOException {
    if (client.cookieJar() == CookieJar.NO_COOKIES) return;

    List<Cookie> cookies = Cookie.parseAll(userRequest.url(), headers);
    if (cookies.isEmpty()) return;

    client.cookieJar().saveFromResponse(userRequest.url(), cookies);
  }

通過以上幾個(gè)關(guān)鍵方法,可以很明顯的感覺到作者的意圖了,為了更加自由定制化的cookie管理。其中loadForRequest()、saveFromResponse()這兩個(gè)方法最為關(guān)鍵,分別是在發(fā)送時(shí)向request header中加入cookie,在接收時(shí),讀取response header中的cookie?,F(xiàn)在再去看Cookiejar這個(gè)類,就很好理解了

public interface CookieJar {
  /** A cookie jar that never accepts any cookies. */
  CookieJar NO_COOKIES = new CookieJar() {
    @Override public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
    }

    @Override public List<Cookie> loadForRequest(HttpUrl url) {
      return Collections.emptyList();
    }
  };

  /**
   * Saves {@code cookies} from an HTTP response to this store according to this jar's policy.
   *
   * <p>Note that this method may be called a second time for a single HTTP response if the response
   * includes a trailer. For this obscure HTTP feature, {@code cookies} contains only the trailer's
   * cookies.
   */
  void saveFromResponse(HttpUrl url, List<Cookie> cookies);

  /**
   * Load cookies from the jar for an HTTP request to {@code url}. This method returns a possibly
   * empty list of cookies for the network request.
   *
   * <p>Simple implementations will return the accepted cookies that have not yet expired and that
   * {@linkplain Cookie#matches match} {@code url}.
   */
  List<Cookie> loadForRequest(HttpUrl url);
}

so!在OkHttpClient創(chuàng)建時(shí),傳入這個(gè)CookieJar的實(shí)現(xiàn),就能完成對(duì)Cookie的自動(dòng)管理了

OkHttpClient client = new OkHttpClient.Builder()
    .cookieJar(new CookieJar() {
        private final HashMap<HttpUrl, List<Cookie>> cookieStore = new HashMap<>();

        @Override
        public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
            cookieStore.put(url, cookies);
        }

        @Override
        public List<Cookie> loadForRequest(HttpUrl url) {
            List<Cookie> cookies = cookieStore.get(url);
            return cookies != null ? cookies : new ArrayList<Cookie>();
        }
    })
    .build();

Cookies持久化

對(duì)Cookies持久化的方案,與之前版本并無很大區(qū)別,還是參考android-async-http這個(gè)庫(kù),主要參考其中兩個(gè)類:

  • PersistentCookieStore

  • SerializableHttpCookie
    與之前版本的區(qū)別是要將對(duì)Java.net.HttpCookie這個(gè)類的緩存處理換成對(duì)okhttp3.Cookie的處理,其他方面幾乎一樣。

廢話不多說了,直接上代碼

SerializableOkHttpCookies

主要做兩件事:

  • 將Cookie對(duì)象輸出為ObjectStream

  • 將ObjectStream序列化成Cookie對(duì)象

public class SerializableOkHttpCookies implements Serializable {

    private transient final Cookie cookies;
    private transient Cookie clientCookies;

    public SerializableOkHttpCookies(Cookie cookies) {
        this.cookies = cookies;
    }

    public Cookie getCookies() {
        Cookie bestCookies = cookies;
        if (clientCookies != null) {
            bestCookies = clientCookies;
        }
        return bestCookies;
    }

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.writeObject(cookies.name());
        out.writeObject(cookies.value());
        out.writeLong(cookies.expiresAt());
        out.writeObject(cookies.domain());
        out.writeObject(cookies.path());
        out.writeBoolean(cookies.secure());
        out.writeBoolean(cookies.httpOnly());
        out.writeBoolean(cookies.hostOnly());
        out.writeBoolean(cookies.persistent());
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        String name = (String) in.readObject();
        String value = http://segmentfault.com/a/(String) in.readObject();
        long expiresAt = in.readLong();
        String domain = (String) in.readObject();
        String path = (String) in.readObject();
        boolean secure = in.readBoolean();
        boolean httpOnly = in.readBoolean();
        boolean hostOnly = in.readBoolean();
        boolean persistent = in.readBoolean();
        Cookie.Builder builder = new Cookie.Builder();
        builder = builder.name(name);
        builder = builder.value(value);
        builder = builder.expiresAt(expiresAt);
        builder = hostOnly ? builder.hostOnlyDomain(domain) : builder.domain(domain);
        builder = builder.path(path);
        builder = secure ? builder.secure() : builder;
        builder = httpOnly ? builder.httpOnly() : builder;
        clientCookies =builder.build();
    }
}

PersistentCookieStore

根據(jù)一定的規(guī)則去緩存或者獲取Cookie:

public class PersistentCookieStore {
    private static final String LOG_TAG = "PersistentCookieStore";
    private static final String COOKIE_PREFS = "Cookies_Prefs";

    private final Map<String, ConcurrentHashMap<String, Cookie>> cookies;
    private final SharedPreferences cookiePrefs;


    public PersistentCookieStore(Context context) {
        cookiePrefs = context.getSharedPreferences(COOKIE_PREFS, 0);
        cookies = new HashMap<>();

        //將持久化的cookies緩存到內(nèi)存中 即map cookies
        Map<String, ?> prefsMap = cookiePrefs.getAll();
        for (Map.Entry<String, ?> entry : prefsMap.entrySet()) {
            String[] cookieNames = TextUtils.split((String) entry.getValue(), ",");
            for (String name : cookieNames) {
                String encodedCookie = cookiePrefs.getString(name, null);
                if (encodedCookie != null) {
                    Cookie decodedCookie = decodeCookie(encodedCookie);
                    if (decodedCookie != null) {
                        if (!cookies.containsKey(entry.getKey())) {
                            cookies.put(entry.getKey(), new ConcurrentHashMap<String, Cookie>());
                        }
                        cookies.get(entry.getKey()).put(name, decodedCookie);
                    }
                }
            }
        }
    }

    protected String getCookieToken(Cookie cookie) {
        return cookie.name() + "@" + cookie.domain();
    }

    public void add(HttpUrl url, Cookie cookie) {
        String name = getCookieToken(cookie);

        //將cookies緩存到內(nèi)存中 如果緩存過期 就重置此cookie
        if (!cookie.persistent()) {
            if (!cookies.containsKey(url.host())) {
                cookies.put(url.host(), new ConcurrentHashMap<String, Cookie>());
            }
            cookies.get(url.host()).put(name, cookie);
        } else {
            if (cookies.containsKey(url.host())) {
                cookies.get(url.host()).remove(name);
            }
        }

        //講cookies持久化到本地
        SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
        prefsWriter.putString(url.host(), TextUtils.join(",", cookies.get(url.host()).keySet()));
        prefsWriter.putString(name, encodeCookie(new SerializableOkHttpCookies(cookie)));
        prefsWriter.apply();
    }

    public List<Cookie> get(HttpUrl url) {
        ArrayList<Cookie> ret = new ArrayList<>();
        if (cookies.containsKey(url.host()))
            ret.addAll(cookies.get(url.host()).values());
        return ret;
    }

    public boolean removeAll() {
        SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
        prefsWriter.clear();
        prefsWriter.apply();
        cookies.clear();
        return true;
    }

    public boolean remove(HttpUrl url, Cookie cookie) {
        String name = getCookieToken(cookie);

        if (cookies.containsKey(url.host()) && cookies.get(url.host()).containsKey(name)) {
            cookies.get(url.host()).remove(name);

            SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
            if (cookiePrefs.contains(name)) {
                prefsWriter.remove(name);
            }
            prefsWriter.putString(url.host(), TextUtils.join(",", cookies.get(url.host()).keySet()));
            prefsWriter.apply();

            return true;
        } else {
            return false;
        }
    }

    public List<Cookie> getCookies() {
        ArrayList<Cookie> ret = new ArrayList<>();
        for (String key : cookies.keySet())
            ret.addAll(cookies.get(key).values());

        return ret;
    }

    /**
     * cookies 序列化成 string
     *
     * @param cookie 要序列化的cookie
     * @return 序列化之后的string
     */
    protected String encodeCookie(SerializableOkHttpCookies 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());
    }

    /**
     * 將字符串反序列化成cookies
     *
     * @param cookieString cookies string
     * @return cookie object
     */
    protected Cookie decodeCookie(String cookieString) {
        byte[] bytes = hexStringToByteArray(cookieString);
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        Cookie cookie = null;
        try {
            ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
            cookie = ((SerializableOkHttpCookies) objectInputStream.readObject()).getCookies();
        } catch (IOException e) {
            Log.d(LOG_TAG, "IOException in decodeCookie", e);
        } catch (ClassNotFoundException e) {
            Log.d(LOG_TAG, "ClassNotFoundException in decodeCookie", e);
        }

        return cookie;
    }

    /**
     * 二進(jìn)制數(shù)組轉(zhuǎn)十六進(jìn)制字符串
     *
     * @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);
    }

    /**
     * 十六進(jìn)制字符串轉(zhuǎn)二進(jìn)制數(shù)組
     *
     * @param hexString string of hex-encoded values
     * @return decoded byte array
     */
    protected byte[] hexStringToByteArray(String hexString) {
        int len = hexString.length();
        byte[] data = http://segmentfault.com/a/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;
    }
}

最終效果

完成對(duì)Cookie持久化之后,就可以對(duì)Cookiejar進(jìn)行進(jìn)一步修改了,最終效果:

/**
     * 自動(dòng)管理Cookies
     */
    private class CookiesManager implements CookieJar {
        private final PersistentCookieStore cookieStore = new PersistentCookieStore(getApplicationContext());

        @Override
        public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
            if (cookies != null && cookies.size() > 0) {
                for (Cookie item : cookies) {
                    cookieStore.add(url, item);
                }
            }
        }

        @Override
        public List<Cookie> loadForRequest(HttpUrl url) {
            List<Cookie> cookies = cookieStore.get(url);
            return cookies;
        }
    }

Tips

在這樣做之前,嘗試了使用InterceptorNetWorkInterceptor在Http請(qǐng)求request和response時(shí),攔截響應(yīng)鏈,加入對(duì)Cookie的管理。so!接下來可能會(huì)詳細(xì)介紹下Interceptor這個(gè)非??岬膶?shí)現(xiàn)。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多