美文网首页Android开发
OkHttp3入门介绍

OkHttp3入门介绍

作者: linzhiyong | 来源:发表于2018-07-12 09:17 被阅读0次

    版权所有,转载请注明出处: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配置:

    <uses-permission android:name="android.permission.INTERNET" />
    

    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入门介绍

        本文链接:https://www.haomeiwen.com/subject/dzhlpftx.html