版權(quán)所有,轉(zhuǎn)載請(qǐng)注明出處:linzhiyong http://www.itdecent.cn/p/af144d662bfd https://blog.csdn.net/u012527802/article/details/81013772
相關(guān)文章
1、OkHttp3入門介紹:http://www.itdecent.cn/p/af144d662bfd
2、OkHttp3入門介紹之Cookie持久化:http://www.itdecent.cn/p/23b35d403148
OkHttp是一個(gè)處理網(wǎng)絡(luò)請(qǐng)求的開源項(xiàng)目,是安卓端最火熱的輕量級(jí)框架,本文主要介紹OkHttp3的基本使用方法。
官網(wǎng):http://square.github.io/okhttp/
Github:https://github.com/square/okhttp
OkHttp3Demo傳送門:https://github.com/linzhiyong/OkHttp3Demo
服務(wù)端Demo傳送門:https://github.com/linzhiyong/SpringMVCDemo
結(jié)合自己的項(xiàng)目經(jīng)驗(yàn),主要從以下幾方面介紹:
- OkHttpClient基本參數(shù)配置介紹
- 普通GET請(qǐng)求(同步/異步)
- 普通POST請(qǐng)求(同步/異步)
- 根據(jù)tag取消請(qǐng)求
- POST請(qǐng)求提交String
- POST請(qǐng)求提交流
- POST請(qǐng)求提交JSON(實(shí)體轉(zhuǎn)JSON)
- POST請(qǐng)求提交普通Form表單
- POST請(qǐng)求提交混合Form表單(文本參數(shù)+文件)
- POST請(qǐng)求提交單/多文件(帶進(jìn)度條)
- GET請(qǐng)求下載文件(帶進(jìn)度條)
開發(fā)配置
使用AndroidStudio開發(fā),在app的build.gradle文件中增加對(duì)okhttp3的依賴:
dependencies {
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
}
網(wǎng)絡(luò)請(qǐng)求需要申請(qǐng)網(wǎng)絡(luò)權(quán)限,需要在AndroidManifest.xml配置:
<uses-permission android:name="android.permission.INTERNET" />
OkHttpClient基本參數(shù)介紹
OkHttpClient是通過(guò)OkHttpClient.Builder來(lái)配置參數(shù),基礎(chǔ)參數(shù)如下:
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.readTimeout(HTTP_TIME_OUT, TimeUnit.SECONDS)
.writeTimeout(HTTP_TIME_OUT, TimeUnit.SECONDS)
.connectTimeout(HTTP_TIME_OUT, TimeUnit.SECONDS)
);
OkHttpClient okHttpClient = okHttpClient = builder.build();
至于其他參數(shù),如Cookie保持、Https證書設(shè)置、Interceptor攔截器設(shè)置等,會(huì)在后續(xù)章節(jié)中介紹。
注:
1、這里建議在項(xiàng)目中創(chuàng)造一個(gè)OkHttpClient實(shí)例并重復(fù)使用,這是因?yàn)槊總€(gè)實(shí)例都有它自己的連接池和線程池,重用連接池和線程池可以減少延遲同時(shí)節(jié)省內(nèi)存。
2、本例中,我將OkHttpClient實(shí)例封裝在LOkHttp3Utils中,使用LOkHttp3Utils.okHttpClient()獲取。
普通GET請(qǐng)求(同步/異步)
// 創(chuàng)建請(qǐng)求體
Request request = new Request.Builder()
.url(url) // 請(qǐng)求地址
.get() // get請(qǐng)求
.addHeader("name", "value") // 請(qǐng)求頭參數(shù)
.tag("getSync") // 為當(dāng)前request請(qǐng)求增加tag,可在okHttpClient中使用tag獲取到當(dāng)前請(qǐng)求
.build();
Call call = LOkHttp3Utils.okHttpClient().newCall(request);
// 同步請(qǐng)求
Response response = call.execute();
// 異步請(qǐng)求
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.i("", "");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
int code = response.code();
if (code == 200) {
String result = response.body().string();
Log.i("r", result);
}
else {
Log.e("e", response.message());
}
}
});
注:
1、Call對(duì)象作為請(qǐng)求執(zhí)行者,可以取消請(qǐng)求,同時(shí)Call請(qǐng)求只能執(zhí)行一次;
2、Response作為響應(yīng)體,獲取返回的string使用response.body().string(),獲取返回的字節(jié)使用response.body().bytes(),獲取返回的字節(jié)流(如下載文件)使用response.body().byteStream();
普通POST請(qǐng)求(同步/異步)
// 創(chuàng)建請(qǐng)求body,MediaType請(qǐng)求包體類型
RequestBody requestBody = RequestBody.create(MediaType.parse("text/html; charset=utf-8"), content);
// 創(chuàng)建請(qǐng)求體
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.addHeader("name", "value")
.tag("postSync")
.build();
Call call = LOkHttp3Utils.okHttpClient().newCall(request);
// 同步請(qǐng)求
Response response = call.execute();
// 異步請(qǐng)求
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.i("", "");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
int code = response.code();
if (code == 200) {
String result = response.body().string();
Log.i("r", result);
}
else {
Log.e("e", response.message());
}
}
});
注:RequestBody作為請(qǐng)求提的包體,有多種實(shí)現(xiàn)形式:FormBody、MultipartBody,作為提交string、file、stream、form的載體;
根據(jù)tag取消請(qǐng)求
在創(chuàng)建請(qǐng)求Request的時(shí)候,如果添加了tag屬性,可以通過(guò)tag取消請(qǐng)求,下面是具體實(shí)現(xiàn):
/**
* 根據(jù)Tag取消請(qǐng)求
*
* @param client OkHttpClient
* @param tag tag
*/
public static void cancelTag(OkHttpClient client, Object tag) {
if (client == null || tag == null) return;
for (Call call : client.dispatcher().queuedCalls()) {
if (tag.equals(call.request().tag())) {
call.cancel();
}
}
for (Call call : client.dispatcher().runningCalls()) {
if (tag.equals(call.request().tag())) {
call.cancel();
}
}
}
/**
* 取消所有請(qǐng)求請(qǐng)求
*
* @param client OkHttpClient
*/
public static void cancelAll(OkHttpClient client) {
if (client == null) return;
for (Call call : client.dispatcher().queuedCalls()) {
call.cancel();
}
for (Call call : client.dispatcher().runningCalls()) {
call.cancel();
}
}
注:為省去一些重復(fù)的代碼量,下面列出的關(guān)于提交string、json、file...方法,本文只寫到如何創(chuàng)建RequestBody,后續(xù)的創(chuàng)建Request、Call、同步異步調(diào)用不在重復(fù)羅列;
POST請(qǐng)求提交String
// 提交普通字符串
String content = "普通字符串";
RequestBody requestBody = RequestBody.create(MediaType.parse("text/html; charset=utf-8"), content);
POST請(qǐng)求提交流
RequestBody requestBody = new RequestBody() {
@Override
public MediaType contentType() {
return MediaType.parse("application/octet-stream; charset=utf-8");
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
sink.writeUtf8("字符串作為流提交");
// 重寫此處可以實(shí)現(xiàn)文件上傳進(jìn)度檢測(cè)
}
};
POST請(qǐng)求提交JSON(實(shí)體轉(zhuǎn)JSON)
// 提交JSON
String bodyStr = "json";
RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), bodyStr);
POST請(qǐng)求提交普通Form表單
FormBody formBody = new FormBody.Builder()
.add("key1", "value1")
.add("key2", "value2")
.add("key3", "value3")
.build();
POST請(qǐng)求提交混合Form表單(文本參數(shù)+文件)
File file = new File("filePath");
RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream; charset=utf-8"), file);
// 方式一
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("key1", "value1")
.addFormDataPart("key2", "value2")
.addFormDataPart("key3", "value3")
.addFormDataPart("file1", "name1", fileBody)
.build();
// 方式二
FormBody formBody = new FormBody.Builder()
.add("key1", "value1")
.add("key2", "value2")
.add("key3", "value3")
.build();
RequestBody requestBody1 = new MultipartBody.Builder()
.addPart(Headers.of(
"Content-Disposition",
"form-data; name=\"params\""),
formBody)
.addPart(Headers.of(
"Content-Disposition",
"form-data; name=\"file\"; filename=\"plans.xml\""),
fileBody)
.build();
POST請(qǐng)求提交單/多文件(帶進(jìn)度條)
1. 提交單文件(不帶進(jìn)度條)
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "test.txt");
RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
// 方式一
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file1", file.getName(), fileBody)
.build();
// 方式二
RequestBody requestBody1 = new MultipartBody.Builder()
.addPart(Headers.of(
"Content-Disposition",
"form-data; name=\"file1\"; filename=\"" + file.getName() + "\""),
fileBody)
.build();
2. 提交多文件(不帶進(jìn)度條)
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "test.txt");
RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
RequestBody fileBody2 = RequestBody.create(MediaType.parse("application/octet-stream"), file);
// 方式一
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file1", file.getName(), fileBody)
.addFormDataPart("file2", file.getName(), fileBody2)
.build();
// 方式二
RequestBody requestBody1 = new MultipartBody.Builder()
.addPart(Headers.of(
"Content-Disposition",
"form-data; name=\"file1\"; filename=\"" + file.getName() + "\""),
fileBody)
.addPart(Headers.of(
"Content-Disposition",
"form-data; name=\"file2\"; filename=\"" + file.getName() + "\""),
fileBody2)
.build();
3. 提交單文件(?。?!帶進(jìn)度條?。。。?/h5>
根據(jù)前面提交流的方式,實(shí)現(xiàn)RequestBody的writeTo(BufferedSink sink)方法監(jiān)聽文件傳輸進(jìn)度:
// 使用方式
RequestBody fileBody = LOkHttp3Utils.createProgressRequestBody(MediaType.parse("application/octet-stream"), file,
new ProgressListener() {
@Override
public void onStart() {
}
@Override
public void onProgress(long total, float progress) {
// progress 顯示當(dāng)前進(jìn)度
}
@Override
public void onFinish(File file) {
}
@Override
public void onError(Exception e) {
}
});
/**
* 創(chuàng)建文件requestbody,自定義進(jìn)度監(jiān)聽器
*
* @param mediaType
* @param file
* @param listener
* @return
*/
public static RequestBody createProgressRequestBody(final MediaType mediaType, final File file,
final ProgressListener listener) {
return new RequestBody() {
@Override
public MediaType contentType() {
return mediaType;
}
@Override
public long contentLength() throws IOException {
return file.length();
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
listener.onStart();
Source source;
try {
source = Okio.source(file);
//sink.writeAll(source);
Buffer buf = new Buffer();
Long remaining = contentLength();
for (long readCount; (readCount = source.read(buf, 2048)) != -1; ) {
sink.write(buf, readCount);
listener.onProgress(contentLength(), 1 - (float)(remaining -= readCount) / contentLength());
}
listener.onFinish(file);
} catch (Exception e) {
listener.onError(e);
e.printStackTrace();
}
}
};
}
定義ProgressListener接口作為上傳下載進(jìn)度監(jiān)聽器:
public interface ProgressListener {
void onStart();
void onProgress(long total, float progress);
void onFinish(File file);
void onError(Exception e);
}
GET請(qǐng)求下載文件(帶進(jìn)度條)
此處需要重新實(shí)現(xiàn)Callback接口,獲取到Response對(duì)象中的文件流,進(jìn)行文件保存操作,如需監(jiān)聽進(jìn)度,則在文件流讀取的時(shí)候進(jìn)行處理;
1. 下載文件(不帶進(jìn)度條)
// 先看使用效果
Request request = new Request.Builder()
.url(url) // 文件地址
.get()
.addHeader("name", "value")
.tag("getFileAsync")
.build();
final Call call = LOkHttp3Utils.okHttpClient().newCall(request);
call.enqueue(new FileNoProgressCallback(Environment.getExternalStorageDirectory().getAbsolutePath(), "test.png") {
@Override
public void onFinish(File file) {
}
@Override
public void onError(Exception e) {
}
});
// 實(shí)現(xiàn)Callback接口,進(jìn)行文件保存操作
public abstract class FileNoProgressCallback implements Callback {
private String destFileDir;
private String destFileName;
public FileNoProgressCallback(String destFileDir, String destFileName) {
this.destFileDir = destFileDir;
this.destFileName = destFileName;
}
@Override
public void onFailure(Call call, IOException e) {
onError(e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
this.saveFile(response);
}
private void saveFile(Response response) throws IOException {
InputStream is = null;
byte[] buf = new byte[2048];
FileOutputStream fos = null;
try {
is = response.body().byteStream();
final long total = response.body().contentLength();
long sum = 0L;
File dir = new File(this.destFileDir);
if (!dir.exists()) {
dir.mkdirs();
}
File file = new File(dir, this.destFileName);
fos = new FileOutputStream(file);
int len = 0;
while ((len = is.read(buf)) != -1) {
sum += (long) len;
fos.write(buf, 0, len);
}
fos.flush();
onFinish(file);
} catch (Exception e) {
onError(e);
} finally {
try {
response.body().close();
if (is != null) {
is.close();
}
} catch (IOException var23) {
}
try {
if (fos != null) {
fos.close();
}
} catch (IOException var22) {
}
}
}
public abstract void onFinish(File file);
public abstract void onError(Exception e);
}
2. 下載文件(?。。нM(jìn)度條?。。。?/h5>
// 使用方式
Request request = new Request.Builder()
.url(url) // 文件地址
.get()
.addHeader("name", "value")
.tag("getFileProgressAsync")
.build();
final Call call = LOkHttp3Utils.okHttpClient().newCall(request);
call.enqueue(new FileCallback(Environment.getExternalStorageDirectory().getAbsolutePath(), "test.png") {
@Override
public void onStart() {
}
@Override
public void onProgress(long total, float progress) {
}
@Override
public void onFinish(File file) {
}
@Override
public void onError(Exception e) {
}
});
// FileCallback同樣實(shí)現(xiàn)了Callback,但是多了對(duì)下載進(jìn)度的監(jiān)聽
public abstract class FileCallback implements Callback, ProgressListener {
private String destFileDir;
private String destFileName;
public FileCallback(String destFileDir, String destFileName) {
this.destFileDir = destFileDir;
this.destFileName = destFileName;
}
@Override
public void onFailure(Call call, IOException e) {
onError(e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
this.saveFile(response);
}
private void saveFile(Response response) throws IOException {
onStart();
InputStream is = null;
byte[] buf = new byte[2048];
FileOutputStream fos = null;
try {
is = response.body().byteStream();
final long total = response.body().contentLength();
long sum = 0L;
File dir = new File(this.destFileDir);
if (!dir.exists()) {
dir.mkdirs();
}
File file = new File(dir, this.destFileName);
fos = new FileOutputStream(file);
int len = 0;
while ((len = is.read(buf)) != -1) {
sum += (long) len;
fos.write(buf, 0, len);
onProgress(total, (float) sum * 1.0F / (float) total);
}
fos.flush();
onFinish(file);
} catch (Exception e) {
onError(e);
} finally {
try {
response.body().close();
if (is != null) {
is.close();
}
} catch (IOException var23) {
}
try {
if (fos != null) {
fos.close();
}
} catch (IOException var22) {
}
}
}
}
// 使用方式
Request request = new Request.Builder()
.url(url) // 文件地址
.get()
.addHeader("name", "value")
.tag("getFileProgressAsync")
.build();
final Call call = LOkHttp3Utils.okHttpClient().newCall(request);
call.enqueue(new FileCallback(Environment.getExternalStorageDirectory().getAbsolutePath(), "test.png") {
@Override
public void onStart() {
}
@Override
public void onProgress(long total, float progress) {
}
@Override
public void onFinish(File file) {
}
@Override
public void onError(Exception e) {
}
});
// FileCallback同樣實(shí)現(xiàn)了Callback,但是多了對(duì)下載進(jìn)度的監(jiān)聽
public abstract class FileCallback implements Callback, ProgressListener {
private String destFileDir;
private String destFileName;
public FileCallback(String destFileDir, String destFileName) {
this.destFileDir = destFileDir;
this.destFileName = destFileName;
}
@Override
public void onFailure(Call call, IOException e) {
onError(e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
this.saveFile(response);
}
private void saveFile(Response response) throws IOException {
onStart();
InputStream is = null;
byte[] buf = new byte[2048];
FileOutputStream fos = null;
try {
is = response.body().byteStream();
final long total = response.body().contentLength();
long sum = 0L;
File dir = new File(this.destFileDir);
if (!dir.exists()) {
dir.mkdirs();
}
File file = new File(dir, this.destFileName);
fos = new FileOutputStream(file);
int len = 0;
while ((len = is.read(buf)) != -1) {
sum += (long) len;
fos.write(buf, 0, len);
onProgress(total, (float) sum * 1.0F / (float) total);
}
fos.flush();
onFinish(file);
} catch (Exception e) {
onError(e);
} finally {
try {
response.body().close();
if (is != null) {
is.close();
}
} catch (IOException var23) {
}
try {
if (fos != null) {
fos.close();
}
} catch (IOException var22) {
}
}
}
}