美文网首页
分享 OkHttp

分享 OkHttp

作者: 木木不见君 | 来源:发表于2017-05-25 14:39 被阅读191次

    Android实现网络请求(SDK自带)
    HttpClient Android5.0以后废弃了HttpClient 在Android6.0更是删除了HttpClient。
    HttpURLConnection Android4.4以后HttpURLConnection的底层已经替换成OkHttp实现

    TCP/IP
    TCP/IP是个协议组,可分为三个层次:网络层、传输层和应用层。
    在网络层有IP协议、ICMP协议、ARP协议、RARP协议和BOOTP协议。
    在传输层中有TCP协议与UDP协议。
    在应用层有:TCP包括FTP、HTTP、TELNET、SMTP等协议
    UDP包括DNS、TFTP等协议

    Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
    客户端首先要创建一个 Socket 实例
    服务端将创建一个 ServerSocket 实例

    socket是java底层的通信方式.使用的协议是tcp/ip.
    而httpclient是模拟(或者说使用)我们日常使用的http协议.也就是说httpclient直接使用的http协议.
    而tcp/ip协议是http协议的底层实现.
    也就是说http协议要转换成tcp/ip协议.
    从java的角度来说,就是httpclient间接的使用了socket来通信.
    HttpURLConnection 基于 apatom 的

    2.2 网络请求库和Android网络请求实现方法的关系
    网络请求框架本质上是一个将网络请求的相关方法( HttpClient或HttpURLConnection)封装好的类库,并实现另开线程进行请求和处理数据,从而实现整个网络请求模块的功能。具体的关系可看下图:
    [图片上传中。。。(1)]
    使用SDK自带:
    Android的主线程不能进行网络请求,还需要另外开一个线程进行请求,
    要考虑到线程池,缓存等一堆问题。
    网络请求库:
    异步请求 线程池 缓存 等等

    如今Android中主流的网络请求框架有:
    Android-Async-Http
    Volley
    OkHttp
    Retrofit

    下面是简单介绍:

    [图片上传中。。。(2)]

    OkHttp
    OkHttp是一个现代,快速,高效的网络库,OkHttp 库的设计和实现的首要目标是高效。
    支持 HTTP/2和SPDY,这使得对同一个主机发出的所有请求都可以共享相同的套接字连接;
    如果 HTTP/2和SPDY不可用,OkHttp会使用连接池来复用连接以提高效率。
    支持Gzip降低传输内容的大小
    支持Http缓存
    会从很多常用的连接问题中自动恢复。如果服务器配置了多个IP地址,OkHttp 会自动重试一个主机的多个 IP 地址。
    使用Okio来大大简化数据的访问与存储,提高性能

    强调:
    可以把 它理解成 一个 封装之后 的 类似 HttpUriConnection的东西,属于同级别 但是并不是 基于 上述二者;
    1:基于 socket
    2:就是 网络请求 ( 虽然有 线程池 ,缓存,等等)

    首先说 第二点:

    [图片上传中。。。(3)]

    [图片上传中。。。(4)]

    // 同步Get请求和异步调用区别就是调用了call的execute()方法。
    // 同步是指:发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式。
    // 异步是指:发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式。
    // 同步:提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事
    // 异步: 请求通过事件触发->服务器处理(这是浏览器仍然可以作其他事情)->处理完毕

    // 同步执行的话,就是程序会呆板地从头执行到尾,耗时间的东西不执行完,程序不会继续往下走,等待时间长的话,有时候就会造成失去响应了。
    // 异步的好处,就是把一些东西,特别是耗时间的东西扔到后台去运行了(doInBackground),程序可以继续做自己的事情,防止程序卡在那里失去响应。

    第一点:看 源码

    [图片上传中。。。(5)]

    [图片上传中。。。(6)]

    源码分析:
    Request
    Request request = new Request
    .Builder()
    .url(url)
    .post(body)
    .addHeader("Accept","/")
    .cacheContro()
    .build();

    private final HttpUrl url;//请求url封装
    private final String method;//请求方法
    private final Headers headers;//请求头
    private final RequestBody body;//请求体,也就是http协议的实体内容
    private final Object tag;//被请求的标签

    private volatile URL javaNetUrl; // Lazily initialized.
    private volatile URI javaNetUri; // Lazily initialized.
    private volatile CacheControl cacheControl; // 缓存控制的封装

    CacheControl
    final CacheControl.Builder builder = new CacheControl.Builder();
    builder.noCache();//不使用缓存,全部走网络
    builder.noStore();//不使用缓存,也不存储缓存
    builder.onlyIfCached();//只使用缓存
    builder.noTransform();//禁止转码
    builder.maxAge(10, TimeUnit.MILLISECONDS);//指示客户机可以接收生存期不大于指定时间的响应。
    builder.maxStale(10, TimeUnit.SECONDS);//指示客户机可以接收超出超时期间的响应消息
    builder.minFresh(10, TimeUnit.SECONDS);//指示客户机可以接收响应时间小于当前时间加上指定时间的响应。
    CacheControl cache = builder.build();//cacheControl

    RequestBody---表单
    public static final MediaType TEXT = MediaType.parse("text/plain; charset=utf-8");
    public static final MediaType STREAM = MediaType.parse("application/octet-stream");
    public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");

    //构建字符串请求体
    RequestBody body1 = RequestBody.create(TEXT, string);
    //构建字节请求体
    RequestBody body2 = RequestBody.create(STREAM, byte);
    //构建文件请求体
    RequestBody body3 = RequestBody.create(STREAM, file);
    //post上传json
    RequestBody body4 = RequestBody.create(JSON, json);//json为String类型的

    //将请求体设置给请求方法内
    Request request = new Request.Builder()
    .url(url)
    .post(xx)// xx表示body1,body2,body3,body4中的某一个
    .build();

    //构建表单RequestBody
    RequestBody formBody=new FormBody.Builder()
    .add("name","maplejaw")
    .add("age","18")
    ...
    .build();

    Response

    private final Request request;
    private final Protocol protocol;
    private final int code;
    private final String message;
    private final Handshake handshake;
    private final Headers headers;
    private final ResponseBody body;
    private final Response networkResponse;
    private final Response cacheResponse;
    private final Response priorResponse;
    private final long sentRequestAtMillis;
    private final long receivedResponseAtMillis;

    OkHttpClient

    final Dispatcher dispatcher;
    final Proxy proxy;
    final List<Protocol> protocols;
    final List<ConnectionSpec> connectionSpecs;
    final List<Interceptor> interceptors;
    final List<Interceptor> networkInterceptors;
    final ProxySelector proxySelector;
    final CookieJar cookieJar;
    final Cache cache;
    final InternalCache internalCache;
    final SocketFactory socketFactory;
    final SSLSocketFactory sslSocketFactory;
    final CertificateChainCleaner certificateChainCleaner;
    final HostnameVerifier hostnameVerifier;
    final CertificatePinner certificatePinner;
    final Authenticator proxyAuthenticator;
    final Authenticator authenticator;
    final ConnectionPool connectionPool;

    private static final Executor executor = new ThreadPoolExecutor(0 /* corePoolSize /,
    Integer.MAX_VALUE /
    maximumPoolSize /, 60L / keepAliveTime */, TimeUnit.SECONDS,
    new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp ConnectionPool", true));
    构建了一个最大线程数为Integer.MAX_VALUE的线程池,也就是说,是个不设最大上限的线程池(其实有限制64个),有多少任务添加进来就新建多少线程,以保证I/O任务中高阻塞低占用的过程中,不会长时间卡在阻塞上。当工作完成后,线程池会在60s内相继关闭所有线程。

    final Dns dns;
    final boolean followSslRedirects;
    final boolean followRedirects;
    final boolean retryOnConnectionFailure;
    final int connectTimeout;
    final int readTimeout;
    final int writeTimeout;

    [图片上传中。。。(7)]

    1.从请求处理开始分析

    当我们要请求网络的时候我们需要用OkHttpClient.newCall(request)进行execute或者enqueue操作,当我们调用newCall时:

    123
    @Override public Call newCall(Request request) {return new RealCall(this, request);}

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

    可以看到最终的请求是dispatcher来完成的。

    2.Dispatcher任务调度

    getResponseWithInterceptorChain方法返回了Response,

    3.Interceptor拦截器

    没有将网络请求与Activity/Fragment的生命周期进行绑定,导致切换页面时没有及时释放网络请求的相关资源;其次OkHttp3的异步请求结束后的回调方法是在子线程中,若要进行UI操作就得采用 runOnUiThread 方法进一步包裹

    对于任务1,实现比较简单,采用Map集合保存每次网络请求Call,Map的键为Activity/Fragment,值采用List集合保存该页面下所有的Call,考虑到多线程情况的复杂性和不可预见性,这里采用了并发包下的ConcurrentHashMap 。

    /**
    * 取消请求
    * @param clazz
    */
    public static void cancelCall(Class<?> clazz){
    List<Call> callList = callsMap.get(clazz);
    if(null != callList){
    for(Call call : callList){
    if(!call.isCanceled())
    call.cancel();
    }
    callsMap.remove(clazz);
    }
    }

    紧接着上篇说的任务2:异步请求采用UI线程回调方式。
    首先采用Handler进行线程间的通信,顺便优化下回调方法,加入HttpInfo以做到工具类使用的渗透性。
    在OkHttpUtil中声明一个自定义的异步回调接口,该接口对网络请求接口进行了封装,使同步、异步请求处理流程保持一致性,代码如下:

    参考网页:
    http://www.jianshu.com/p/92a61357164b
    http://blog.csdn.net/zsf442553199/article/details/51752974
    http://liuwangshu.cn/application/network/7-okhttp3-sourcecode.html

    相关文章

      网友评论

          本文标题:分享 OkHttp

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