OkHttp3入門介紹

版權(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),主要從以下幾方面介紹:

  1. OkHttpClient基本參數(shù)配置介紹
  2. 普通GET請(qǐng)求(同步/異步)
  3. 普通POST請(qǐng)求(同步/異步)
  4. 根據(jù)tag取消請(qǐng)求
  5. POST請(qǐng)求提交String
  6. POST請(qǐng)求提交流
  7. POST請(qǐng)求提交JSON(實(shí)體轉(zhuǎn)JSON)
  8. POST請(qǐng)求提交普通Form表單
  9. POST請(qǐng)求提交混合Form表單(文本參數(shù)+文件)
  10. POST請(qǐng)求提交單/多文件(帶進(jìn)度條)
  11. 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) {
            }
        }
    }

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容