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公司開發(fā)的用于檢測okhttp日志的插件包,用于在chrome瀏覽器中調(diào)試okhttp網(wǎng)絡(luò)訪問數(shù)據(jù)非常方便。
stetho需要在Application中初始化
Stetho.initializeWithDefaults(this);
然后在chrome中輸入地址:chrome://inspect/
2.okhttp的Get請求+封裝
本身OkHttp的請求分為三步:
1.拿到OkHttpClient
OkHttpClient client = new OkHttpClient();
2.構(gòu)造Request對象
Request request = new Request.Builder()
.get()
.url("https:www.baidu.com")
.build();
3 . 將Request封裝為Call
Call call = client.newCall(request);
4 . 根據(jù)需要調(diào)用同步或者異步請求方法
//同步調(diào)用,返回Response,會拋出IO異常
Response response = call.execute();
//異步調(diào)用,并設(shè)置回調(diào)函數(shù)
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);
}
});
}
});
注意:
同步調(diào)用會阻塞主線程,一般不適用
異步調(diào)用的回調(diào)函數(shù)是在子線程,我們不能在子線程更新UI,需要借助于runOnUiThread()方法或者Handler來處理
但每次都寫這樣的四步對于程序員來說肯定是痛苦的,所以下面進(jìn)行封裝處理,新建一個OkHttpUtils工具類用于統(tǒng)一的處理網(wǎng)絡(luò)請求
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.
* 全局統(tǒng)一使用的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 網(wǎng)絡(luò)調(diào)試器,可在Chrome調(diào)試網(wǎng)絡(luò)請求,查看SharePreferences,數(shù)據(jù)庫等
.addNetworkInterceptor(new StethoInterceptor())
//http數(shù)據(jù)log,日志中打印出HTTP請求&響應(yīng)數(shù)據(jù)
.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);
}
}
這樣每次訪問數(shù)據(jù)的時候我們只需要,通過在Activity中調(diào)用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));
}
});
}
});
注意:
這里返回的數(shù)據(jù)因?yàn)槭钱惒秸埱螅允沁\(yùn)行在子線程中的,那么想要顯示在UI控件上就要用到runOnUiThread方式,或者Handler。
3.okhttp的POST請求+封裝
有了get請求的封裝,post也是類似的封裝,不過多加了一個請求體RequestBody,在Activity中請求的時候我們是用的RequestBody的子類FormBody.Builder構(gòu)造來寫入鍵值對的,不再使用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);
}
在使用的時候需要自己構(gòu)建文件File并用MultipartBody構(gòu)建參數(shù)
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()
//一定要設(shè)置這句
.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);
}
在得到回調(diào)結(jié)果的時候?qū)α鬟M(jìn)行處理,不再是單純的數(shù)據(jù)。
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循環(huán)中每次遞增我們讀取的buf的長度
long sum = 0L;
//拿到字節(jié)流
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() {
//將進(jìn)度設(shè)置到TextView中
tvView.setText(finalSum + "/" + total);
}
});
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
fos.flush();
//關(guān)閉流
fos.close();
is.close();
}
});
寫了這些后,你會發(fā)現(xiàn)其實(shí)下載文件getDownload方法和getAsyn方法,上傳文件upFile方法和postAsyn方法內(nèi)部方法體都是一樣的。確實(shí)是一樣的,不同的是在Activity中發(fā)出請求前的處理和得到數(shù)據(jù)后的處理不同而已。所以后面兩個方法其實(shí)都可以省略,關(guān)鍵要知道在具體的請求時應(yīng)該做什么就可以了!
6.okhttp的https請求
okhttp默認(rèn)是支持https請求的,比如你用get方法去訪問https://www.baidu.com是可以直接訪問的,但是針對有的個人自建證書或不被認(rèn)可的證書,就需要特殊處理一下了!
我們一般是把證書(.crt文件)放在資源文件assets下,然后在okhttp的配置中去配置sslSocketFactory用于訪問自建的證書。
下面是完整的OkHttpUtils封裝類,方便大家根據(jù)自己的需要修改。
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.
* 全局統(tǒng)一使用的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 網(wǎng)絡(luò)調(diào)試器,可在Chrome調(diào)試網(wǎng)絡(luò)請求,查看SharePreferences,數(shù)據(jù)庫等
.addNetworkInterceptor(new StethoInterceptor())
//http數(shù)據(jù)log,日志中打印出HTTP請求&響應(yīng)數(shù)據(jù)
.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 網(wǎng)絡(luò)調(diào)試器,可在Chrome調(diào)試網(wǎng)絡(luò)請求,查看SharePreferences,數(shù)據(jù)庫等
.addNetworkInterceptor(new StethoInterceptor())
//http數(shù)據(jù)log,日志中打印出HTTP請求&響應(yīng)數(shù)據(jù)
.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()); // 這里添加自定義的密碼,默認(rèn)
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));
}
});
}
});
未完待續(xù)。。。