现在Android网络方面的第三方库很多,volley,Retrofit,OKHttp等,各有各自的特点。okhttp是一款高效的HTTP客户端,支持连接同一地址的链接共享同一个socket,通过连接池来减小响应延迟,还有透明的GZIP压缩,请求缓存等优势 。
okhttp官网
github: https://github.com/square/okhttp
一、 使用教程
使用之前,先在在app module build.gradle配置:
compile 'com.squareup.okhttp3:okhttp:3.2.0' compile 'com.google.code.gson:gson:2.2.4'
1. Http Get请求
// 1. 拿到okHttpClient对象
OkHttpClient okHttpClient = new OkHttpClient();
// 2. 构造Request对象
Request.Builder requestBuilder = new Request.Builder();
// Request request = requestBuilder.url("https://www.baidu.com").build();
Request request = requestBuilder.get().url("https://www.baidu.com").build();
// 3. 将Request封装为Call
Call call = okHttpClient.newCall(request);
// 4. 执行call
// Response response = call.execute(); //同步
call.enqueue(new Callback() { //异步
@Override
public void onFailure(Call call, IOException e) {
}
/**
* 此时还在非UI线程中
* @param call
* @param response
* @throws IOException
*/
@Override
public void onResponse(Call call, Response response) throws IOException {
// response.body().string();
}
});
2. Http Post请求
// 1. 拿到okHttpClient对象
OkHttpClient okHttpClient = new OkHttpClient();
// 2. 构造Request对象
FormBody.Builder fromBodyBuilder = new FormBody.Builder();
// 2.1 构造RequestBody对象
RequestBody requestBody = fromBodyBuilder.add("mb", "19811230100").add("pwd", "999999q").build();
Request.Builder requestBuilder = new Request.Builder();
Request request = requestBuilder.post(requestBody).url("").build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
3. Http Post提交Json字符串
OkHttpClient okHttpClient = new OkHttpClient();
RequestBody requestBody = RequestBody.create(MediaType.parse("text/plain;chaset=utf-8"), "{mb:19811230100,pwd:123}");
Request.Builder requestBuilder = new Request.Builder();
Request request = requestBuilder.post(requestBody).url("").build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
4. Http Post提交File
跟上面相比,就是RequestBody不同,如下:
5.Http Post上传文件
OkHttpClient okHttpClient = new OkHttpClient();
File file = new File(Environment.getExternalStorageDirectory(), "test.jpg");
if (!file.exists()) {
Log.d("chengjie", file.getAbsolutePath() + " not exist");
return;
}
MultipartBody.Builder multipartBodyBuilder = new MultipartBody.Builder();
multipartBodyBuilder.setType(MultipartBody.FORM)
.addFormDataPart("username", "jackie")
.addFormDataPart("password", "123")
/**
* mPhoto 表单域 input标签
* 在服务端,通过这个表单域来获取上传的文件
*
* 这两个命名是固定的
* File mPhoto
* String mPhotoFileName
*
* if (mPhoto == null) {
* System.out.println(mPhotoFileName + " is null");
* }
*
* String dir = ServletActionContext.getServletContext().getRealPath("files");
* File file = new File(dir, mPhotoFileName);
* FileUtils.copyFile(mPhoto, file) //struts2封装的方法
*/
.addFormDataPart("mPhoto", "jackie.jpg", RequestBody.create(MediaType.parse("application/octet-stream"), file));
RequestBody requestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
Request.Builder requestBuilder = new Request.Builder();
Request request = requestBuilder.post(requestBody).url("").build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
6.下载文件
OkHttpClient okHttpClient = new OkHttpClient();
Request.Builder requestBuilder = new Request.Builder();
Request request = requestBuilder.get().url("" + "test.jpg").build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//文件总长度
long total = response.body().contentLength();
long sum = 0L;
InputStream is = response.body().byteStream();
FileOutputStream fos = new FileOutputStream(new File(Environment.getExternalStorageDirectory(), "12306.jpg"));
int len = 0;
byte[] buf = new byte[128];
while ((len = is.read(buf)) != -1) {
fos.write(buf, 0, len);
sum += len;
Log.d("chengjie", sum + "/" + total);
}
fos.flush();
fos.close();
is.close();
}
});
7.Session保持
8.上传文件进度
CountingRequestBody.java
package com.jackie.sample.okhttp.request;
import java.io.IOException;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import okio.Buffer;
import okio.BufferedSink;
import okio.ForwardingSink;
import okio.Okio;
import okio.Sink;
/**
* Created by Administrator on 2016/12/16.
*/
public class CountingRequestBody extends RequestBody {
private RequestBody mDelegate;
private OnCountingListener mOnCountingListener;
private CountingSink mCountingSink;
public CountingRequestBody(RequestBody delegate, OnCountingListener onCountingListener) {
this.mDelegate = delegate;
this.mOnCountingListener = onCountingListener;
}
@Override
public MediaType contentType() {
return mDelegate.contentType();
}
@Override
public long contentLength() {
try {
return mDelegate.contentLength();
} catch (Exception e) {
return -1;
}
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
mCountingSink = new CountingSink(sink);
BufferedSink bufferedSink = Okio.buffer(mCountingSink);
mDelegate.writeTo(bufferedSink);
bufferedSink.flush();
}
private class CountingSink extends ForwardingSink {
private long byteWritten;
public CountingSink(Sink delegate) {
super(delegate);
}
@Override
public void write(Buffer source, long byteCount) throws IOException {
super.write(source, byteCount);
byteWritten += byteCount;
if (mOnCountingListener != null) {
mOnCountingListener.onRequestProgress(byteWritten, contentLength());
}
}
}
public interface OnCountingListener {
void onRequestProgress(long byteWritten, long contentLength);
}
}
使用方法如下:
OkHttpClient okHttpClient = new OkHttpClient();
File file = new File(Environment.getExternalStorageDirectory(), "test.jpg");
if (!file.exists()) {
Log.d("chengjie", file.getAbsolutePath() + " not exist");
return;
}
MultipartBody.Builder multipartBodyBuilder = new MultipartBody.Builder();
multipartBodyBuilder.setType(MultipartBody.FORM)
.addFormDataPart("username", "jackie")
.addFormDataPart("password", "123")
/**
* mPhoto 表单域 input标签
* 在服务端,通过这个表单域来获取上传的文件
*
* 这两个命名是固定的
* File mPhoto
* String mPhotoFileName
*
* if (mPhoto == null) {
* System.out.println(mPhotoFileName + " is null");
* }
*
* String dir = ServletActionContext.getServletContext().getRealPath("files");
* File file = new File(dir, mPhotoFileName);
* FileUtils.copyFile(mPhoto, file) //struts2封装的方法
*/
.addFormDataPart("mPhoto", "jackie.jpg", RequestBody.create(MediaType.parse("application/octet-stream"), file))
.build();
RequestBody requestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
CountingRequestBody countingRequestBody = new CountingRequestBody(requestBody, new CountingRequestBody.OnCountingListener() {
@Override
public void onRequestProgress(long byteWritten, long contentLength) {
Log.d("chengjie", byteWritten + "/" + contentLength);
}
});
Request.Builder requestBuilder = new Request.Builder();
Request request = requestBuilder.post(requestBody).url("").build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
二、封装
OKHttpException.java
package com.jackie.sample.okhttp.exception;
/**
* Created by Administrator on 2016/10/30.
*/
public class OkHttpException extends Exception {
private static final long serialVersionUID = 1L;
/**
* the server return code
*/
private int ecode;
/**
* the server return error message
*/
private Object emsg;
public OkHttpException(int ecode, Object emsg) {
this.ecode = ecode;
this.emsg = emsg;
}
public Object getEmsg() {
return emsg;
}
public void setEmsg(Object emsg) {
this.emsg = emsg;
}
public int getEcode() {
return ecode;
}
public void setEcode(int ecode) {
this.ecode = ecode;
}
}
DisposeDataHandler.java
package com.jackie.sample.okhttp.listener;
/**
* Created by Administrator on 2016/10/30.
*/
public class DisposeDataHandler {
public DisposeDataListener mListener = null;
public Class> mClazz = null;
public DisposeDataHandler(DisposeDataListener listener) {
mListener = listener;
}
public DisposeDataHandler(DisposeDataListener listener, Class> clazz) {
mListener = listener;
mClazz = clazz;
}
}
DisposeDataListener.java
package com.jackie.sample.okhttp.listener;
/**
* Created by Administrator on 2016/10/30.
*/
public interface DisposeDataListener {
void onSuccess(Object response);
void onFailure(Object error);
}
CommonRequest.java
package com.jackie.sample.okhttp.request;
import java.io.File;
import java.util.Map;
import okhttp3.FormBody;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.Request;
import okhttp3.RequestBody;
/**
* Created by Administrator on 2016/10/30.
* 负责创建各种类型的请求对象,包括get,post,文件上传类型,文件下载类型
*/
public class CommonRequest {
/**
* append the params to the url
*/
public static Request createGetRequest(String url, RequestParams params) {
StringBuilder urlBuilder = new StringBuilder(url).append("?");
if (params != null) {
for (Map.Entry entry : params.urlParams.entrySet()) {
urlBuilder.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
}
}
return new Request.Builder().url(urlBuilder.substring(0, urlBuilder.length() - 1)).get().build();
}
/**
* create the key-value Request
* @param url
* @param params
* @return
*/
public static Request createPostRequest(String url, RequestParams params) {
FormBody.Builder builder = new FormBody.Builder();
if (params != null) {
for (Map.Entry entry : params.urlParams.entrySet()) {
builder.add(entry.getKey(), entry.getValue());
}
}
FormBody body = builder.build();
return new Request.Builder().url(url).post(body).build();
}
/**
* 文件上传请求
*/
private static final MediaType FILE_TYPE = MediaType.parse("application/octet-stream");
public static Request createMultiPostRequest(String url, RequestParams params) {
MultipartBody.Builder builder = new MultipartBody.Builder();
builder.setType(MultipartBody.FORM);
if (params != null) {
for (Map.Entry entry : params.fileParams.entrySet()) {
if (entry.getValue() instanceof File) {
builder.addPart(MultipartBody.Part.createFormData(entry.getKey(),
null,
RequestBody.create(FILE_TYPE, (File) entry.getValue())));
} else {
builder.addFormDataPart(entry.getKey(), String.valueOf(entry.getValue()));
}
}
}
return new Request.Builder().url(url).post(builder.build()).build();
}
}
RequestParams.java
package com.jackie.sample.okhttp.request;
import java.io.File;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Created by Administrator on 2016/10/30.
*/
public class RequestParams {
public ConcurrentHashMap urlParams = new ConcurrentHashMap<>();
public ConcurrentHashMap fileParams = new ConcurrentHashMap<>();
/**
* Constructs a new empty { @code RequestParams } instance.
*/
public RequestParams() {
this((Map) null);
}
/**
* Constructs a new RequestParams instance containing the key/value string
* params from this specified map.
*
* @param source
* the source key/value string map to add.
*/
public RequestParams(Map source) {
if (source != null) {
for (Map.Entry entry : source.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
}
public void put(String key, String value) {
urlParams.put(key, value);
}
public void put(String key, File file) {
fileParams.put(key, file);
}
}
CommonJsonCallback.java
package com.jackie.sample.okhttp.response;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import com.google.gson.Gson;
import com.jackie.sample.okhttp.exception.OkHttpException;
import com.jackie.sample.okhttp.listener.DisposeDataHandler;
import com.jackie.sample.okhttp.listener.DisposeDataListener;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;
/**
* Created by Administrator on 2016/10/30.
*/
/**
* 专门处理Json的回调
*/
public class CommonJsonCallback implements Callback {
/**
* the logic layer exception, may alter in different app
*/
protected final String RESULT_CODE = "ecode"; //有返回则对于http请求来说是成功的,但还有可能失败
protected final int RESULT_CODE_VALUE = 0;
protected final String ERROR_MSG = "emsg";
protected final String EMPTY_MSG = "";
/**
* the java layer exception, do not same to the logic error
*/
protected final int NETWORK_ERROR = -1; //the network relative error
protected final int JSON_ERROR = -2; //the json relative error
protected final int OTHER_ERROR = -3; //the unknown error
private DisposeDataListener mListener;
private Class> mClazz;
private Handler mHandler;
public CommonJsonCallback(DisposeDataHandler handler) {
mListener = handler.mListener;
mClazz = handler.mClazz;
mHandler = new Handler(Looper.getMainLooper());
}
@Override
public void onFailure(Call call, final IOException e) {
mHandler.post(new Runnable() {
@Override
public void run() {
if (mListener != null) {
mListener.onFailure(e);
}
}
});
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//还在子线程中
final String result = response.body().toString();
mHandler.post(new Runnable() {
@Override
public void run() {
handleResponse(result);
}
});
}
private void handleResponse(String result) {
if (TextUtils.isEmpty(result)) {
if (mListener != null) {
mListener.onFailure(new OkHttpException(NETWORK_ERROR, EMPTY_MSG));
return;
}
}
try {
JSONObject resultJsonObject = new JSONObject(result);
if (resultJsonObject.has(RESULT_CODE)) {
if (resultJsonObject.optInt(RESULT_CODE) == RESULT_CODE_VALUE) {
if (mClazz == null) {
if (mListener != null) {
mListener.onSuccess(resultJsonObject);
}
} else {
Gson gson = new Gson();
Object resultObject = gson.fromJson(result, mClazz);
if (resultObject == null) {
if (mListener != null) {
mListener.onFailure(new OkHttpException(JSON_ERROR, EMPTY_MSG));
}
} else {
//相应真正正确的处理,并且直接返回了实体对象
if (mListener != null) {
mListener.onSuccess(resultObject);
}
}
}
}
} else {
if (mListener != null) {
mListener.onFailure(new OkHttpException(NETWORK_ERROR, EMPTY_MSG));
}
}
} catch (JSONException e) {
if (mListener != null) {
mListener.onFailure(new OkHttpException(OTHER_ERROR, e.getMessage()));
}
}
}
}
CommonOkHttpClient.java
package com.jackie.sample.okhttp;
/**
* Created by Administrator on 2016/10/30.
*/
import com.jackie.sample.okhttp.listener.DisposeDataHandler;
import com.jackie.sample.okhttp.response.CommonJsonCallback;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Request;
/**
* 用来发送get,post请求的工具类,包括设置一些请求的公用参数
*/
public class CommonOkHttpClient {
private static final int TIME_OUT = 30;
private static OkHttpClient mOkHttpClient;
static {
OkHttpClient.Builder okHttpBuilder = new OkHttpClient.Builder();
//对https的认证
okHttpBuilder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
});
try {
okHttpBuilder.connectTimeout(TIME_OUT, TimeUnit.SECONDS);
okHttpBuilder.readTimeout(TIME_OUT, TimeUnit.SECONDS);
okHttpBuilder.writeTimeout(TIME_OUT, TimeUnit.SECONDS);
okHttpBuilder.followRedirects(true);
SSLContext sslContext = SSLContext.getInstance("TLS");
okHttpBuilder.sslSocketFactory(sslContext.getSocketFactory());
mOkHttpClient = okHttpBuilder.build();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
public static void get(Request request, DisposeDataHandler handler) {
Call call = mOkHttpClient.newCall(request);
call.enqueue(new CommonJsonCallback(handler));
}
public static void post(Request request, DisposeDataHandler handler) {
Call call = mOkHttpClient.newCall(request);
call.enqueue(new CommonJsonCallback(handler));
}
}
封装之后代码结构图如下:
Get请求