1.首先添加依赖库
compile 'com.squareup.okhttp3:okhttp:3.5.0'
compile 'com.squareup.okio:okio:1.11.0'
compile 'com.facebook.stetho:stetho-okhttp3:1.4.2'
上面引入了三个库,分别是okhttp框架包,okhttp操作流的包,最后一个是facebook公司开发的用于检测okhttp日志的插件包,用于在chrome浏览器中调试okhttp网络访问数据非常方便。
stetho需要在Application中初始化
Stetho.initializeWithDefaults(this);
然后在chrome中输入地址:chrome://inspect/
2.okhttp的Get请求+封装
本身OkHttp的请求分为三步:
1.拿到OkHttpClient
OkHttpClient client = new OkHttpClient();
2.构造Request对象
Request request = new Request.Builder()
.get()
.url("https:www.baidu.com")
.build();
3 . 将Request封装为Call
Call call = client.newCall(request);
4 . 根据需要调用同步或者异步请求方法
//同步调用,返回Response,会抛出IO异常
Response response = call.execute();
//异步调用,并设置回调函数
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Toast.makeText(OkHttpActivity.this, "get failed", Toast.LENGTH_SHORT).show();
}
@Override
public void onResponse(Call call, final Response response) throws IOException {
final String res = response.body().string();
runOnUiThread(new Runnable() {
@Override
public void run() {
contentTv.setText(res);
}
});
}
});
注意:
同步调用会阻塞主线程,一般不适用
异步调用的回调函数是在子线程,我们不能在子线程更新UI,需要借助于runOnUiThread()方法或者Handler来处理
但每次都写这样的四步对于程序员来说肯定是痛苦的,所以下面进行封装处理,新建一个OkHttpUtils工具类用于统一的处理网络请求
package cq.cake.okhttpdemo;
import android.content.Context;
import com.facebook.stetho.okhttp3.StethoInterceptor;
import java.io.File;
import java.util.concurrent.TimeUnit;
import okhttp3.Cache;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
/**
* MyApplication -- cq.cake.okhttpdemo
* Created by Small Cake on 2016/12/26 16:02.
* 全局统一使用的OkHttpClient工具
*/
public class OkHttpUtils {
public static final long DEFAULT_READ_TIMEOUT_MILLIS = 15 * 1000;
public static final long DEFAULT_WRITE_TIMEOUT_MILLIS = 20 * 1000;
public static final long DEFAULT_CONNECT_TIMEOUT_MILLIS = 20 * 1000;
private static final long HTTP_RESPONSE_DISK_CACHE_MAX_SIZE = 10 * 1024 * 1024;
private static volatile OkHttpUtils sInstance;
private OkHttpClient mOkHttpClient;
private OkHttpUtils() {
mOkHttpClient = new OkHttpClient.Builder()
.readTimeout(DEFAULT_READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
.writeTimeout(DEFAULT_WRITE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
.connectTimeout(DEFAULT_CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
//FaceBook 网络调试器,可在Chrome调试网络请求,查看SharePreferences,数据库等
.addNetworkInterceptor(new StethoInterceptor())
//http数据log,日志中打印出HTTP请求&响应数据
.addInterceptor(new LoggingInterceptor())
.build();
}
public static OkHttpUtils getInstance() {
if (sInstance == null) {
synchronized (OkHttpUtils.class) {
if (sInstance == null) {
sInstance = new OkHttpUtils();
}
}
}
return sInstance;
}
public OkHttpClient getOkHttpClient() {
return mOkHttpClient;
}
public void setCache(Context appContext) {
final File baseDir = appContext.getApplicationContext().getCacheDir();
if (baseDir != null) {
final File cacheDir = new File(baseDir, "HttpResponseCache");
mOkHttpClient.newBuilder().cache((new Cache(cacheDir, HTTP_RESPONSE_DISK_CACHE_MAX_SIZE)));
}
}
public static void getAsyn(String url, Callback callBack){
OkHttpClient okHttpClient = OkHttpUtils.getInstance().getOkHttpClient();
Request request = new Request.Builder().url(url).build();
okHttpClient.newCall(request).enqueue(callBack);
}
}
这样每次访问数据的时候我们只需要,通过在Activity中调用getAsyn方法就可以了,方便很多。
OkHttpUtils.getAsyn("http://m.yuzetianxia.com", new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
// System.out.println(response.body().string().toString());
final String str = response.body().string();
runOnUiThread(new Runnable() {
@Override
public void run() {
tvView.setText(decodeUnicode(str));
}
});
}
});
注意:
这里返回的数据因为是异步请求,所以是运行在子线程中的,那么想要显示在UI控件上就要用到runOnUiThread方式,或者Handler。
3.okhttp的POST请求+封装
有了get请求的封装,post也是类似的封装,不过多加了一个请求体RequestBody,在Activity中请求的时候我们是用的RequestBody的子类FormBody.Builder构造来写入键值对的,不再使用Map的方式!
在OkHttpUtils中加入方法:
public static void postAsyn(String url, RequestBody body, Callback callBack){
OkHttpClient okHttpClient = OkHttpUtils.getInstance().getOkHttpClient();
Request request = new Request.Builder().url(url).post(body).build();
okHttpClient.newCall(request).enqueue(callBack);
}
然后在Activity中使用:
FormBody.Builder builder = new FormBody.Builder();
builder.add("account","888888");
builder.add("pwd","123456");
OkHttpUtils.postAsyn("http://www.baidu.com/Api/Login/doLogin/",builder.build(),new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
// Log.i("post>>>>",response.body().string().toString());
final String str = response.body().string();
runOnUiThread(new Runnable() {
@Override
public void run() {
tvView.setText(decodeUnicode(str));
}
});
}
});
4.okhttp的上传文件+封装
在OkHttpUtils中加入方法:
public static void upFile(String url, RequestBody body, Callback callBack){
OkHttpClient okHttpClient = OkHttpUtils.getInstance().getOkHttpClient();
Request request = new Request.Builder().url(url).post(body).build();
okHttpClient.newCall(request).enqueue(callBack);
}
在使用的时候需要自己构建文件File并用MultipartBody构建参数
int time = TimeUtils.getTime();
File file = new File(Environment.getExternalStorageDirectory(), "1.png");
if (!file.exists()){
Toast.makeText(this, "文件不存在", Toast.LENGTH_SHORT).show();
return;
}
RequestBody muiltipartBody = new MultipartBody.Builder()
//一定要设置这句
.setType(MultipartBody.FORM)
.addFormDataPart("mid", "3")
.addFormDataPart("time", time+"")
.addFormDataPart("sign", MD5.getSign("3", time))
.addFormDataPart("pic", "1.png", RequestBody.create(MediaType.parse("application/octet-stream"), file))
.build();
OkHttpUtils.upFile("http://m.hxfuuu.cn/Api/Manage/setHeadpic",muiltipartBody,new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
// Log.i("post>>>>",response.body().string().toString());
final String str = response.body().string();
runOnUiThread(new Runnable() {
@Override
public void run() {
tvView.setText(decodeUnicode(str));
}
});
}
});
5.okhttp的下载文件+封装
在OkHttpUtils中加入方法:
public static void getDownload(String url, Callback callBack){
OkHttpClient okHttpClient = OkHttpUtils.getInstance().getOkHttpClient();
Request request = new Request.Builder().url(url).build();
okHttpClient.newCall(request).enqueue(callBack);
}
在得到回调结果的时候对流进行处理,不再是单纯的数据。
OkHttpUtils.getDownload("http://m.hxfuuu.cn/Public/upload/headpic/3.jpg", new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//1.拿到下载文件的总长度
final long total = response.body().contentLength();
//2.在while循环中每次递增我们读取的buf的长度
long sum = 0L;
//拿到字节流
InputStream is = response.body().byteStream();
int len = 0;
File file = new File(Environment.getExternalStorageDirectory(), "n.png");
FileOutputStream fos = new FileOutputStream(file);
byte[] buf = new byte[128];
while ((len = is.read(buf)) != -1){
fos.write(buf, 0, len);
//每次递增
sum += len;
final long finalSum = sum;
Log.d("pyh1", "onResponse: " + finalSum + "/" + total);
runOnUiThread(new Runnable() {
@Override
public void run() {
//将进度设置到TextView中
tvView.setText(finalSum + "/" + total);
}
});
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
fos.flush();
//关闭流
fos.close();
is.close();
}
});
写了这些后,你会发现其实下载文件getDownload方法和getAsyn方法,上传文件upFile方法和postAsyn方法内部方法体都是一样的。确实是一样的,不同的是在Activity中发出请求前的处理和得到数据后的处理不同而已。所以后面两个方法其实都可以省略,关键要知道在具体的请求时应该做什么就可以了!
6.okhttp的https请求
okhttp默认是支持https请求的,比如你用get方法去访问https://www.baidu.com是可以直接访问的,但是针对有的个人自建证书或不被认可的证书,就需要特殊处理一下了!
我们一般是把证书(.crt文件)放在资源文件assets下,然后在okhttp的配置中去配置sslSocketFactory用于访问自建的证书。
下面是完整的OkHttpUtils封装类,方便大家根据自己的需要修改。
package cq.cake.okhttpdemo;
import android.content.Context;
import com.facebook.stetho.okhttp3.StethoInterceptor;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.cert.CertificateFactory;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import okhttp3.Cache;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
/**
* MyApplication -- cq.cake.okhttpdemo
* Created by Small Cake on 2016/12/26 16:02.
* 全局统一使用的OkHttpClient工具
*/
public class OkHttpUtils {
public static final long DEFAULT_READ_TIMEOUT_MILLIS = 15 * 1000;
public static final long DEFAULT_WRITE_TIMEOUT_MILLIS = 20 * 1000;
public static final long DEFAULT_CONNECT_TIMEOUT_MILLIS = 20 * 1000;
private static final long HTTP_RESPONSE_DISK_CACHE_MAX_SIZE = 10 * 1024 * 1024;
private static volatile OkHttpUtils sInstance;
private OkHttpClient mOkHttpClient;
/**
* 需要访问https,且证书只能在本地访问
* @param context
*/
private OkHttpUtils(Context context) {
X509TrustManager trustManager = null;
SSLSocketFactory sslSocketFactory = null;
try {
final InputStream inputStream;
inputStream = context.getAssets().open("yuzetianxia.com.crt"); // 得到证书的输入流
trustManager = trustManagerForCertificates(inputStream);//以流的方式读入证书
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{trustManager}, null);
sslSocketFactory = sslContext.getSocketFactory();
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
} catch (IOException e) {
e.printStackTrace();
}
mOkHttpClient = new OkHttpClient.Builder()
.readTimeout(DEFAULT_READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
.writeTimeout(DEFAULT_WRITE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
.connectTimeout(DEFAULT_CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
//FaceBook 网络调试器,可在Chrome调试网络请求,查看SharePreferences,数据库等
.addNetworkInterceptor(new StethoInterceptor())
//http数据log,日志中打印出HTTP请求&响应数据
.addInterceptor(new LoggingInterceptor())
.sslSocketFactory(sslSocketFactory, trustManager)
.build();
}
private OkHttpUtils() {
mOkHttpClient = new OkHttpClient.Builder()
.readTimeout(DEFAULT_READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
.writeTimeout(DEFAULT_WRITE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
.connectTimeout(DEFAULT_CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
//FaceBook 网络调试器,可在Chrome调试网络请求,查看SharePreferences,数据库等
.addNetworkInterceptor(new StethoInterceptor())
//http数据log,日志中打印出HTTP请求&响应数据
.addInterceptor(new LoggingInterceptor())
.build();
}
public static OkHttpUtils getInstance(Context context) {
if (sInstance == null) {
synchronized (OkHttpUtils.class) {
if (sInstance == null) {
sInstance = new OkHttpUtils(context);
}
}
}
return sInstance;
}
public static OkHttpUtils getInstance() {
if (sInstance == null) {
synchronized (OkHttpUtils.class) {
if (sInstance == null) {
sInstance = new OkHttpUtils();
}
}
}
return sInstance;
}
public OkHttpClient getOkHttpClient() {
return mOkHttpClient;
}
public void setCache(Context appContext) {
final File baseDir = appContext.getApplicationContext().getCacheDir();
if (baseDir != null) {
final File cacheDir = new File(baseDir, "HttpResponseCache");
mOkHttpClient.newBuilder().cache((new Cache(cacheDir, HTTP_RESPONSE_DISK_CACHE_MAX_SIZE)));
}
}
public static void getAsyn(Context context,String url, Callback callBack){
OkHttpClient okHttpClient = OkHttpUtils.getInstance(context).getOkHttpClient();
Request request = new Request.Builder().url(url).build();
okHttpClient.newCall(request).enqueue(callBack);
}
public static void postAsyn(Context context,String url, RequestBody body, Callback callBack){
OkHttpClient okHttpClient = OkHttpUtils.getInstance(context).getOkHttpClient();
Request request = new Request.Builder().url(url).post(body).build();
okHttpClient.newCall(request).enqueue(callBack);
}
public static void getAsyn(String url, Callback callBack){
OkHttpClient okHttpClient = OkHttpUtils.getInstance().getOkHttpClient();
Request request = new Request.Builder().url(url).build();
okHttpClient.newCall(request).enqueue(callBack);
}
public static void postAsyn(String url, RequestBody body, Callback callBack){
OkHttpClient okHttpClient = OkHttpUtils.getInstance().getOkHttpClient();
Request request = new Request.Builder().url(url).post(body).build();
okHttpClient.newCall(request).enqueue(callBack);
}
public static void upFile(String url, RequestBody body, Callback callBack){
OkHttpClient okHttpClient = OkHttpUtils.getInstance().getOkHttpClient();
Request request = new Request.Builder().url(url).post(body).build();
okHttpClient.newCall(request).enqueue(callBack);
}
public static void getDownload(String url, Callback callBack){
OkHttpClient okHttpClient = OkHttpUtils.getInstance().getOkHttpClient();
Request request = new Request.Builder().url(url).build();
okHttpClient.newCall(request).enqueue(callBack);
}
/**
* 以流的方式添加信任证书
*/
/**
* Returns a trust manager that trusts {@code certificates} and none other. HTTPS services whose
* certificates have not been signed by these certificates will fail with a {@code
* SSLHandshakeException}.
* <p>
* <p>This can be used to replace the host platform's built-in trusted certificates with a custom
* set. This is useful in development where certificate authority-trusted certificates aren't
* available. Or in production, to avoid reliance on third-party certificate authorities.
* <p>
* <p>
* <h3>Warning: Customizing Trusted Certificates is Dangerous!</h3>
* <p>
* <p>Relying on your own trusted certificates limits your server team's ability to update their
* TLS certificates. By installing a specific set of trusted certificates, you take on additional
* operational complexity and limit your ability to migrate between certificate authorities. Do
* not use custom trusted certificates in production without the blessing of your server's TLS
* administrator.
*/
private X509TrustManager trustManagerForCertificates(InputStream in)
throws GeneralSecurityException {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
Collection<? extends java.security.cert.Certificate> certificates = certificateFactory.generateCertificates(in);
if (certificates.isEmpty()) {
throw new IllegalArgumentException("expected non-empty set of trusted certificates");
}
// Put the certificates a key store.
char[] password = "password".toCharArray(); // Any password will work.
KeyStore keyStore = newEmptyKeyStore(password);
int index = 0;
for (java.security.cert.Certificate certificate : certificates) {
String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(certificateAlias, certificate);
}
// Use it to build an X509 trust manager.
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(
KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, password);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:"
+ Arrays.toString(trustManagers));
}
return (X509TrustManager) trustManagers[0];
}
/**
* 添加password
* @param password
* @return
* @throws GeneralSecurityException
*/
private KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException {
try {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); // 这里添加自定义的密码,默认
InputStream in = null; // By convention, 'null' creates an empty key store.
keyStore.load(in, password);
return keyStore;
} catch (IOException e) {
throw new AssertionError(e);
}
}
}
使用的时候需要多传入一个资源文件
OkHttpUtils.getAsyn(this,"https://m.yuzetianxia.com", new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
// System.out.println(response.body().string().toString());
final String str = response.body().string();
runOnUiThread(new Runnable() {
@Override
public void run() {
tvView.setText(decodeUnicode(str));
}
});
}
});
未完待续。。。