by hzwusibo 20190504
常见问题一:简述一下OkHttp
OkHttp已被谷歌加入到Android的源码中。目前比较流行的Retrofit也是默认使用OkHttp的。
支持http2,对一台机器的所有请求共享同一个socket
内置连接池,支持连接复用,减少延迟
支持透明的gzip压缩响应体
通过缓存避免重复的请求
请求失败时自动重试主机的其他ip,自动重定向
好用的API
关于 HTTP2 和 HTTPS,这些你必须要知道
常见问题二:看过OkHttp的源码吗,简单说一下
(一)、总体设计
第一,通过一个构建者模式(Request.Builder)构建所有的request;然后分发到Dispatcher(分发器);
第二,Dispatcher再把request分发到HttpEngine(真正干活的类)中,HttpEngine首先要看一下本次请求有没有cache(缓存),如果有缓存,就从缓存中拿到信息,然后返回给response;如果没有缓存,HttpEngine就把request分发到ConnectionPool(连接池)中;
第三,在ConnectionPool(连接池)中,通过Connection发送请求,通过Route(路由)和Platfrom(平台),到达Server(Socket),获取到Data,然后返回response。
(二)、流程图
上面是OKHttp总体设计图,主要是通过Diapatcher不断从RequestQueue中取出请求(Call),根据是否已缓存调用Cache或Network这两类数据获取接口之一,从内存缓存或是服务器取得请求的数据。该引擎有同步和异步请求,同步请求通过Call.execute()直接返回当前的Response,而异步请求会把当前的请求Call.enqueue添加(AsyncCall)到请求队列中,并通过回调(Callback)的方式来获取最后结果。
OkHttp使用教程
Dispatcher线程池总结
1)调度线程池Disptcher实现了高并发,低阻塞的实现
2)采用Deque作为缓存,先进先出的顺序执行
3)任务在try/finally中调用了finished函数,控制任务队列的执行顺序,而不是采用锁,减少了编码复杂性提高性能。
Android面试题-OkHttp3源码分析
OkHttp通过定义许多拦截器一步一步地对Request进行拦截处理,直到请求返回网络数据,后面又倒过来,一步一步地对Response进行拦截处理,最后拦截的结果就是回调的最终Response。
RealInterceptorChain的proceed方法,通过顺序地传入一个拦截器的集合,创建一个RealInterceptorChain,然后拿到之前OkHttp创建的各种拦截器,并调用其interrupt方法,并返回Response对象。其调用顺序如下:
1)在配置 OkHttpClient 时设置的 interceptors;
2)负责失败重试以及重定向的 RetryAndFollowUpInterceptor;
3)负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应的 BridgeInterceptor;
4)负责读取缓存直接返回、更新缓存的 CacheInterceptor;
5)负责和服务器建立连接的 ConnectInterceptor;
6)配置 OkHttpClient 时设置的 networkInterceptors;
7)负责向服务器发送请求数据、从服务器读取响应数据的 CallServerInterceptor。
OkHttp的这种拦截器链采用的是责任链模式,这样的好处是将请求的发送和处理分开,并且可以动态添加中间的处理方实现对请求的处理、短路等操作
责任链模式是一种对象的行为模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。
可以看到 OkHttp 的线程池配置如下:
核心线程数:0
最大线程数:Iteger.MAX_VALUE,其实就是不限制
空闲线程保活时间:60s
任务队列:SynchronousQueue,为有界队列,且队列大小为 0,不存 Runnable,只是用来进行生产者和消费者之间的传递任务。
是不是很眼熟?和我们在 Executors 使用的 缓存线程池的配置完全一样,缓存线程池的代码如下:
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue(),
threadFactory);
}
所以 OkHttp 自己创建的这个线程池,和缓存线程池的区别,仅仅是修改了线程名。
我们简要分析一下缓存线程池的运行机制,进入到 ThreadPoolExecutor 中的execute方法的源码中,假设线程池处于运行状态,会有这样的流程:
新过来一个任务,由于核心线程数为 0,不需要创建核心线程,所以尝试加入任务队列
如果线程池中有线程刚好空闲可以接收任务,因为任务队列的类型是 SynchronousQueue,实际大小为 0,只做消费者和生产者的中转站,所以这时候,就可以入列成功,通过 SynchronousQueue 中转任务给空闲的线程执行。
如果当前线程池所有线程工作饱和,会入列失败。这时候 ThreadPoolExecutor 会调用addWorker(command, false)来创建并且启动新线程。
这样子,就会产生疑问,那么如果一次性同时发起大量的请求,不就会产生大量线程了?比如我一次性发起 100 个请求,那么是不是会发出 100 个线程?
是的,如果直接使用该线程池而没有其他的策略的话,是有这样的问题。
所以在 OkHttp 中线程池只是一个辅助作用,仅仅是用来做线程缓存,便于复用的。真正对这些请求的并发数量限制,执行时机等等都是调度器 Dispatcher 承担的。在 OkHttp 这里,线程池只是个带缓存功能的执行器,而真正的调度是外部包了一个调度策略的。
Retrofit
OkHttp和Retrofit的联系
Retrofit负责请求的数据和请求的结果,使用接口的方式呈现(注解)。
OkHttp负责请求的过程。
RxJava负责异步,各种线程之间的切换。
Retrofit是Square开源的一款适用于Android网络请求的框架。Retrofit底层是基于OkHttp实现的,与其他网络框架不同的是,它更多使用运行时注解的方式提供功能。
1.Retrofit优缺点
优点:
• 可以配置不同HTTP client来实现网络请求,如okhttp、httpclient等;
• 请求的方法参数注解都可以定制;
• 支持同步、异步和RxJava;
• 解耦;
• 可以配置不同的反序列化工具来解析数据,如json、xml等;
• 使用非常方便灵活;
2.Retrofit注解
Retrofit注解分为三大类,分别是HTTP请求方法注解(8种)、标记类注解(3种)和参数类注解(11种)。
• HTTP请求方法注解:GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS、HTTP
• 标记类注解:FormUrlEncoded、Multipart、Streaming
• 参数类注解:Headers、Header、Body、Field、FieldMap、Part、PartMap、Path、Query、QueryMap、Url
Retrofit底层对网络的访问默认是基于okhttp,不过Retrofit非常适合于restful url格式的请求,更多使用注解的方式提供功能。
Retrofit框架网络请求流程图
网络请求:APP发起网络请求,Retrofit通过注解配置请求参数、Header、Url之后,通过OkHttp发生网络请求给服务器。
服务器响应:服务器返回响应数据,OkHttp将数据传递给Retrofit,再把数据直接传递给APP,界面刷新反馈结果给用户。
OkHttp和Retrofit都是网络开源框架,但是他们之间的区别请不要混淆:
职责不同:
• Retrofit主要负责应用层面的封装,就是说主要面向开发者,方便使用,比如请求参数,响应数据的处理,错误处理等等。
• OkHttp主要负责socket部分的优化,比如多路复用,buffer缓存,数据压缩等等。
封装不同:
• Retrofit封装了具体的请求,线程切换以及数据转换。
• OkHttp 是基于Http协议封装的一套请求客户端,虽然它也可以开线程,但根本上它更偏向真正的请求,跟HttpClient, HttpUrlConnection的职责是一样的。
网上一般都推荐RxJava+Retrofit+OkHttp框架。