OKhttp使用详解(一)(封装)

API部分简介
OkHttpClient.Builder:OkHttpClient可通过Builder采用建造者模式构建,通过Builder可以方便灵活的设置通用参数

public Builder connectTimeout(long timeout, TimeUnit unit) 为连接设置超时时间
public Builder readTimeout(long timeout, TimeUnit unit) 设置默认读时间
public Builder writeTimeout(long timeout, TimeUnit unit) 设置默认写时间
public Builder proxy(Proxy proxy) 设置代理
public Builder proxySelector(ProxySelector proxySelector)
public Builder cookieJar(CookieJar cookieJar) 设置处理cookies的处理者(Sets the handler that can accept cookies from incoming HTTP responses and provides cookies to outgoing HTTP requests)
void setInternalCache(InternalCache internalCache) 设置内部缓存
public Builder cache(Cache cache) 设置缓存
public Builder dns(Dns dns) 设置DNS 服务
public Builder socketFactory(SocketFactory socketFactory)
public Builder sslSocketFactory(SSLSocketFactory sslSocketFactory) 配置证书用的
public Builder sslSocketFactory( SSLSocketFactory sslSocketFactory, X509TrustManager trustManager) 配置证书用的
public Builder hostnameVerifier(HostnameVerifier hostnameVerifier) Sets the verifier used to confirm that response certificates apply to requested hostnames for HTTPS connections. 应该也是是配置https的
public Builder certificatePinner(CertificatePinner certificatePinner) 不太懂,没用过
public Builder authenticator(Authenticator authenticator) Sets the authenticator used to respond to challenges from origin servers.
public Builder proxyAuthenticator(Authenticator proxyAuthenticator) Sets the authenticator used to respond to challenges from proxy servers.
public Builder connectionPool(ConnectionPool connectionPool) Sets the connection pool used to recycle HTTP and HTTPS connections. 设置连接池
public Builder followSslRedirects(boolean followProtocolRedirects) Configure this client to follow redirects from HTTPS to HTTP and from HTTP to HTTPS.不懂
public Builder followRedirects(boolean followRedirects) Configure this client to follow redirects. If unset, redirects be followed. 不懂
public Builder retryOnConnectionFailure(boolean retryOnConnectionFailure) 连接失败是否重连
public Builder dispatcher(Dispatcher dispatcher) Sets the dispatcher used to set policy and execute asynchronous requests. Must not be null.
public Builder protocols(List protocols) 设置协议
public Builder connectionSpecs(List connectionSpecs) 不懂
public List interceptors() 应该是获取拦截器列表
public Builder addInterceptor(Interceptor interceptor) 添加拦截器
public List networkInterceptors() 返回网络拦截器
public Builder addNetworkInterceptor(Interceptor interceptor) 添加网络拦截器
public OkHttpClient build() 构建OkHttpClient,并为它设置参数
由上面的设计图可知,OkhttpClient是核心,他就像一个浏览器一样,什么工作,比如请求,接受响应都是由它做的,关键就是怎么给他配置http协议的那些配置了,因为配置太多,所以用了Builder帮助配置

翻翻Builder的源码可以知道,即时你没有配置啥参数,Builder也为你默认设置一些参数,比如他默认设置支持 Protocol.HTTP_2, Protocol.SPDY_3, Protocol.HTTP_1_1这三种协议,默认连接,读写超时都是10s,DNS用系统的,默认失败重连。。。。

Dispatcher dispatcher;
Proxy proxy;
List protocols;
List connectionSpecs;
final List interceptors = new ArrayList<>();
final List networkInterceptors = new ArrayList<>();
ProxySelector proxySelector;
CookieJar cookieJar;
Cache cache;
InternalCache internalCache;
SocketFactory socketFactory;
SSLSocketFactory sslSocketFactory;
CertificateChainCleaner certificateChainCleaner;
HostnameVerifier hostnameVerifier;
CertificatePinner certificatePinner;
Authenticator proxyAuthenticator;
Authenticator authenticator;
ConnectionPool connectionPool;
Dns dns;
boolean followSslRedirects;
boolean followRedirects;
boolean retryOnConnectionFailure;
int connectTimeout;
int readTimeout;
int writeTimeout;

public Builder() {
  dispatcher = new Dispatcher();
  protocols = DEFAULT_PROTOCOLS;
  connectionSpecs = DEFAULT_CONNECTION_SPECS;
  proxySelector = ProxySelector.getDefault();
  cookieJar = CookieJar.NO_COOKIES;
  socketFactory = SocketFactory.getDefault();
  hostnameVerifier = OkHostnameVerifier.INSTANCE;
  certificatePinner = CertificatePinner.DEFAULT;
  proxyAuthenticator = Authenticator.NONE;
  authenticator = Authenticator.NONE;
  connectionPool = new ConnectionPool();
  dns = Dns.SYSTEM;
  followSslRedirects = true;
  followRedirects = true;
  retryOnConnectionFailure = true;
  connectTimeout = 10_000;
  readTimeout = 10_000;
  writeTimeout = 10_000;
}

创建OkhttpHelper
因为只要有请求就必须有okhttpclient参与,一般项目是分模块的,每个模块基本都有网络请求,总不能每个请求都要构造一个okhttpclient去请求吧,所以okhttpclient设计为单例,不用new ,到处都可以用它,方便快捷,代码得到了复用,也节省了内存开销

这里采用内部静态类实现单例

public class OkhttpHelper {
public static final int DEFAULTITMEOUT=5;
private OkHttpClient okHttpClient;

private OkhttpHelper(){
    OkHttpClient.Builder builder=new OkHttpClient.Builder()
            .connectTimeout(DEFAULTITMEOUT, TimeUnit.SECONDS);
    okHttpClient=builder.build();
}
public OkHttpClient getOkHttpClient() {
    return okHttpClient;
}
private static class OkhttpHelperHolder{
    public static OkhttpHelper INSTANCE=new OkhttpHelper();
}

}
有Http协议,每个连接必有请求头,是供服务端或客户端读取的,这里Okhttp封装了请求,并且也为它提供builder类,再翻翻看

这些是默认的参数

private HttpUrl url;
private String method;
private Headers.Builder headers;
private RequestBody body;
private Object tag;

public Builder() {
  this.method = "GET";
  this.headers = new Headers.Builder();
}

private Builder(Request request) {
  this.url = request.url;
  this.method = request.method;
  this.body = request.body;
  this.tag = request.tag;
  this.headers = request.headers.newBuilder();
}

public Builder url(HttpUrl url) 设置请求的URl,这个方法相当于浏览器上的地址输入框
public Builder url(String url) 重载方法
public Builder url(URL url) 也是重载,所以这里有三种url的表达格式
public Builder header(String name, String value) 这里是更新header字段的,就是设置键值对,里面的header应该用了个Map来记录的,我还在看
public Builder addHeader(String name, String value) 添加header字段
public Builder removeHeader(String name) 删除header字段
public Builder headers(Headers headers) 看吧,这里直接设置Headers,headers肯定是把所有的字段都包起来了
public Builder cacheControl(CacheControl cacheControl) 设置缓存控制,它其实也只设置header字段,不信看它的内部实现
public Builder cacheControl(CacheControl cacheControl) {
String value = cacheControl.toString();
if (value.isEmpty()) return removeHeader(“Cache-Control”);
return header(“Cache-Control”, value);
}
public Builder method(String method, RequestBody body) 设置请求的方法和请求体,为什么先说这个呢?
public Builder get() 设置请求方法
其实它的具体实现是调用method()方法

public Builder get() {
return method(“GET”, null);
}
怎么使用?
HTTP客户端的工作是接受您的请求并产生响应。这在理论上很简单,但在实践中变得棘手。

Requests 请求
每个HTTP请求包含一个URL,一个请求方法(像GET,POST),和一个头部列表,请求也包含一个body:特殊内容类型的数据流,比如文件,json数据…

Responses 响应
响应使用代码(如200获得成功或404找不到)来应答请求,标题及其自己的可选正文。

重写请求
当你提供给OKhttp一个HTTP请求,他就会按你定义的请求去执行,重写请求上面已给出了源码,直接用builder去set就是,可见okhttp支持很多header,如Content-Length, Transfer-Encoding, User-Agent, Host, Connection, and Content-Type

重写响应
响应是okhttp制造的,我们只是处理响应就行了。

后续请求
当您请求的URL已经移动时,网络服务器将返回一个302类型的响应代码,以指示该文档的新URL。 OkHttp将遵循重定向来检索最终的响应。

重试请求
有时连接失败:池连接过时并断开连接,或者无法访问Web服务器本身。 OkHttp将使用不同的路由重试该请求(如果有)。

Calls 呼叫
通过重写,重定向,后续跟踪和重试,您的简单请求可能会产生许多请求和响应,OkHttp使用Call来建立满足您的请求的任务,但是需要许多中间请求和响应。
有两种执行呼叫的方式

Synchronous 同步:你的线程被阻塞,直到响应可读。
Asynchronous 异步:您在任何线程上排队请求,并在响应可读时在另一个线程获取回调
呼叫可以在任何线程被取消,如果呼叫尚未完成,这将呼叫失败!编写请求正文或读取响应主体的代码在其呼叫被取消时将抛出IOException。
Dispatch
对于同步调用,您可以自己创建线程,并负责管理同时发出的请求数。同时连接太多浪费资源;太少的危害延迟。在安卓开发显然不能阻塞主线程,得在另一个线程执行。

对于异步调用,Dispatcher实现最大同时请求的策略。您可以设置每个网络服务器的最大值(默认值为5),总体(默认值为64)。

连接
1.它使用URL并配置了OkHttpClient来创建一个地址。该地址指定我们如何连接到Web服务器。
2.它尝试从连接池检索与该地址的连接.
3.如果在池中没有找到连接,则会选择尝试的路由。这通常意味着进行DNS请求以获取服务器的IP地址。然后,如果需要,可以选择TLS版本和代理服务器。
4.如果它是一个新路由,它通过构建直接套接字连接,TLS隧道(通过HTTP代理的HTTPS)或直接TLS连接进行连接。它需要TLS握手。
5.它发送HTTP请求并读取响应

如果连接有问题,OkHttp将选择另一个路由,然后重试。这允许OkHttp在服务器地址的子集不可达时恢复。

在接收到响应后,连接将返回到池中,以便将来可以重用该请求。连接在一段时间不活动之后从游泳池逐出。

使用实例
由于多处都会使用okhttpClient,所以我把它封装在一个单例类里,便于随处获取

/**

  • Created by Newtrek on 2017/4/12.
    */
    public class HttpHelper {
    public static int DEFAULT_TIMEOUT=5;
    private OkHttpClient okHttpClient;
    private HttpHelper(){
    OkHttpClient.Builder builder=new OkHttpClient.Builder();
    // set time out
    builder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
    okHttpClient=builder.build();
    }

    public static HttpHelper getInstance(){
    return HttpHelperHolder.instance;
    }

    public OkHttpClient getOkHttpClient() {
    return okHttpClient;
    }

    private static class HttpHelperHolder{
    private static HttpHelper instance=new HttpHelper();
    }
    }
    同步请求 GET
    /**

    • 下载一个文件,并以字符串形式打印响应里的头部,正文
      */
      public static void SynchGet(){
      // 获取client
      OkHttpClient okHttpClient= HttpHelper.getInstance().getOkHttpClient();
      // 构造请求,builder源码已分析,不用设置methos就是默认的get方法
      Request request=new Request.Builder()
      .url(“http://publicobject.com/helloworld.txt”)
      .build();
      try {
      // 获取响应
      Response response=okHttpClient.newCall(request).execute();
      // 响应成功,就打印相关内容,其实他是判断code是否属于【200,300)
      if (response.isSuccessful()){
      Headers headers=response.headers();
      for (int i=0;i<headers.size();i++){
      System.out.println(headers.name(i)+":"+headers.value(i));
      }
      System.out.println(response.body().string());
      }
      } catch (IOException e) {
      e.printStackTrace();
      }
      }
      结果:

title

title
异步请求 GET

public static  void AsynchGet(){
    //        获取client
    OkHttpClient okHttpClient=HttpHelper.getInstance().getOkHttpClient();
    //         构造请求
    Request request=new Request.Builder()
            .url("http://publicobject.com/helloworld.txt")
            .build();

// 请求加入队列
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}

        @Override
        public void onResponse(Call call, Response response) throws IOException {
            if (response.isSuccessful()){
                Headers headers=response.headers();
                for (int i=0;i<headers.size();i++){
                    System.out.println(headers.name(i)+":"+headers.value(i));
                }
                System.out.println(response.body().string());
            }
        }
    });
}

访问HTTP标头
通常,HTTP标头的工作方式与Map <String,String>类似,但是有些标题允许多个值,如Guava的Multimap。
当写请求头时,使用头(名称,值)来设置名称唯一出现的值。
如果存在现有值,则在添加新值之前将删除它们。使用addHeader(name,value)来添加标题,而不会删除已经存在的标题。

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
Request request = new Request.Builder()
.url(“https://api.github.com/repos/square/okhttp/issues”)
.header(“User-Agent”, “OkHttp Headers.java”)
.addHeader(“Accept”, “application/json; q=0.5”)
.addHeader(“Accept”, “application/vnd.github.v3+json”)
.build();

Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

System.out.println("Server: " + response.header("Server"));
System.out.println("Date: " + response.header("Date"));
System.out.println("Vary: " + response.headers("Vary"));

}
post a String
使用HTTP POST将请求体发送到服务

public static void postAString() {
MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse(“text/x-markdown; charset=utf-8”);
String postBody = “”
+ “Releases\n”
+ “--------\n”
+ “\n”
+ " * 1.0 May 6, 2013\n"
+ " * 1.1 June 15, 2013\n"
+ " * 1.2 August 11, 2013\n";

    OkHttpClient okHttpClient = HttpHelper.getInstance().getOkHttpClient();
    Request request = new Request.Builder()
            .url("https://api.github.com/markdown/raw")
            .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody))
            .build();
    try {
        Response response = okHttpClient.newCall(request).execute();
           System.out.println( response.toString());
    } catch (IOException e) {
        e.printStackTrace();
    }
}

post Streaming
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse(“text/x-markdown; charset=utf-8”);

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
RequestBody requestBody = new RequestBody() {
@Override public MediaType contentType() {
return MEDIA_TYPE_MARKDOWN;
}

  @Override public void writeTo(BufferedSink sink) throws IOException {
    sink.writeUtf8("Numbers\n");
    sink.writeUtf8("-------\n");
    for (int i = 2; i <= 997; i++) {
      sink.writeUtf8(String.format(" * %s = %s\n", i, factor(i)));
    }
  }

  private String factor(int n) {
    for (int i = 2; i < n; i++) {
      int x = n / i;
      if (x * i == n) return factor(x) + " × " + i;
    }
    return Integer.toString(n);
  }
};

Request request = new Request.Builder()
    .url("https://api.github.com/markdown/raw")
    .post(requestBody)
    .build();

Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

System.out.println(response.body().string());

}
post a file
直接 .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file)),file是File类型

post form parameters (post表单)
RequestBody formBody = new FormBody.Builder()
.add(“search”, “Jurassic Park”)
.build();

Posting a multipart request
MultipartBody.Builder可以构建与HTML文件上传表单兼容的复杂请求体。多部分请求体的每个部分本身就是一个请求体,并且可以定义自己的头。如果存在,这些标题应该描述零件体,例如它的Content-Disposition。如果Content-Length和Content-Type标题可用,则会自动添加。

// Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart(“title”, “Square Logo”)
.addFormDataPart(“image”, “logo-square.png”,
RequestBody.create(MEDIA_TYPE_PNG, new File(“website/static/logo-square.png”)))
.build();
用Gson解析json响应
Gist gist = gson.fromJson(response.body().charStream(), Gist.class);
for (Map.Entry<String, GistFile> entry : gist.files.entrySet()) {
System.out.println(entry.getKey());
System.out.println(entry.getValue().content);
}
响应缓存
要缓存响应,您需要一个可以读取和写入的缓存目录,并对缓存的大小有限制。缓存目录应该是私有的,不受信任的应用程序不能读取其内容!

响应缓存使用HTTP头进行所有配置,你可以添加请求头,如Cache-Control:max-stale = 3600,OkHttp的缓存将遵守它们。您的网络服务器使用自己的响应头配置响应缓存的时间长短像Cache-Control:max-age = 9600。有缓存标头强制缓存的响应,强制网络响应,或强制使用条件GET验证网络响应

// set cache
int cacheSize=1010241024;//10MB
File cacheFile=new File(“E://java/”);
if (!cacheFile.exists()){
cacheFile.mkdir();
}
Cache cache=new Cache(cacheFile,cacheSize);
builder.cache(cache);

public static void ResponseCache(){
OkHttpClient okHttpClient = HttpHelper.getInstance().getOkHttpClient();
Request request = new Request.Builder()
.url(“http://publicobject.com/helloworld.txt”)
.build();
try {
Response response1 = okHttpClient.newCall(request).execute();
String response1Body = response1.body().string();
System.out.println("Response 1 response: " + response1);
System.out.println("Response 1 cache response: " + response1.cacheResponse());
System.out.println("Response 1 network response: " + response1.networkResponse());
} catch (IOException e) {
e.printStackTrace();
}

    try {
        Response response2 = okHttpClient.newCall(request).execute();
        String response2Body = response2.body().string();
        System.out.println("Response 2 response:          " + response2);
        System.out.println("Response 2 cache response:    " + response2.cacheResponse());
        System.out.println("Response 2 network response:  " + response2.networkResponse());

    } catch (IOException e) {
        e.printStackTrace();
    }
}

title

title

可以验证到我的E://java/目录里多了四个文件,其实他们就是缓存文件

cancel Call
每次client.newCall()的时候会返回一个Call,所以取消的话就调用call.cancel()就行,如果请求任务未完成,会抛出IO异常

Timeout
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
处理身份验证authentication
OkHttp可以自动重试未经身份验证的请求。当答复为401未授权时,将要求身份验证者提供凭证。实现应该构建一个包含缺少凭据的新请求。如果没有凭据可用,返回null以跳过重试。使用Response.challenges()来获取任何认证挑战的方案和领域。在履行基本挑战时,请使用Credentials.basic(用户名,密码)对请求标头进行编码。

client = new OkHttpClient.Builder()
.authenticator(new Authenticator() {
@Override public Request authenticate(Route route, Response response) throws IOException {
System.out.println("Authenticating for response: " + response);
System.out.println("Challenges: " + response.challenges());
String credential = Credentials.basic(“jesse”, “password1”);
return response.request().newBuilder()
.header(“Authorization”, credential)
.build();
}
})
.build();

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于使用OkHttp进行封装,以下是一个基本的示例: 1. 首先,确保已经在项目中添加了OkHttp的依赖。在build.gradle文件中添加以下代码: ```groovy implementation 'com.squareup.okhttp3:okhttp:4.9.0' ``` 2. 创建一个HttpHelper类来封装OkHttp使用。示例代码如下: ```java import okhttp3.*; import java.io.IOException; public class HttpHelper { private final OkHttpClient client; public HttpHelper() { client = new OkHttpClient(); } public void get(String url, Callback callback) { Request request = new Request.Builder() .url(url) .build(); client.newCall(request).enqueue(callback); } public void post(String url, RequestBody requestBody, Callback callback) { Request request = new Request.Builder() .url(url) .post(requestBody) .build(); client.newCall(request).enqueue(callback); } } ``` 3. 现在可以在其他类中使用HttpHelper进行网络请求。以下是一个使用GET请求的示例: ```java import okhttp3.Call; import okhttp3.Callback; import okhttp3.Response; import java.io.IOException; public class Main { public static void main(String[] args) { HttpHelper httpHelper = new HttpHelper(); String url = "https://api.example.com/data"; httpHelper.get(url, new Callback() { @Override public void onFailure(Call call, IOException e) { // 处理请求失败的情况 } @Override public void onResponse(Call call, Response response) throws IOException { // 处理请求成功的情况 String responseData = response.body().string(); System.out.println(responseData); } }); } } ``` 以上示例中,我们首先创建了一个HttpHelper实例,然后使用get方法发送GET请求。在回调函数中,可以处理请求成功或失败的情况,并处理返回的响应数据。 对于POST请求,可以使用post方法,并传递一个RequestBody对象作为请求体。具体的请求参数和请求头信息可以根据实际需求进行设置。 这只是一个基本的封装示例,你可以根据自己的项目需求进行更多的定制和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值