分享 OkHttp

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,591评论 18 139
  • 参考Android网络请求心路历程Android Http接地气网络请求(HttpURLConnection) 一...
    合肥黑阅读 21,230评论 7 63
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,565评论 18 399
  • 参考资源 官网 国内博客 GitHub官网 鉴于一些关于OKHttp3源码的解析文档过于碎片化,本文系统的,由浅入...
    风骨依存阅读 12,462评论 11 82
  • 王之嘉下班后到家,看到家里的锅台上放着几个糖瓜,他才意识到原来今天是腊月二十三,传统意义上的小年。妈妈王美丽在灶间...
    宋月才河北阅读 133评论 0 0