原理
- 选择网络框架
Xutils、Volley、OkHttp、Retrofit
发送一个网络请求
集成OkHttp
OkHttpClient client = new OkHttpClient(); // okhttp 配置一些默认参数
FormBody.Builder builder = new FormBody.Builder();
builder.add("key", "0f08cd674792667feb5ce236ea028747");
okhttp源码解析
来源 https://github.com/square/okhttp
OkHttpClient : 建议OkHttp的客户端,初始化OkHttp要使用的配置信息。
查看OkHttpClient 内部源码,其构造方法就是赋值作用,通过传入的builder值,赋值到OkHttpClient成员变量里。
Builder下:new了一个OkHttpClient里面有个Builder()给OkHttp设置一些参数
public Builder() {
//Dispatcher 相当于任务调度器dispatcher = new Dispatcher();
//协议protocols = DEFAULT_PROTOCOLS;
//网络是否加密connectionSpecs = DEFAULT_CONNECTION_SPECS;
//事件监听-通过工厂模式eventListenerFactory = EventListener.factory(EventListener.NONE);
//代理proxySelector = ProxySelector.getDefault();if (proxySelector == null) {proxySelector = new NullProxySelector();}
//设置cookiecookieJar = CookieJar.NO_COOKIES;
//socketFactory = SocketFactory.getDefault();
//host认证-跟https相关hostnameVerifier = OkHostnameVerifier.INSTANCE;certificatePinner = CertificatePinner.DEFAULT;proxyAuthenticator = Authenticator.NONE;authenticator = Authenticator.NONE;
//连接池connectionPool = new ConnectionPool();
//DNS解析dns = Dns.SYSTEM;
...
//连接时间connectTimeout = 10_000;
//读取时间readTimeout = 10_000;
//写入时间writeTimeout = 10_000;
}
Dispatcher()管所有的任务,点击进入查看源码
拿到具体的请求数量;
public synchronized int getMaxRequests() {return maxRequests;}
拿到当前线程队列有哪些,具体管控哪些请求队列;
void enqueue(AsyncCall call) {synchronized (this) {readyAsyncCalls.add(call);}promoteAndExecute();}
点击进入DEFAULT_PROTOCOLS 协议,http网络,点击DEFAULT_CONNECTION_SPECS,https网络
点击MODERN_TLS 进入,TLS加密解密有哪些版本
点击APPROVED_CIPHER_SUITES进入,TLS所有版本
// This is nearly equal to the cipher suites supported in Chrome 51, current as of 2016-05-25.// All of these suites are available on Android 7.0; earlier releases support a subset of these// suites. https://github.com/square/okhttp/issues/1972private static final CipherSuite[] APPROVED_CIPHER_SUITES = new CipherSuite[] {// TLSv1.3CipherSuite.TLS_AES_128_GCM_SHA256,CipherSuite.TLS_AES_256_GCM_SHA384,CipherSuite.TLS_CHACHA20_POLY1305_SHA256,
...
...
}
点击ConnectionSpec.CLEARTEXT进入:没有进行https加密,默认支持http请求
Request:请求信息封装
Request request = new Request.Builder().url("http://apis.juhe.cn/lottery/types").post(builder.build()).build(); //建造者设计模式,优点:实现圆点(连点)操作
点击查看这里面的Builder(),返回url是Builder本身,可以实现灵敏操作
Call call = client.newCall(request);
点击newCall()方法
这个Call面向抽象编程
Call:可执行异步或者同步请求
实际上调用的是这个RealCall,
来执行网络请求
//Response execute = call.execute(); //同步请求可直接拿到Response//基于观察者模式拿到responsecall.enqueue(new Callback() { //异步请求@Overridepublic void onFailure(Call call, IOException e) {Log.e("initGetNetData","onFailure" + e);}@Overridepublic void onResponse(Call call, Response response) throws IOException {Log.e("initGetNetData","onResponse" + response.body().string());}});
同步方法synchronized (this)
这里的client就是OkHttpClient,它构造了一个类似于任务管理器dispatcher()去执行了一个enqueue()方法,并传入到匿名内部类(观察者模式-数据回调的接口Callback responseCallback,传递到Asyncall()对象里)
点击AsyncCall()进入:它实际上就是个Runnable,Runnable可以放到Handler里面,执行相应的代码
点击enquene进入:就回到之前提到过的
点击promoteAndExecute进来:
点击executeOn进入:也是RealCall里面的内部类
点击executorService()进入:executorService就是一个线程池,把任务调度器,把创建好的线程池,传入到executeOn()里面。
Handler可以执行Runnable
线程池也可以执行Runnable
复写Runnable的重要方法—NamedRunnable
Run()里面是一个具体想要的一个网络请求
通过Runnable把它加入到线程池里,线程池通过它的线程管理去执行Runnable对象,这样就可以走到Run()方法,这里面用了了个抽象方法,可以由具体的类(如:这里的AsyncCall)调用去实现(重写execute())
同样在RealCall下,这段代码是实现OkHttp的核心
拦截器
“U”型处理,即向下跑完代码,再向上走
- Interceptor: OkHttp中的精华就是 拦截器,责任链设计模式。
- RetryAndFollowUpInterceptor:负责错误重试及重定向。 比如发送一个请求失败了,它可以进行一个错误的重试还可以进行重定向
- BridgeInterceptor:负责组装请求及解析数据。 一个是Request负责组装还有一个Response负责解析
- CacheInterceptor:负责读取缓存和更新缓存。 发送一个网络请求,可以读取这个缓存,响应速度更快,然后请求下来的数据(最新的),那这个缓存拿回来的Response是最新数据,需更新本地缓存。
- ConnectInterceptor:负责和服务器建立连接。
- CallServerInterceptor:负责发送请求及接收数据, Okio 对接 OkHttp当中最重要的一个拦截器,点击CallServerInterceptor进入:面向抽象编程,指定一个实体对象来做的
定位HttpCodec源码
public final class Http1Codec implements HttpCodec {private static final int STATE_IDLE = 0; // Idle connections are ready to write request headers.
...
/** The client that configures this stream. May be null for HTTPS proxy tunnels. */final OkHttpClient client;
/** The stream allocation that owns this stream. May be null for HTTPS proxy tunnels. */final StreamAllocation streamAllocation;
...final BufferedSource source;final BufferedSink sink; //相当于一个流
...
}
OkHttp基于Okio(IO数据流,Okio封装在OkHtto里面),所以Okio是具体负责和服务端数字缔结的,它也是基于socket连接,读取相应数据
java除了IO,还有一个NIO,也是流操作
OkHttp相对于客户端,它封装的代码组装Request,拿到Response,,具体的网络请求和服务端对接的,就是CallServerInterceptorl里面的HttpCodec,调用相应的Okio里面的代码
同步请求与异步请求
拦截器可以处理Request封装请求,还可以拿到Response
如:统一给Request加一些统一的参数,每个接口同样的参数,那就可以单独做一个拦截器往里添加
如果对Response做一些统一处理,比如错误码处理,可定义一个拦截器,放到client里去处理。
Get请求
从指定的资源请求数据。
GET 请求不应在处理敏感数据时使用
GET 请求有长度限制
例子:http://v.juhe.cn/joke/content/list.php?key=您申请的KEY&page=2&pagesize=10&sort=asc&time=1418745237
把参数拼接成url,发送到服务器
/ :目录
?:分割前面的url和后面的参数
这些参数是Key和Value一一对应的,key是写死的,value可以想传什么值就传什么值
Post请求
post请求是放到http这个FormBody里面,通过抓包Contents目录下查找到key,通常来讲Post请求更安全一些
上传文件,像指定的服务器推送数据(把key推送到服务端),服务端也相应的给客户端Response(请求到的一些资源)
现在大多数app接口都不用get,都用post,post 既能向服务端发送数据(包括大文件),也可以进行请求数据(安全性更高,对数据格式长度没有限制,get是有长度限制的)
有抽象类,就有对应的实体类
点进去FormBody和MultipartBody,都是用于post请求的
FormBody属于普通请求,其CONTENT_TYPE = MediaType.get("application/x-www-form-urlencoded")
MultipartBody属于上传文件的(图片,视频,文档),它的CONTENT_TYPE是
参考 https://imququ.com/post/four-ways-to-post-data-in-http.html
FormBody.Builder builder = new FormBody.Builder();builder.add("key", "0f08cd674792667feb5ce236ea028747");Request request = new Request.Builder().url("http://apis.juhe.cn/lottery/types").post(builder.build()).build(); //建造者设计模式,优点:实现圆点(连点)操作Call call = client.newCall(request);//Response execute = call.execute(); //同步请求可直接拿到Response//基于观察者模式拿到responsecall.enqueue(new Callback() { //异步请求@Overridepublic void onFailure(Call call, IOException e) {Log.e("initGetNetData","onFailure" + e);}@Overridepublic void onResponse(Call call, Response response) throws IOException {Log.e("initGetNetData","onResponse" + response.body().string());}});