OkHttp使用详解一

来源:互联网 发布:1hhhh域名升级访问 编辑:程序博客网 时间:2024/05/21 17:39

概要

本篇主要讲解OkHttp最基本的使用,如最常见的get和post请求,在本文中post主要介绍的是表单提交方式的请求,文章最后介绍了如何在服务端和客户端设置处理Cookie,客户端给出了两种最常见的方式处理Cookie。

在学习Android的过程中,官方集成网络框架就包含了HttpUrlConnection、HttpClient、Volley,其中Volley是android开发团队在2013年Google I/O大会上推出了一个新的网络通信框架,目前Volley中部分代码仍然借助于HttpClient中部分功能,然而HttpClient在Android最新版本6.0中已经被剔除掉了,如果想要使用Volley还必须使用一个第三方的jai包org.apache.http.legacy.jar,再者Volley是针对数据量不大,但通信频繁的网络操作,而对于大数据量的网络操作,比如说下载文件等,Volley的表现就会非常糟糕。如果开发中使用HttpUrlConnection则要从头开始封装对应得操作,所以最近转向了一个第三方网络请求框架OkHttp,本文所介绍的示例都是针对OkHttp3.0库。

OkHttp

OkHttp是一个 Java 的 HTTP+SPDY 客户端开发包,同时也支持 Android。需要Android 2.3以上,OkHttp github源码,同时还需要一个okio包

OKHttp是Android版Http客户端。非常高效,支持SPDY、连接池、GZIP和 HTTP 缓存;
默认情况下,OKHttp会自动处理常见的网络问题,像二次连接、SSL的握手问题;
如果你的应用程序中集成了OKHttp,Retrofit默认会使用OKHttp处理其他网络层请求;
从Android4.4开始HttpURLConnection的底层实现采用的是okHttp。

OkHttp的简单使用

OkHttp建立一个网络请求可以是异步的也可以是同步的,大体上分为三个步骤:

1、 新建一个OkHttpClient对象,可以直接new一个OkHttpClient,也可以使用建造者模式build一个,事实上new一个新的内部也是调用的build方式构建出来的。

public OkHttpClient() {    this(new Builder());}

2、 通过Request.Builder对象新建一个Request对象,仍然使用的是建造者模式;
3、 返回执行结果,同步请求和异步请求在这里才有区别,在执行请求之前,上面两个步骤都是一样的。

OkHttp同步请求

//步骤1OkHttpClient client = new OkHttpClient();//步骤2Request request = new Request.Builder().url(url).build();//步骤3Response response = client.newCall(request).execute();if(response.isSuccessful()){    String result = response.body().string();    System.out.println(result);}

这里建立的默认请求采用的是get方式请求的,源码如下:

public Builder() {  this.method = "GET";  this.headers = new Headers.Builder();}

OkHttp异步请求

与同步请求不同之处就在步骤三中,步骤三代码如下:

//步骤3client.newCall(request).enqueue(new Callback() {    @Override    public void onFailure(Call call, IOException e) {    }    @Override    public void onResponse(Call call, Response response) throws IOException {        String result = response.body().string();        System.out.println(result);    }});

注意:虽然这里是异步请求,但是这里的回调函数确是在子线程中执行的回调,在Android开发中如果想将回调放入主线程中,因为只有在主线程中才能更新UI,我们一般会借助于Handler消息机制来处理,以前写过一篇文章Handler机制,一般Looper在哪个线程,它处理的消息就在哪个线程。

//新建一个Handler实例Handler handler = new Handler(Looper.getMainLooper());//自定义一个接口回调public interface RequestCallback {    void onStart();    void onSuccess(String result);    void onFailure(ErrorType errorType);}//设置转换为主线程的回调httpClient.newCall(request).enqueue(new Callback() {    public void onResponse(Response response) throws IOException {        if (response.isSuccessful()) {            final String result = response.body().string();            handler.post(new Runnable() {                public void run() {                    callback.onSuccess(result);                }            });        }    }    public void onFailure(Request req, final IOException e) {        handler.post(new Runnable() {            public void run() {                callback.onFailure(ErrorType.SERVER);            }        });    }});

get请求url转码

在处理get请求是我们要对url中需要传入的参数进行Encoder处理,主要是为了处理传输过程中的特殊字符如中文或者空格等。

public static String buildQueryUrl(String uri, List<KeyValue> params) {    StringBuilder queryBuilder = new StringBuilder(uri);    if (!uri.contains("?")) {        queryBuilder.append("?");    } else if (!uri.endsWith("?")) {        queryBuilder.append("&");    }    List<KeyValue> queryParams = params;    if (queryParams != null) {        for (KeyValue kv : queryParams) {            String name = kv.key;            String value = kv.getValueStr();            if (!TextUtils.isEmpty(name) && value != null) {                queryBuilder.append(Uri.encode(name, "utf-8")).append("=").append(Uri.encode(value, "utf-8")).append("&");            }        }    }    if (queryBuilder.charAt(queryBuilder.length() - 1) == '&') {        queryBuilder.deleteCharAt(queryBuilder.length() - 1);    }    if (queryBuilder.charAt(queryBuilder.length() - 1) == '?') {        queryBuilder.deleteCharAt(queryBuilder.length() - 1);    }    return queryBuilder.toString();}

其中KeyValue就是一个JavaBean,也可以中Map代替,本文KeyValue代码来自xUtils3。

public class KeyValue {    public final String key;    public final Object value;    public KeyValue(String key, Object value) {        this.key = key;        this.value = value;    }    public String getValueStr() {        return value == null ? null : value.toString();    }    @Override    public boolean equals(Object o) {        if (this == o) return true;        if (o == null || getClass() != o.getClass()) return false;        KeyValue keyValue = (KeyValue) o;        return key == null ? keyValue.key == null : key.equals(keyValue.key);    }    @Override    public int hashCode() {        return key != null ? key.hashCode() : 0;    }    @Override    public String toString() {        return "KeyValue{" + "key='" + key + '\'' + ", value=" + value + '}';    }}

下面是一个url简单的示例:

List<KeyValue> list = new ArrayList<KeyValue>();list.add(new KeyValue("name", "木子"));list.add(new KeyValue("pwd", "123456"));String uri = "http://192.168.0.117:8080/cookie/get?isLogin=true";uri = ParamUtils.buildQueryUrl(uri, list);

转码后输入结果为:

http://192.168.0.117:8080/cookie/get?isLogin=true&name=%E6%9C%A8%E5%AD%90&pwd=123456

post请求

这里所讲的post请求,仅仅限制在表单提交中,也就是content-type为” application/x-www-form-urlencoded”的请求,其余的方式如文件上传下次再做介绍。对于post方式提交表单,OkHttp已经封装好了相应的类FormBody来设置表单的参数,示例代码如下:

//步骤1OkHttpClient client = new OkHttpClient();//步骤2RequestBody formBody = new FormBody.Builder().add("name", "木子").add("pwd", "123456").build();Request request = new Request.Builder().url(uri).post(formBody).build();//步骤3client.newCall(request).enqueue(new Callback() {    @Override    public void onFailure(Call call, IOException e) {    }    @Override    public void onResponse(Call call, Response response) throws IOException {        String result = response.body().string();    }});

OkHttp中Cookie操作

先介绍一下接下来demo的操作流程

1、 访问LoginServlet,然后服务器端返回Cookie,在Cookie中设置了isLogin=true,LoginServlet核心处理代码如下:

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {    resp.setCharacterEncoding("utf-8");    PrintWriter out=resp.getWriter();    //設置cookie,返回isLogin为true    Cookie cookie=new Cookie("isLogin", "true");    resp.addCookie(cookie);    out.println("login success!");    out.flush();    out.close();}

2、客户端带着Cookie访问ListServlet,在ListServlet中检查Cookie中isLogin是否为true,若为true则返回成功结果,否则返回错误信息,ListServlet核心处理代码如下:

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {    resp.setCharacterEncoding("utf-8");    PrintWriter out = resp.getWriter();    // 获取所有的cookie值    Cookie[] cookies = req.getCookies();    if(cookies==null||cookies.length==0){        out.println("list  error!");        return;    }    Cookie cookie = null;    for (int i = 0; i < cookies.length; i++) {        cookie = cookies[i];        if (cookie.getName().equals("isLogin")) {            if(cookie.getValue().equals("true")){                resp.addCookie(cookie);                out.println("list success!");            }else{                out.println("list  error!");            }            break;        }    }    out.flush();    out.close();}

OkHttp操作Cookie方式一

该种方式是通过Http请求中的Header来处理Cookie的,服务器端返回的相应头中会有Set-Cookie字段,客户端请求时携带Cookie可以设置Cookie字段。

//Login 在响应头中获取请求成功后的Cookiepublic void onResponse(Call call, Response response) throws IOException {    String result = response.body().string();    cookie=response.header("Set-Cookie");}//List请求时携带客户端Login成功后CookieRequest request = new Request.Builder().url(url).addHeader("Cookie", cookie).build();

OkHttp操作Cookie方式二

事实上OkHttp提供了一种便捷的方式来操作Cookie,通过实现CookieJar接口来自动管理Cookie。

public final class JavaNetCookieJar implements CookieJar {    private final List<Cookie> allCookies=new ArrayList<Cookie>();    @Override    public synchronized void saveFromResponse(HttpUrl url, List<Cookie> cookies) {        allCookies.addAll(cookies);    }    @Override    public synchronized List<Cookie> loadForRequest(HttpUrl url) {        List<Cookie> result = new ArrayList<Cookie>();        for (Cookie cookie : allCookies) {            if (cookie.matches(url)) {                result.add(cookie);            }        }        return result;    }}//OkHttp中使用CookieJarOkHttpClient client = new OkHttpClient.Builder().cookieJar(new JavaNetCookieJar()).build();

通过上面方式就可以自定管理Cookie了,但是有一个问题,如果我们客户端使用了多个OkHttpClient,由于CookieJar并不是一个静态属性,所以每一个OkHttpClient都会有一个自己的CookieJar实例,如果已经登录成功后使用一个新的OkHttpClient会发现CookieJar中Cookie为null,当然了如果一个App只有一个OkHttpClient不会出现该问题,解决该问题也很简单,就是设置一个全局的静态变量,每次请求成功后都将Cookie相关信息赋值给该变量就可以了,加入全局静态变量后代码如下:

public final class JavaNetCookieJar implements CookieJar {    private final List<Cookie> allCookies=new ArrayList<Cookie>();    @Override    public synchronized void saveFromResponse(HttpUrl url, List<Cookie> cookies) {        allCookies.addAll(cookies);        if(cookies!=null){            //全局静态变量ALL_COOKIES            NetUtils.ALL_COOKIES=allCookies;        }    }    @Override    public synchronized List<Cookie> loadForRequest(HttpUrl url) {        List<Cookie> result = new ArrayList<Cookie>();        for (Cookie cookie : allCookies) {            if (cookie.matches(url)) {                result.add(cookie);            }        }        if(result.size()==0&&NetUtils.ALL_COOKIES!=null){            result=NetUtils.ALL_COOKIES;        }        return result;    }}

结束语

本文只讲解了OkHttp的一小部分应用,接下来文件上传和下载还有请求中缓存cache的处理还没有设计,在以后的文章中再详细的介绍。有关Cookie的介绍,在网上也有许多代码,有些将Cookie持久化到了SharedPreferences,有兴趣的可以查看下面这个链接http://stackoverflow.com/questions/25461792/persistent-cookie-store-using-okhttp-2-on-android,在本文中也可以在CookieJar的实例中来持久化Cookie,代码就不再介绍了。

本文地址www.sunnyang.com/364.html




You can set your CookieStore in the OkHttp client with the following code:

OkHttpClient client = new OkHttpClient();client.setCookieHandler(new CookieManager(                       new PersistentCookieStore(getApplicationContext()),                       CookiePolicy.ACCEPT_ALL));

Putting the code into a custom Application’s onCreate function solves the issue. It works now.

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);    }}

I managed to get persistent cookies with okhttp with the following CookieStore, which is partly copied from the one of android-async-http. It works with API lvl 9 and maybe fewer.

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 &lt;-&gt; 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;    }}

The 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());    }}

转自:http://stackoverflow.com/questions/25461792/persistent-cookie-store-using-okhttp-2-on-android

0 0
原创粉丝点击