OkHttp使用详解

一、介绍

OkHttp是一个第三方类库,用于android中请求网络。这是一个开源项目,是安卓端最火热的轻量级框架,由移动支付Square公司贡献。用于替代HttpUrlConnection和Apache HttpClient,HttpUrlConnection使用较复杂,android API23 里已移除HttpClient。OkHttp 处理了很多网络疑难杂症:会从很多常用的连接问题中自动恢复。如果您的服务器配置了多个IP地址,当第一个IP连接失败的时候,OkHttp会自动尝试下一个IP。OkHttp还处理了代理服务器问题和SSL握手失败问题。

二、创建OkHttpClient

public OkHttpUtil() {
    // okhttp报文打印拦截器
    HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
        @Override
        public void log(String message) {
            Log.i(TAG, "----http报文:" + message);
        }
    });
    loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

    okHttpClient = new OkHttpClient.Builder()
            .addInterceptor(loggingInterceptor)
            .connectTimeout(10, TimeUnit.SECONDS)
            .readTimeout(10, TimeUnit.SECONDS)
            .writeTimeout(10, TimeUnit.SECONDS)
            .build();
}

OkHttpUtil是单例模式,全局中新建一个OkHttpClient的实例就可以了。

1、设置日志打印拦截器

    HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
        @Override
        public void log(String message) {
            Log.i(TAG, "----http报文:" + message);
        }
    });
    loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

2、设置超时

没有响应时使用超时结束call。没有响应的原因可能是客户点连接问题、服务器可用性问题或者这之间的其他东西。OkHttp支持连接,读取和写入超时。

    okHttpClient = new OkHttpClient.Builder()
            .addInterceptor(loggingInterceptor)
            .connectTimeout(10, TimeUnit.SECONDS) // 连接超时
            .readTimeout(10, TimeUnit.SECONDS) // 读超时
            .writeTimeout(10, TimeUnit.SECONDS) // 写超时
            .build();

三、Get请求

1、同步请求

public String get(String url) {
    Request.Builder builder = new Request.Builder();
    Request request = builder.get()  // 默认是get方式,不调用这个也一样
            .url(url)
            .build();
    try {
        Response response = okHttpClient.newCall(request).execute();
        if (!response.isSuccessful()) {
            return null;
        }
        return response.body().string();
    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }
}

1)、发送一个get请求的步骤,首先构造一个Request对象,参数最起码有个url,当然你可以通过Request.Builder设置更多的参数比如:header、method等。

2)、然后通过request的对象去构造得到一个Call对象,类似于将你的请求封装成了任务,既然是任务,就会有execute()和cancel()等方法。

3)、最后我们希望以同步的方式去执行请求,所以我们调用的是call.execute(),这时程序会阻塞,网络返回后继续向下执行。

网络请求成功会返回一个Response对象,通过response.isSuccessful()可以判断返回码是否是成功,如果是成功通过response.body().string()获取返回的body数据。

2、异步请求

public void get(String url, final ResponseListener listener) {
    Request.Builder builder = new Request.Builder();
    Request request = builder.get()  // 默认是get方式,不调用这个也一样
            .url(url)
            .build();

    okHttpClient.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
            listener.onFailure(e.getMessage());
        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {
            if (response.isSuccessful()) {
                listener.onSucess(response.body().string());
            } else {
                listener.onFailure("response code is err." + response.code());
            }
        }
    });
}

ResponseListener是自定义的响应数据Listener

public interface ResponseListener {
    void onSucess(String res);

    void onFailure(String msg);
}

前两步和同步请求一样,第三步时调用call.execute()异步请求数据,请求成功回调Callback.onResponse,请求失败回调Callback.onFailure。异步请求不会阻塞程序。

四、Post请求

Post的同步请求、异步请求和Get一样,所以后面不讨论异步请求,所以交易都使用同步请求。

Post使用步骤比Get多一步新建RequestBody,其他都是一样,RequestBody中包含Http报文的body部分。

1、Post提交json数据

public String post(String url, String json) {
    MediaType mediaType = MediaType.parse("application/json; charset=utf-8");
    RequestBody requestBody = RequestBody.create(mediaType, json);

    Request.Builder builder = new Request.Builder();
    Request request = builder.url(url)
            .post(requestBody)
            .build();

    try {
        Response response = okHttpClient.newCall(request).execute();
        if (!response.isSuccessful()) {
            return null;
        }
        return response.body().string();
    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }
}

使用MediaType指定Content-type,"application/json; charset=utf-8"表示请求数据是json,字符编码是utf-8。

后台如何使用Content-type?Spring MVC 使用@RequestMapping 注解的consumes指定处理请求的提交内容类型(Content-Type),例如application/json, text/html,收窄请求范围,如果用户发送的请求内容类型不匹配则方法不会响应请求。使用如下:

public class HomeController {
    // 请求内容类型Content-type必须为text/html
    @RequestMapping(value = "/action8",consumes="text/html")
    public String action8(Model model) {
        model.addAttribute("message", "请求的提交内容类型(Content-Type)是text/html");
        return "foo/index";
    }
}

2、Post提交form表单

public String postForm(String url, Map<String, String> form) {
    FormBody.Builder formBuilder = new FormBody.Builder();
    Iterator iterator = form.entrySet().iterator();
    while (iterator.hasNext()) {
        Map.Entry entry = (Map.Entry) iterator.next();
        formBuilder.add((String) entry.getKey(), (String) entry.getValue());
    }
    RequestBody requestBody = formBuilder.build();

    Request.Builder reqBuilder = new Request.Builder();
    Request request = reqBuilder.url(url)
            .post(requestBody)
            .build();

    try {
        Response response = okHttpClient.newCall(request).execute();
        if (!response.isSuccessful()) {
            return null;
        }
        return response.body().string();
    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }
}

使用FormBody创建Form表单请求的body。FormBody会自动加上content-type:application/x-www-form-urlencoded。

3、Post方式提交流

public String postStream(String url) {
    RequestBody requestBody = new RequestBody() {
        @Override
        public MediaType contentType() {
            return MediaType.parse("text/x-markdown; charset=utf-8");
        }

        @Override
        public void writeTo(BufferedSink sink) throws IOException {
            sink.writeString("post流方式提交数据", Charset.forName("GBK"));
        }
    };

    Request.Builder builder = new Request.Builder();
    Request request = builder.url(url)
            .post(requestBody)
            .build();

    try {
        Response response = okHttpClient.newCall(request).execute();
        if (response.isSuccessful()) {
            return response.body().string();
        } else {
            return null;
        }
    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }
}

这里其实是自己实现RequestBody,写入到Http body中的数据在writeTo()方法写入。上面2中的FormBody实际也是继承RequestBody,实现writeTo()方法完成body数据的组建。实现contentType()放回content-type。

4、Post方式提交文件

public String postFile(String url, File file) {
    RequestBody requestBody = RequestBody.create(MediaType.parse("\"text/x-markdown; charset=utf-8\""), file);

    Request.Builder builder = new Request.Builder();
    Request request = builder.url(url)
            .post(requestBody)
            .build();

    try {
        Response response = okHttpClient.newCall(request).execute();
        if (response.isSuccessful()) {
            return response.body().string();
        } else {
            return null;
        }
    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }
}

RequestBody.create(MediaType.parse("\"text/x-markdown; charset=utf-8\""), file);使用RequestBody.create传入文件File对象就可以创建body。

5、Post提交分块请求

public String postMultiPart(String url, File imgFile) {
    MultipartBody.Builder bodyBuilder = new MultipartBody.Builder();
    MultipartBody body = bodyBuilder.addPart(Headers.of("Content-Disposition", "form-data; name=\"title\""), RequestBody.create(null, "title"))
            .addPart(Headers.of("Content-Disposition", "form-data; name=\"image\""), RequestBody.create(MediaType.parse("image/png"), imgFile))
            .build();

    Request.Builder builder = new Request.Builder();
    Request request = builder.url(url)
            .post(body)
            .build();

    try {
        Response response = okHttpClient.newCall(request).execute();
        if (response.isSuccessful()) {
            return response.body().string();
        } else {
            return null;
        }
    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }
}

MultipartBuilder可以构建复杂的请求体,与HTML文件上传形式兼容。多块请求体中每块请求都是一个请求体,可以定义自己的请求头。这些请求头可以用来描述这块请求,例如他的Content-Disposition。如果Content-Length和Content-Type可用的话,他们会被自动添加到请求头中。MultipartBuilder的addPart()方法可以添加一个请求体。

五、取消一个请求Call

1、知道需要取消的Call对象使用:call.cancel();取消

2、通过Tag取消Call

创建Request时指定Tag

Request request = builder.url(url)
        .get() 
        .tag("tag") // 指定tag
        .build();

遍历当前OkHttpClient所有Request的tag,根据tag取消指定的Call

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

3、取消所有Call

OkHttpClient.dispatcher().cancelAll();

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值