1. 什么是Builder模式

关于什么是builder模式,具体可以参考一下这篇文章
Builder模式分为:经典builder模式和变种builder模式。而在Android项目中,一般使用的都是变种Builder模式,OkHttp中也例外。不管是OkHttpClient还是Request,都是通过变种Builder模式进行构建的。下面来我们来看一下OkHttp的源码,来感受一下Builder模式的应用。

2. OkHttp中的实例

OkHttp中很多类都有用到Builder模式,使用上都大同小异,所以下面就以OkHttpClient为例子,来讲一下Builder模式在OkHttp中的应用。

2.1 OkHttpClient的构建方式

构建OkHttpClient有两种方式:

//方式1
OkHttpClient client1 = new OkHttpClient();

//方式2
OkHttpClient client2 = new OkHttpClient.Builder()
                .writeTimeout(1000, TimeUnit.MILLISECONDS)
                .readTimeout(1000, TimeUnit.MILLISECONDS)
                .addInterceptor(new Interceptor() {
                    @Override
                    public Response intercept(Chain chain) throws IOException {
                        return null;
                    }
                })
                .build();
    }

方式2很明显,一看就是通过Builder模式构建的。但是实际上,方式1内部也是通过调用了new Builder()的方式通过构建了一个默认参数的OkHttpClient

public OkHttpClient() {
    this(new Builder());
  }

2.2 OkHttpClient中的Builder

2.2.1 通过Builder构建一个OkHttpClient

OkHttpClient(Builder builder) {
    this.dispatcher = builder.dispatcher;
    this.proxy = builder.proxy;
    this.protocols = builder.protocols;
    this.connectionSpecs = builder.connectionSpecs;
    this.interceptors = Util.immutableList(builder.interceptors);
    this.eventListenerFactory = builder.eventListenerFactory;
    if (builder.sslSocketFactory != null || !isTLS) {
      this.sslSocketFactory = builder.sslSocketFactory;
      this.certificateChainCleaner = builder.certificateChainCleaner;
    } else {
      X509TrustManager trustManager = Util.platformTrustManager();
      this.sslSocketFactory = newSslSocketFactory(trustManager);
      this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
    }
    //还有一堆参数...
}

这个没啥好说的,就是将builder中设置过的参数一一赋值给OkHttpClient,然后可能还会做一些判断处理,保证参数的合法性

2.2.2 默认的Builder

public Builder() {
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;
      connectionSpecs = DEFAULT_CONNECTION_SPECS;
      followRedirects = true;
      retryOnConnectionFailure = true;
      callTimeout = 0;
      connectTimeout = 10_000;
      readTimeout = 10_000;
      writeTimeout = 10_000;
      pingInterval = 0;
      //还有一堆参数...
    }

在2.1 OkHttpClient的构建方式中我们知道,OkHttpClient的默认构造函数中就调用了Builder()方式。在我们自己的项目中,也可以通过这种方式设置默认的参数,特别是一些必选项,防止因为调用者没有设置必选参数而出现问题。

OkHttpClient client1 = new OkHttpClient();

public OkHttpClient() {
    this(new Builder());
  }

2.2.3 反构建Builder

2.2.3.1 反构建的作用
public Builder newBuilder() {
    return new Builder(this);
  }

Builder(OkHttpClient okHttpClient) {
      this.dispatcher = okHttpClient.dispatcher;
      this.proxy = okHttpClient.proxy;
      this.protocols = okHttpClient.protocols;
      this.connectionSpecs = okHttpClient.connectionSpecs;
      this.interceptors.addAll(okHttpClient.interceptors);
      //还有一堆参数...
    }

个人理解,反构建Builder最主要的使用场景就是:当使用者构建了OkHttpClient之后发现有一些参数需要改变,所以就可以通过反构建一个Builder,然后调用Builder的set方法修改对应的参数在build一次,生成新的OkHttpClient对象。
当然,假如OkHttpClient中有参数对应的set方法(实际上并没有),也是能修改参数的。但是这就违背了Builder模式的设计原则,而且会引发一些问题:

1、比如我想重新设置OkHttpClientsslSocketFactory参数,使用Builder模式时,在生成OkHttpClient的时候是会做参数校验的(见2.1.1)。但是如果直接通过OkHttpClient的set方法的形式,这个参数校验就会显得很麻烦,要在sslSocketFactorycertificateChainCleaner的set方法中都做一次校验。
2、这是我认为更关键的一个点:如果我们需要同时修改多个参数,并希望它们同时生效。在多线程高并发的情况下,很可能我们在获取参数配置的时候,只有部分参数是修改过的,还有部分参数是原来的值,这会引发很多不可预估的问题。

2.2.3.2 反构建的实际应用

在使用Interceptor时,我们经常会使用到Request的反构建,例如:

public Response intercept(Chain chain) throws IOException {
                        Request request = chain.request();
                        //先走p2p请求,如果失败了,再走原始url请求。
                        Response response = chain.proceed(request.newBuilder().url(p2pUrl).build());
                        if (response.isSuccessful()) {
                            return response;
                        }
                        return chain.proceed(request);
                    }

3. Tips

3.1 Builder插件

手打Builder是一件非常痛苦的事情。不会偷懒的程序员不是好的程序员:),所以我们可以通过Builder插件来帮助我们自动化生成Builder。插件的名字叫做InnerBuilder。可以在File–>Settings–>Plugins中搜索"InnerBuilder"下载。
在这里插入图片描述
使用时,只需要在类的空白处按下快捷键Alt + Insert就可以看到Builder的选项。点击后,插件就会帮助我们自动化生成一个Builder了~

在这里插入图片描述

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐