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