美文网首页
okhttp3(3.5.0)

okhttp3(3.5.0)

作者: sofarsogoo_932d | 来源:发表于2018-03-27 13:56 被阅读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的配置的详细解析

    相关文章

      网友评论

          本文标题:okhttp3(3.5.0)

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