OkHttp+Retrofit+RxJava流程浅析+面试

做Android开发的小伙伴都知道,现在最流行的网络框架就是OkHttp+Retrofi+RxJava。今天我们就一起来学习一下流程实现,让你面试分分钟过

导入依赖

//ok依赖
implementation("com.squareup.okhttp3:okhttp:3.0.0")
配置retrofit2.0
implementation 'com.squareup.retrofit2:retrofit:+'
//让retrofit支持Gson自动解析json
implementation 'com.squareup.retrofit2:converter-gson:+'
//Rxjava2需要依赖
implementation 'io.reactivex.rxjava2:rxjava:+'
//Rxandroid需要依赖
implementation 'io.reactivex.rxjava2:rxandroid:+'

流程分析


OkHttpClient  okHttpClient =  newOkHttpClient.Builder()
.connectTimeout(Config.HTTP_TIMEOUT,TimeUnit.MILLISECONDS)
.retryOnConnectionFailure(true).addInterceptor(newDeviceInterceptor())
.addInterceptor(OkHttpUtils.getHttpInterceptor(TAG))
.build();

Retrofit retrofit=newRetrofit.Builder()
 .client(okHttpClient)
 .baseUrl(Config.DEVICE_HOST)
 .addConverterFactory(JacksonConverterFactory.create())
 .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
 .build();
 //创建代理实例
  ServerApi requst= retrofit.create(ServerApi.class); 

假设在没有newOkHttpClient.Builder()的时候
初始化Retrofit.Builder的时候相当于初始化了一个 OkHttp对象

这个在源码中有体现Retrofit会为我们new OkHttpClient.Builder()对象

我们在构建Retrofit.Builder时,它首先解析它的 ServiceMethod 一个 LinkedHashMap 容器

public final class Retrofit {
  private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>();
    private final okhttp3.Call.Factory callFactory;
    private final HttpUrl baseUrl;
    private final List<Converter.Factory> converterFactories;
    private final List<CallAdapter.Factory> adapterFactories;
    private final Executor callbackExecutor;
    private final boolean validateEagerly;
    Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,
        List<Converter.Factory> converterFactories, List<CallAdapter.Factory> adapterFactories,
        Executor callbackExecutor, boolean validateEagerly) {
        this.callFactory = callFactory;
        this.baseUrl = baseUrl;
        this.converterFactories = unmodifiableList(converterFactories); // Defensive copy at call site.
        this.adapterFactories = unmodifiableList(adapterFactories); // Defensive copy at call site.
        this.callbackExecutor = callbackExecutor;
        this.validateEagerly = validateEagerly;
    }
    //省略其他代码
}

它在这个 ServiceMethod中做了哪些操作:

//记录自定义接口所有内容.  
//通过Retrofit的动态代理 + 反射获取接口当中所有的信息(注解,请求方式,参数类型,参数Key)

(接口演示)
 @FormUrlEncoded 
 @POST("health/user/v1/weChatLogin") 
  Observable<WxBean> weChatLogin(@Field("wxCode") String code);

通过serviceMethod中的信息会生成一个 OkHttpCall 接下来就是Call里面的
RxJava 的观察者(obverser)与被观察者(obverseable) ,来完成线程之间的调度

Observable<Integer> observable = Observable.create(new ObservableOnSubscribe<Integer>() {
        @Override
        public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
            emitter.onNext(1);
        }
    });
    Consumer<Integer> consumer = new Consumer<Integer>() {
        @Override
        public void accept(Integer integer) throws Exception {
        }
    };

    observable.subscribeOn(Schedulers.io)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(consumer);
}

这也是RxJava最大的好处 ,优美的异步操作
接着我们从被观察者里面反射出来的信息来进行拼接参数 也就是说Http的请求格式
生成一个request去真正走了OkHttp请求网络
OkHttp请求网络可以分成三大步
1.队列
2.线程池
3.网络拦截器

OKhttp会通过dispatch事件分发器将请求对象发送到RealCall 类,并且RealCall 实现了Call方法,RealCall是真正的核心代码

 @Override
    public Call newCall(Request request) {
        return RealCall.newRealCall(this, request, false);
    }
//RealCall 主要方法:同步请求 :client.newCall(request).execute(); 
//                  异步请求: client.newCall(request).enqueue();

1.将不同的请求放到不同的请求队列当中 同步的请求放置同步请求队列,异步请求放置异步请求队列,在走异步请求队列时会同时缓存一条记录到异步执行队列,最后通过dispatch分发器发送线程池中执行

  //TODO 执行异步请求
    synchronized void enqueue(AsyncCall call) {
        //TODO 同时请求不能超过并发数(64,可配置调度器调整)
        //TODO okhttp会使用共享主机即 地址相同的会共享socket
        //TODO 同一个host最多允许5条线程通知执行请求
        if (runningAsyncCalls.size() < maxRequests &&
                runningCallsForHost(call) < maxRequestsPerHost) {
            //TODO 加入运行队列 并交给线程池执行
            runningAsyncCalls.add(call);
            //TODO AsyncCall 是一个runnable,放到线程池中去执行,查看其execute实现
            executorService().execute(call);
        } else {
            //TODO 加入等候队列
            readyAsyncCalls.add(call);
        }
    }

常见的四种线程池(附加)
newFixedThreadPool
固定大小的线程池,可以指定线程池的大小,该线程池corePoolSize和maximumPoolSize相等,阻塞队列使用的是LinkedBlockingQueue,大小为整数最大值。
当任务提交十分频繁的时候,LinkedBlockingQueue
迅速增大,存在着耗尽系统资源的问题。而且在线程池空闲时,即线程池中没有可运行任务时,它也不会释放工作线程,还会占用一定的系统资源,需要shutdown。
—————————————————————————————————
newSingleThreadExecutor
单个线程线程池,只有一个线程的线程池,阻塞队列使用的是LinkedBlockingQueue,若有多余的任务提交到线程池中,则会被暂存到阻塞队列,待空闲时再去执行。按照先入先出的顺序执行任务。
—————————————————————————————————
newScheduledThreadPool
定时线程池,该线程池可用于周期性地去执行任务,通常用于周期性的同步数据。
—————————————————————————————————
newCachedThreadPool
缓存线程池,缓存的线程默认存活60秒。线程的核心池corePoolSize大小为0,最大线程数为Integer.MAX_VALUE,阻塞队列使用的是SynchronousQueue。是一个直接提交的阻塞队列, 他总会迫使线程池增加新的线程去执行新的任务。在没有任务执行时,当线程的空闲时间超过keepAliveTime(60秒),则工作线程将会终止被回收,当提交新任务时,如果没有空闲线程,则创建新线程执行任务,会导致一定的系统开销。如果同时又大量任务被提交,而且任务执行的时间不是特别快,那么线程池便会新增出等量的线程池处理任务,这很可能会很快耗尽系统的资源。
————————————————————————————————
2. 我们OkHttp所用的线程就是缓存线程池

Ok的拦截器总共分为七个,在执行线程的时候就会走Interceptor。
Interceptor是OkHttp最核心的一个东西,采用了责任链的设计模式,不要误以为它只是负责拦截请求进行一些额外的处理(例如cookie),实际上它把时机的网络请求、缓存、透明压缩等功能都统一了起来,每一个功能只是一个Interceptor,它们再连接成为Interceptor.Chain,环环相扣,最终圆满完成一次网络请求。interceptor的顺序也很重要,比如负责网络请求的Interceptor必须放在最后,负责缓存策略的Inteceptor需要放到前面。


//TODO 核心代码 开始真正的执行网络请求
  Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    //TODO 责任链
    List<Interceptor> interceptors = new ArrayList<>();
    //TODO 在配置okhttpClient 时设置的intercept 由用户自己设置
    interceptors.addAll(client.interceptors());
    //TODO 负责处理失败后的重试与重定向
    interceptors.add(retryAndFollowUpInterceptor);
    //TODO 负责把用户构造的请求转换为发送到服务器的请求 、把服务器返回的响应转换为用户友好的响应 处理 配置请求头等信息
    //TODO 从应用程序代码到网络代码的桥梁。首先,它根据用户请求构建网络请求。然后它继续呼叫网络。最后,它根据网络响应构建用户响应。
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    //TODO 处理 缓存配置 根据条件(存在响应缓存并被设置为不变的或者响应在有效期内)返回缓存响应
    //TODO 设置请求头(If-None-Match、If-Modified-Since等) 服务器可能返回304(未修改)
    //TODO 可配置用户自己设置的缓存拦截器
    interceptors.add(new CacheInterceptor(client.internalCache()));
    //TODO 连接服务器 负责和服务器建立连接 这里才是真正的请求网络
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      //TODO 配置okhttpClient 时设置的networkInterceptors
      //TODO 返回观察单个网络请求和响应的不可变拦截器列表。
      interceptors.addAll(client.networkInterceptors());
    }
    //TODO 执行流操作(写出请求体、获得响应数据) 负责向服务器发送请求数据、从服务器读取响应数据
    //TODO 进行http请求报文的封装与请求报文的解析
    interceptors.add(new CallServerInterceptor(forWebSocket));
    //TODO 创建责任链
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());
    //TODO 执行责任链
    return chain.proceed(originalRequest);
  }

3.OkHttp拦截器分析
先列出之前在getResponseWithInterceptorChain方法中添加的各Interceptor,概括一下它们分别负责什么功能:

* client.interceptors()  用户自定义的Interceptor,能拦截到所有的请求 
* RetryAndFollowUpInterceptor  负责失败重连和重定向相关 
* BridgeInterceptor  负责配置请求的头信息,比如Keep-Alive、gzip、Cookie等可以优化请求 
* CacheInterceptor  负责缓存管理,使用DiskLruCache做本地缓存,CacheStrategy决定缓存策略 
* ConnectInterceptor  开始与目标服务器建立连接,获得RealConnection 
* client.networkInterceptors()  用户自定义的Interceptor,仅在生产网络请求时生效 
* CallServerInterceptor  向服务器发出一次网络请求的地方。

最后会生成一个响应体Body由通过被观察者发送一个事件(just) 观察者会接收在Model层接收这个事件
至此一个流程介绍完毕

你可能感兴趣的:(android,面试)