OkHttp使用详解一
2017-04-11 15:38
218 查看
概要
本篇主要讲解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同步请求
//步骤1 OkHttpClient client = new OkHttpClient(); //步骤2 Request request = new Request.Builder().url(url).build(); //步骤3 Response 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异步请求
与同步请求不同之处就在步骤三中,步骤三代码如下://步骤3 client.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来设置表单的参数,示例代码如下://步骤1 OkHttpClient client = new OkHttpClient(); //步骤2 RequestBody formBody = new FormBody.Builder().add("name", "木子").add("pwd", "123456").build(); Request request = new Request.Builder().url(uri).post(formBody).build(); //步骤3 client.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 在响应头中获取请求成功后的Cookie public void onResponse(Call call, Response response) throws IOException { String result = response.body().string(); cookie=response.header("Set-Cookie"); } //List请求时携带客户端Login成功后Cookie Request 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中使用CookieJar OkHttpClient 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 <-> 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
相关文章推荐
- OkHttp在Android中使用详解
- OkHttp使用详解——完全版
- OKHttp使用详解
- Android中okhttp3使用详解
- okhttp实战使用详解
- OkHttp使用方法详解
- Retrofit 2.0使用详解,配合OkHttp、Gson,Android最强网络请求框架
- okhttp的详解及其缓存的使用
- OkHttp的封装和使用详解
- okhttp的使用及详解
- Retrofit 2.0使用详解,配合OkHttp、Gson,Android最强网络请求框架
- OkHttp3使用详解
- OkHttp使用详解
- OkHttp3使用详解
- Okhttp使用详解
- OKHttp使用详解及源码解析
- Android OkHttp 网络请求 使用详解
- Android okhttp3的使用详解
- OkHttp使用详解
- OKHttp使用详解,步骤挺详细的,适合初学者使用!