@linux1s1s
2017-01-09T12:12:52.000000Z
字数 6438
阅读 1766
Base 2017-01
我们不管Build模式是啥,如何使用,在什么场景下使用,我们先看看在Android客户端红遍半边天的第三方库是如何用Build的。
OkHttp构建网络Request:
Request request = new Request.Builder().url(url).build();
Retrofit构建实例:
Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create()).build();
Fresco构建DraweeController实例:
DraweeController controller = Fresco.newDraweeControllerBuilder().setUri(Uri.parse(GIF)).setAutoPlayAnimations(true).build();
从上面的三个实例,我们发现,大红大紫的三方库居然都使用了build模式,所以我们本着认真学习的姿势来看看他们是如何干活的。
我们准备以Okhttp为样板,深究内部的实现,其他两个大同小异,不再赘述。
涉及到Build的Request类如下:
Request.java
public final class Request {private final HttpUrl url;private final String method;private final Headers headers;private final RequestBody body;private final Object tag;private volatile CacheControl cacheControl; // Lazily initialized.private Request(Builder builder) {this.url = builder.url;this.method = builder.method;this.headers = builder.headers.build();this.body = builder.body;this.tag = builder.tag != null ? builder.tag : this;}public HttpUrl url() {return url;}public String method() {return method;}public Headers headers() {return headers;}public String header(String name) {return headers.get(name);}public List<String> headers(String name) {return headers.values(name);}public RequestBody body() {return body;}public Object tag() {return tag;}public Builder newBuilder() {return new Builder(this);}/*** Returns the cache control directives for this response. This is never null, even if this* response contains no {@code Cache-Control} header.*/public CacheControl cacheControl() {CacheControl result = cacheControl;return result != null ? result : (cacheControl = CacheControl.parse(headers));}public boolean isHttps() {return url.isHttps();}@Override public String toString() {return "Request{method="+ method+ ", url="+ url+ ", tag="+ (tag != this ? tag : null)+ '}';}public static class Builder {private HttpUrl url;private String method;private Headers.Builder headers;private RequestBody body;private Object tag;public Builder() {this.method = "GET";this.headers = new Headers.Builder();}private Builder(Request request) {this.url = request.url;this.method = request.method;this.body = request.body;this.tag = request.tag;this.headers = request.headers.newBuilder();}public Builder url(HttpUrl url) {if (url == null) throw new NullPointerException("url == null");this.url = url;return this;}/*** Sets the URL target of this request.** @throws IllegalArgumentException if {@code url} is not a valid HTTP or HTTPS URL. Avoid this* exception by calling {@link HttpUrl#parse}; it returns null for invalid URLs.*/public Builder url(String url) {if (url == null) throw new NullPointerException("url == null");// Silently replace websocket URLs with HTTP URLs.if (url.regionMatches(true, 0, "ws:", 0, 3)) {url = "http:" + url.substring(3);} else if (url.regionMatches(true, 0, "wss:", 0, 4)) {url = "https:" + url.substring(4);}HttpUrl parsed = HttpUrl.parse(url);if (parsed == null) throw new IllegalArgumentException("unexpected url: " + url);return url(parsed);}/*** Sets the URL target of this request.** @throws IllegalArgumentException if the scheme of {@code url} is not {@code http} or {@code* https}.*/public Builder url(URL url) {if (url == null) throw new NullPointerException("url == null");HttpUrl parsed = HttpUrl.get(url);if (parsed == null) throw new IllegalArgumentException("unexpected url: " + url);return url(parsed);}/*** Sets the header named {@code name} to {@code value}. If this request already has any headers* with that name, they are all replaced.*/public Builder header(String name, String value) {headers.set(name, value);return this;}/*** Adds a header with {@code name} and {@code value}. Prefer this method for multiply-valued* headers like "Cookie".** <p>Note that for some headers including {@code Content-Length} and {@code Content-Encoding},* OkHttp may replace {@code value} with a header derived from the request body.*/public Builder addHeader(String name, String value) {headers.add(name, value);return this;}public Builder removeHeader(String name) {headers.removeAll(name);return this;}/** Removes all headers on this builder and adds {@code headers}. */public Builder headers(Headers headers) {this.headers = headers.newBuilder();return this;}/*** Sets this request's {@code Cache-Control} header, replacing any cache control headers already* present. If {@code cacheControl} doesn't define any directives, this clears this request's* cache-control headers.*/public Builder cacheControl(CacheControl cacheControl) {String value = cacheControl.toString();if (value.isEmpty()) return removeHeader("Cache-Control");return header("Cache-Control", value);}public Builder get() {return method("GET", null);}public Builder head() {return method("HEAD", null);}public Builder post(RequestBody body) {return method("POST", body);}public Builder delete(RequestBody body) {return method("DELETE", body);}public Builder delete() {return delete(RequestBody.create(null, new byte[0]));}public Builder put(RequestBody body) {return method("PUT", body);}public Builder patch(RequestBody body) {return method("PATCH", body);}public Builder method(String method, RequestBody body) {if (method == null) throw new NullPointerException("method == null");if (method.length() == 0) throw new IllegalArgumentException("method.length() == 0");if (body != null && !HttpMethod.permitsRequestBody(method)) {throw new IllegalArgumentException("method " + method + " must not have a request body.");}if (body == null && HttpMethod.requiresRequestBody(method)) {throw new IllegalArgumentException("method " + method + " must have a request body.");}this.method = method;this.body = body;return this;}/*** Attaches {@code tag} to the request. It can be used later to cancel the request. If the tag* is unspecified or null, the request is canceled by using the request itself as the tag.*/public Builder tag(Object tag) {this.tag = tag;return this;}public Request build() {if (url == null) throw new IllegalStateException("url == null");return new Request(this);}}}
上面的代码很长,其实我们关注的有几点如下:
Request构造器
private Request(Builder builder) {this.url = builder.url;this.method = builder.method;this.headers = builder.headers.build();this.body = builder.body;this.tag = builder.tag != null ? builder.tag : this;}
注意是个私有构造器,而且参数是Builder实例,这个构造器限制了必须通过Builder实例才能生成Request实例,并且,只能在看得到构造器的地方调用如下所示:
public Request build() {if (url == null) throw new IllegalStateException("url == null");return new Request(this);}
这个成员方法build()构造了Request实例,所以有必要看一下如何获取Build实例
public Builder() {this.method = "GET";this.headers = new Headers.Builder();}private Builder(Request request) {this.url = request.url;this.method = request.method;this.body = request.body;this.tag = request.tag;this.headers = request.headers.newBuilder();}
提供了两个重载的构造器,而且分别是私有的和公有的,所以对于类的外部来说,只能通过公有构造器获取Build实例,然后在调用成员方法build()构造Request实例,当然如果如果想反向获取Build实例,也可以直接通过Request的成员方法获取,如下:
public Builder newBuilder() {return new Builder(this);}
对于上面的设计,可能有些人会问,为啥非得通过Build这个类来间接的完成Request的构建呢?
原因个人理解是,因为Request的参数比较多,通过Build可以有效的管理这些参数,另外还可以做有效性检查等其他操作。
至于其他的原因读者可以另行补充。
