OkHttp3入门介绍

版权所有,转载请注明出处:linzhiyong https://www.jianshu.com/p/af144d662bfd https://blog.csdn.net/u012527802/article/details/81013772

相关文章
1、OkHttp3入门介绍:https://www.jianshu.com/p/af144d662bfd
2、OkHttp3入门介绍之Cookie持久化:https://www.jianshu.com/p/23b35d403148

OkHttp是一个处理网络请求的开源项目,是安卓端最火热的轻量级框架,本文主要介绍OkHttp3的基本使用方法。
官网:http://square.github.io/okhttp/
Github:https://github.com/square/okhttp
OkHttp3Demo传送门:https://github.com/linzhiyong/OkHttp3Demo
服务端Demo传送门:https://github.com/linzhiyong/SpringMVCDemo

结合自己的项目经验,主要从以下几方面介绍:

  1. OkHttpClient基本参数配置介绍
  2. 普通GET请求(同步/异步)
  3. 普通POST请求(同步/异步)
  4. 根据tag取消请求
  5. POST请求提交String
  6. POST请求提交流
  7. POST请求提交JSON(实体转JSON)
  8. POST请求提交普通Form表单
  9. POST请求提交混合Form表单(文本参数+文件)
  10. POST请求提交单/多文件(带进度条)
  11. GET请求下载文件(带进度条)

开发配置

使用AndroidStudio开发,在app的build.gradle文件中增加对okhttp3的依赖:

dependencies {
    implementation 'com.squareup.okhttp3:okhttp:3.10.0'
}

网络请求需要申请网络权限,需要在AndroidManifest.xml配置:


OkHttpClient基本参数介绍

OkHttpClient是通过OkHttpClient.Builder来配置参数,基础参数如下:

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();

至于其他参数,如Cookie保持、Https证书设置、Interceptor拦截器设置等,会在后续章节中介绍。

注:
1、这里建议在项目中创造一个OkHttpClient实例并重复使用,这是因为每个实例都有它自己的连接池和线程池,重用连接池和线程池可以减少延迟同时节省内存。
2、本例中,我将OkHttpClient实例封装在LOkHttp3Utils中,使用LOkHttp3Utils.okHttpClient()获取。

普通GET请求(同步/异步)

// 创建请求体
Request request = new Request.Builder()
                .url(url) // 请求地址
                .get() // get请求
                .addHeader("name", "value") // 请求头参数
                .tag("getSync") // 为当前request请求增加tag,可在okHttpClient中使用tag获取到当前请求
                .build();
Call call = LOkHttp3Utils.okHttpClient().newCall(request);
// 同步请求
Response response = call.execute();

// 异步请求
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对象作为请求执行者,可以取消请求,同时Call请求只能执行一次;
2、Response作为响应体,获取返回的string使用response.body().string(),获取返回的字节使用response.body().bytes(),获取返回的字节流(如下载文件)使用response.body().byteStream();

普通POST请求(同步/异步)

// 创建请求body,MediaType请求包体类型
RequestBody requestBody = RequestBody.create(MediaType.parse("text/html; charset=utf-8"), content);
// 创建请求体
Request request = new Request.Builder()
        .url(url)
        .post(requestBody)
        .addHeader("name", "value")
        .tag("postSync")
        .build();
Call call = LOkHttp3Utils.okHttpClient().newCall(request);
// 同步请求
Response response = call.execute();

// 异步请求
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作为请求提的包体,有多种实现形式:FormBody、MultipartBody,作为提交string、file、stream、form的载体;

根据tag取消请求

在创建请求Request的时候,如果添加了tag属性,可以通过tag取消请求,下面是具体实现:

/**
 * 根据Tag取消请求
 *
 * @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();
        }
    }
}

/**
 * 取消所有请求请求
 *
 * @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();
    }
}

注:为省去一些重复的代码量,下面列出的关于提交string、json、file...方法,本文只写到如何创建RequestBody,后续的创建Request、Call、同步异步调用不在重复罗列;

POST请求提交String

// 提交普通字符串
String content = "普通字符串";
RequestBody requestBody = RequestBody.create(MediaType.parse("text/html; charset=utf-8"), content);

POST请求提交流

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("字符串作为流提交");
        // 重写此处可以实现文件上传进度检测
    }
};

POST请求提交JSON(实体转JSON)

// 提交JSON
String bodyStr = "json";
RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), bodyStr);

POST请求提交普通Form表单

FormBody formBody = new FormBody.Builder()
        .add("key1", "value1")
        .add("key2", "value2")
        .add("key3", "value3")
        .build();

POST请求提交混合Form表单(文本参数+文件)

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请求提交单/多文件(带进度条)

1. 提交单文件(不带进度条)
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. 提交多文件(不带进度条)
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. 提交单文件(!!!带进度条!!!)

根据前面提交流的方式,实现RequestBody的writeTo(BufferedSink sink)方法监听文件传输进度:

// 使用方式
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 显示当前进度
    }

    @Override
    public void onFinish(File file) {

    }

    @Override
    public void onError(Exception e) {

    }
});

/**
 * 创建文件requestbody,自定义进度监听器
 *
 * @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接口作为上传下载进度监听器:

public interface ProgressListener {

    void onStart();

    void onProgress(long total, float progress);

    void onFinish(File file);

    void onError(Exception e);

}

GET请求下载文件(带进度条)

此处需要重新实现Callback接口,获取到Response对象中的文件流,进行文件保存操作,如需监听进度,则在文件流读取的时候进行处理;

1. 下载文件(不带进度条)
// 先看使用效果
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) {

    }
});

// 实现Callback接口,进行文件保存操作
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. 下载文件(!!!带进度条!!!)
// 使用方式
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同样实现了Callback,但是多了对下载进度的监听
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) {
            }
        }
    }

}

你可能感兴趣的:(OkHttp3入门介绍)