okhttp3(3.5.0)

http网络请求框架
github

关于什么是okhttp3我就不介绍了,我们直入主题

1. okhttp API介绍

  • Request.Builder 请求构造者
  • url(String url):请求的url
  • post():默认是Get方式
  • post(RequestBody body):Post带参数
  • build():构造请求
    请求参数有三种:
  • RequestBody:普通的请求参数
  • FormBody.Builder:以表单的方式传递键值对的请求参数
  • MultipartBody.Builder:以表单的方式上传文件的请求参数
    执行方法:
    Call
  • enqueue(Callback callback):异步请求
  • execute():同步请求

2. 用法

2.1 创建一个OkHttpClient实例

基本配置

 OkHttpClient.Builder builder = new OkHttpClient.Builder();
 builder.connectTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS);//连接超时时间
 builder.writeTimeout(DEFAULT_WRITE_OUT, TimeUnit.SECONDS);//写操作 超时时间
 builder.readTimeout(DEFAULT_READ_TIME_OUT, TimeUnit.SECONDS);//读操作超时时间

其它配置
这些配置在这篇文章就不详细说明了,会放在单独的文章解析

builder.sslSocketFactory()   //添加https证书信任
builder.addInterceptor()      //添加拦截器,可调用多次
builder.cache()                     //添加缓存配置
builder.cookieJar()              //添加cookie

build出OkHttpClient实例

mClient.newBuilder().build();   
2.2 调用接口方法封装

get方法

public void httpGet(String url, Object tag, Map<String, String> params, final StringCallback callback) {
 
    Request request = new Request.Builder().url(url).tag(tag).build();
    Call call = this.mClient.newCall(request);
    call.enqueue(new Callback() {
        public void onResponse(Call call, final Response response) throws IOException {
            final String res = response.body().string();
            OkhttpUtils.this.mDelivery.post(new Runnable() {
                public void run() {
                    if (response.isSuccessful()) {
                        callback.OnSuccess(res);
                    } else {
                        callback.OnError(String.valueOf(response.code()));
                    }

                }
            });
        }

        public void onFailure(Call call, final IOException e) {
            OkhttpUtils.this.mDelivery.post(new Runnable() {
                public void run() {
                    if (e != null && e.getMessage() != null) {
                        callback.OnError(e.getMessage());
                    } else {
                        callback.OnError("服务器通讯异常,请重试");
                    }
                }
            });
        }
    });
}

post表单

public void httpPost(String url, Object tag, Map<String, String> params, final StringCallback callback) {
    FormBody.Builder fbuilder = new FormBody.Builder();
    for (String key : params.keySet()) {
        builder.add(key, params.get(key));
    }
    Request request = new Request.Builder().url(url).tag(tag).post(fBuilder.build()).build();
    Call call = this.mClient.newCall(request);
    call.enqueue(new Callback() {
        public void onResponse(Call call, final Response response) throws IOException {
            final String res = response.body().string();
            OkhttpUtils.this.mDelivery.post(new Runnable() {
                public void run() {
                    if (response.isSuccessful()) {
                        callback.OnSuccess(res);
                    } else {
                        callback.OnError(String.valueOf(response.code()));
                    }
                }
            });
        }

        public void onFailure(Call call, final IOException e) {
            OkhttpUtils.this.mDelivery.post(new Runnable() {
                public void run() {
                    if (e != null && e.getMessage() != null) {
                        callback.OnError(e.getMessage());
                    } else {
                        callback.OnError("服务器通讯异常,请重试");
                    }

                }
            });
        }
    });
}

post提交json格式参数

public void httpPostJson(String url, Object tag, Map<String, String> params, final StringCallback callback) {
    
    MediaType JSON = MediaType.parse("application/json");
    JSONObject json = new JSONObject();
    Iterator var9 = params.entrySet().iterator();

    while(var9.hasNext()) {
        Entry<String, String> entry = (Entry)var9.next();
        String key = (String)entry.getKey();
        String value = (String)entry.getValue();
        json.put(key, value);
    }

    RequestBody requestBody = RequestBody.create(JSON, json.toString());
    Request request = new Request.Builder().url(url).tag(tag).post(requestBody).build();
    Call call = this.mClient.newCall(request);
    call.enqueue(new Callback() {
        public void onResponse(Call call, final Response response) throws IOException {
            final String res = response.body().string();
            OkhttpUtils.this.mDelivery.post(new Runnable() {
                public void run() {
                    if (response.isSuccessful()) {
                        callback.OnSuccess(res);
                    } else {
                        callback.OnError(String.valueOf(response.code()));
                    }

                }
            });
        }

        public void onFailure(Call call, final IOException e) {
            OkhttpUtils.this.mDelivery.post(new Runnable() {
                public void run() {
                    if (e != null && e.getMessage() != null) {
                        callback.OnError(e.getMessage());
                    } else {
                        callback.OnError("服务器通讯异常,请重试");
                    }

                }
            });
        }
    });
}

okhttp的基本使用到这已经结束了

3. 核心类解析

我们从一个标准的okhttp发送请求的代码开始

OkHttpClient client = new OkHttpClient.Builder().build();
Request request = new Request.Builder()
.url("http://www.baidu.com").get().build();
client.newCall(request).enqueue(new Callback() {
@Override public void onFailure(Call call, IOException e) {

  }

@Override public void onResponse(Call call, Response response) throws IOException {

  }
});

用到的类OkHttpClient,Request,Call,Response

OkHttpClient
参数配置类,举例说明一下
CookieJar Cookie配置
Cache 缓存配置
SSLSocketFactory https配置
HostnameVerifier 验证主机
interceptors 拦截器,如日志拦截器。

Request
请求类,包含了与请求相关的参数

final HttpUrl url;       //请求地址
final String method;  //请求方法get,post等等
final Headers headers;  //请求头
final RequestBody body;  //请求体
final Object tag;   //标记

Call

public Call newCall(Request request) {
  return new RealCall(this, request, false /* for web socket */);
}

这是一个接口,里面有两个关键方法
execute()和enqueue(Callback paramCallback)
真正的实现放在了RealCall这个实现类里面
execute 是同步任务
enqueue是异步任务

public void enqueue(Callback responseCallback) {
synchronized (this) {
  if (executed) throw new IllegalStateException("Already Executed");
  executed = true;
}
captureCallStackTrace();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}

根据上述代码,我们知道调用的是dispatcher的入队方法

Dispatcher

synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
  runningAsyncCalls.add(call);
  executorService().execute(call);
} else {
  readyAsyncCalls.add(call);
}
}

其中runningAsyncCalls是可执行的异步任务队列,runningCallsForHost表示同一个主机正在接受的请求数
从源码中可看出maxRequests=64,maxRequestsPerHost=5。
当满足if条件,则将新增加的请求添加到异步任务队列,并且立刻执行任务,否咋就将异步任务添加到预备队列

从 executorService().execute(call)这句代码,我们知道AsyncCall是一个线程,在从源码中可以看出,这句代码最终会调用AsyncCall的execute方法

AsyncCall

  protected void execute() {
  boolean signalledCallback = false;
  try {
    Response response = getResponseWithInterceptorChain();
    if (retryAndFollowUpInterceptor.isCanceled()) {
      signalledCallback = true;
      responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
    } else {
      signalledCallback = true;
      responseCallback.onResponse(RealCall.this, response);
    }
  } catch (IOException e) {
    if (signalledCallback) {
      // Do not signal the callback twice!
      Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
    } else {
      responseCallback.onFailure(RealCall.this, e);
    }
  } finally {
    client.dispatcher().finished(this);
  }
}

从这里就很可以很容易看出,请求被发出去了,并将Response返了回去,并且在finally中调用了Dispatcher的finished方法

private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
  if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
  if (promoteCalls) promoteCalls();
  runningCallsCount = runningCallsCount();
  idleCallback = this.idleCallback;
}

if (runningCallsCount == 0 && idleCallback != null) {
  idleCallback.run();
}
}

从上述代码可以看出,在这里面会移除刚才执行过的任务,并调用了promoteCalls()方法

private void promoteCalls() {
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
  AsyncCall call = i.next();

  if (runningCallsForHost(call) < maxRequestsPerHost) {
    i.remove();
    runningAsyncCalls.add(call);
    executorService().execute(call);
  }

  if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}

上述代码做的操作主要是
循环遍历预备队列
将预备队列的异步任务移除,并放进执行队列中
调用execute方法执行异步任务

Response

final Request request;        //请求类
final Protocol protocol;      //协议http1.0,http1.1和http2.0
final int code;                    //返回码
final String message;        
final Handshake handshake;     //TLS(SSL后续版本),https加密
final Headers headers;        //响应头
final ResponseBody body;   //响应内容
final Response networkResponse;
final Response cacheResponse;
final Response priorResponse;
final long sentRequestAtMillis;
final long receivedResponseAtMillis;

其他用到的技术
ConnectionPool
连接池
我们知道http在传输层用的协议是tcp,而tcp在通信时,每次都需要三次握手来创建连接,四次挥手来释放连接,这必然会增加通信的时间,连接池的出现则解决了这个问题,连接池的实现方式类似于java的垃圾回收机制。每次创建的连接都会保存在连接池中,并不会马上释放,而那些空闲的连接会在合适的时机被释放掉
由于不用每次都三次握手,四次挥手,明显缩短了通信的时间。

Connection:keep-alive    //保持长连接  1.0默认关闭,1.1默认开启
Connection:close        //关闭长连接

4.总结

这篇文章主要是介绍如何快速使用okhttp
注意okhttp的回调在子线程,需要自己切回主线程
接下来会持续更新okhttp的高级使用,即前面所讲的okhttp的配置的详细解析

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,273评论 19 139
  • 6.1 公钥密钥加密原理 6.1.1 基础知识 密钥:一般就是一个字符串或数字,在加密或者解密时传递给加密/解密算...
    AndroidMaster阅读 4,056评论 1 8
  • 这段时间老李的新公司要更换网络层,知道现在主流网络层的模式是RxJava+Retrofit+OKHttp,所以老李...
    隔壁老李头阅读 33,478评论 51 405
  • 然2016阅读 234评论 0 0
  • 本质上,我是个很自我的人。自我到自己的拜物教,就是那个曾经无所不能的自己,当然,这种能够掌控一切的感觉随着年龄和生...
    荒芜延伸阅读 137评论 0 0