Android okhttp

一、okhttp介绍

OKHttp优点

  1. 支持HTTP2/SPDY(SPDY是Google开发的基于TCP的传输层协议,用以最小化网络延迟,提升网络速度,优化用户的网络使用体验。)
  2. socket自动选择最好路线,并支持自动重连,拥有自动维护的socket连接池,减少握手次数,减少了请求延迟,共享Socket,减少对服务器的请求次数。
  3. 基于Headers的缓存策略减少重复的网络请求。
  4. 拥有Interceptors轻松处理请求与响应(自动处理GZip压缩)。

OKHttp的功能

  • OkHttpClient基本参数配置介绍
  • 普通GET请求(同步/异步)
  • 普通POST请求(同步/异步)
  • 根据tag取消请求
  • POST请求提交String
  • POST请求提交流
  • POST请求提交JSON(实体转JSON)
  • POST请求提交普通Form表单
  • POST请求提交混合Form表单(文本参数+文件)
  • POST请求提交单/多文件(带进度条)
  • GET请求下载文件(带进度条)

 

一、OkHttpClient基本参数介绍

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

 

二、如何提高效率

OkHttpClient的实例有一个就可以了。为什么用一个实例呢?

因为每个OkHttpClient实例都有自己的连接池和线程池,用同一个实例的话就能更好的复用线程池和链接池,能够减少延迟和减小内存的消耗。

 

三、普通GET的同步与异步请求

       String url="http://xxx.xxx.xxx.xxx/test.php";
        //创建请求体
        Request request=new Request.Builder()
                .url(url)
                .get()  //get请求
                .addHeader("name","value")//请求头参数
                .tag("connect1")//为当前request请求增加tag,可在okHttpClient中使用tag获取到当前请求
                .build();
        final Call call=OkhttpUtils.okHttpClient().newCall(request);
        //同步请求在安卓中要在分线程的
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Response response=call.execute();
                    Log.e(TAG, "run: "+response.body().string() );
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        //异步请求
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                //失败
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if(response.code()==200){
                    String result=response.body().string();
                    Log.e(TAG, "onResponse: "+result );
                }else{
                    Log.e(TAG, "onResponse: "+response.message() );;
                }
            }
        });
    }

注意:
1、Call对象作为请求执行者,可以取消请求,同时Call请求只能执行一次;
2、Response作为响应体,获取返回的string使用response.body().string(),获取返回的字节使用response.body().bytes(),获取返回的字节流(如下载文件)使用response.body().byteStream();


 

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

        String url="http://xxx.xxx.xxx.xxx/test.php";
        String str="我一会就会被发送到服务器端了";
        //create()有四个重载方法,支持,文本,文件,流等的传输
        RequestBody body=RequestBody.create(MediaType.parse("text/html; charset=utf-8"),str);
        Request request=new Request.Builder()
                .url(url)
                .post(body)
                .build();
        final Call call=OkhttpUtils.okHttpClient().newCall(request);
        //同步
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    call.execute().body().string();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        //异步
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                //失败
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                //成功
                if(response.code()==200){
                    Log.e(TAG, "onResponse: "+response.body().string());
                }
            }
        });

 

提示: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) {
            }
        }
    }

}

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OkHttp是一个开源的轻量级框架,由Square公司贡献,用于在Android端进行网络请求。它提供了几乎和java.net.HttpURLConnection一样的API,因此使用OkHttp无需重写您程序中的网络代码。OkHttp可以替代HttpUrlConnection和Apache HttpClient,特别是在Android API23之后,HttpClient已经被移除。\[1\]\[2\] OkHttp是一个相对成熟的解决方案,在Android API19 4.4的源码中可以看到HttpURLConnection已经被替换成了OkHttp。此外,OkHttp还支持HTTP/2协议,允许连接到同一个主机地址的所有请求共享Socket,从而提高请求效率。\[3\] #### 引用[.reference_title] - *1* [AndroidOkHttp的理解和使用](https://blog.csdn.net/JMW1407/article/details/117898465)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Android之网络请求2————OkHttp的基本使用](https://blog.csdn.net/weixin_50961942/article/details/127738248)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Android OkHttp](https://blog.csdn.net/weixin_44826221/article/details/109801566)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值