-
public String encodedName(int index) {
-
return encodedNames.get(index);
-
}
-
public String name(int index) {
-
return percentDecode(encodedName(index), true);
-
}
-
public String encodedValue(int index) {
-
return encodedValues.get(index);
-
}
-
public String value(int index) {
-
return percentDecode(encodedValue(index), true);
-
}
-
@Override public MediaType contentType() {
-
return CONTENT_TYPE;
-
}
-
@Override public long contentLength() {
-
return writeOrCountBytes(null, true);
-
}
-
@Override public void writeTo(BufferedSink sink) throws IOException {
-
writeOrCountBytes(sink, false);
-
}
-
/**
-
- Either writes this request to {@code sink} or measures its content length. We have one method
-
- do double-duty to make sure the counting and content are consistent, particularly when it comes
-
- to awkward operations like measuring the encoded length of header strings, or the
-
- length-in-digits of an encoded integer.
-
*/
-
private long writeOrCountBytes(BufferedSink sink, boolean countBytes) {
-
long byteCount = 0L;
-
Buffer buffer;
-
if (countBytes) {
-
buffer = new Buffer();
-
} else {
-
buffer = sink.buffer();
-
}
-
, size = encodedNames.size(); i < size; i++) {
-
) buffer.writeByte(‘&’);
-
buffer.writeUtf8(encodedNames.get(i));
-
buffer.writeByte(‘=’);
-
buffer.writeUtf8(encodedValues.get(i));
-
}
-
if (countBytes) {
-
byteCount = buffer.size();
-
buffer.clear();
-
}
-
return byteCount;
-
}
-
public static final class Builder {
-
private final List names = new ArrayList<>();
-
private final List values = new ArrayList<>();
-
public Builder add(String name, String value) {
-
names.add(HttpUrl.canonicalize(name, FORM_ENCODE_SET, false, false, true, true));
-
values.add(HttpUrl.canonicalize(value, FORM_ENCODE_SET, false, false, true, true));
-
return this;
-
}
-
public Builder addEncoded(String name, String value) {
-
names.add(HttpUrl.canonicalize(name, FORM_ENCODE_SET, true, false, true, true));
-
values.add(HttpUrl.canonicalize(value, FORM_ENCODE_SET, true, false, true, true));
-
return this;
-
}
-
public FormBody build() {
-
return new FormBody(names, values);
-
}
-
}
-
}
``````````````我们主要来看一下方法````````````````writeOrCountBytes``````````````````````````````,通过writeOrCountBytes来计算请求体大小和将请求体写入BufferedSink。
至于BufferSink和Buffer类,这两个类是Okio中的类,Buffer相当于一个缓存区,BufferedSink相当于OutputStream,它扩展了
OutputStream的功能,Okio的完整源码我后续也会写博客
③方式三:MultipartBody分块表单创建
``MultipartBody, 既可以添加表单,又可以也可以添加文件等二进制数据,我们就看几个重要的方法
-
public static Part createFormData(String name, String filename, RequestBody body) {
-
if (name == null) {
-
throw new NullPointerException(“name == null”);
-
}
-
StringBuilder disposition = new StringBuilder(“form-data; name=”);
-
appendQuotedString(disposition, name);
-
if (filename != null) {
-
disposition.append(“; filename=”);
-
appendQuotedString(disposition, filename);
-
}
-
return create(Headers.of(“Content-Disposition”, disposition.toString()), body);
-
}
我们来看这个方法,我们是addPart还是addFormDataPart最终都走到了这个方法,封装成一个Part对象,也就是实体内容中
的Content-Disposition跟文件二进制流或者键值对的值
````````````````MultipartBody和FormBody大体上相同,主要区别在于`writeOrCountBytes方法,分块表单主要是将每个块的大小进行累加来求出请求体大小,如果其中有一个块没有指定大小,就会返回-1。所以分块表单中如果包含文件,默认是无法计算出大小的,除非你自己给文件的RequestBody指定contentLength。`````````````````
-
private long writeOrCountBytes(BufferedSink sink, boolean countBytes) throws IOException {
-
long byteCount = 0L;
-
Buffer byteCountBuffer = null;
-
if (countBytes) {
-
//如果是计算大小的话,就new个
-
sink = byteCountBuffer = new Buffer();
-
}
-
//循环块
-
, partCount = parts.size(); p < partCount; p++) {
-
Part part = parts.get§;
-
//获取每个块的头
-
Headers headers = part.headers;
-
//获取每个块的请求体
-
RequestBody body = part.body;
-
//写 --xxxxxxxxxx 边界
-
sink.write(DASHDASH);
-
sink.write(boundary);
-
sink.write(CRLF);
-
//写块的头
-
if (headers != null) {
-
, headerCount = headers.size(); h < headerCount; h++) {
-
sink.writeUtf8(headers.name(h))
-
.write(COLONSPACE)
-
.writeUtf8(headers.value(h))
-
.write(CRLF);
-
}
-
}
-
//写块的Content_Type
-
MediaType contentType = body.contentType();
-
if (contentType != null) {
-
sink.writeUtf8("Content-Type: ")
-
.writeUtf8(contentType.toString())
-
.write(CRLF);
-
}
-
//写块的大小
-
long contentLength = body.contentLength();
-
) {
-
sink.writeUtf8("Content-Length: ")
-
.writeDecimalLong(contentLength)
-
.write(CRLF);
-
} else if (countBytes) {
-
// We can’t measure the body’s size without the sizes of its components.
-
//如果有个块没有这名大小,就返回-1.
-
byteCountBuffer.clear();
-
return -1L;
-
}
-
sink.write(CRLF);
-
//如果是计算大小就累加,否则写入BufferedSink
-
if (countBytes) {
-
byteCount += contentLength;
-
} else {
-
body.writeTo(sink);
-
}
-
sink.write(CRLF);
-
}
-
//写 --xxxxxxxxxx-- 结束边界
-
sink.write(DASHDASH);
-
sink.write(boundary);
-
sink.write(DASHDASH);
-
sink.write(CRLF);
-
if (countBytes) {
-
byteCount += byteCountBuffer.size();
-
byteCountBuffer.clear();
-
}
-
return byteCount;
-
}
4.CacheControl
( 1) Cache-Control:
Cache-Control指定请求和响应遵循的缓存机制。在请求消息或响应消息中设置Cache-Control并不会修改另一个消息处理过程中的缓存处理过程。请求时的缓存指令有下几种:
-
Public:所有内容都将被缓存(客户端和代理服务器都可缓存)。
-
Private:内容只缓存到私有缓存中(仅客户端可以缓存,代理服务器不可缓存)
-
no-cache:请求或者响应消息不能缓存
-
no-store:不使用缓存,也不存储缓存
-
max-age:缓存的内容将在指定时间(秒)后失效, 这个选项只在HTTP 1.1可用, 并如果和Last-Modified一起使用时, 优先级较高
-
在 xxx 秒后,浏览器重新发送请求到服务器,指定时间(秒)内,客户端会直接返回cache而不会发起网络请求,若过期会自动发起网络请求
-
min-fresh:指示客户端可以接收响应时间小于当前时间加上指定时间的响应。
-
max-stale:指示客户端可以接收超出超时期间的响应消息。如果指定max-stale消息的值,那么客户机可以接收超出超时期指定值之内的响应消息。
(2)CacheControl类
①常用的函数
-
final CacheControl.Builder builder = new CacheControl.Builder();
-
builder.noCache();//不使用缓存,全部走网络
-
builder.noStore();//不使用缓存,也不存储缓存
-
builder.onlyIfCached();//只使用缓存
-
builder.noTransform();//禁止转码
-
builder.maxAge(, TimeUnit.MILLISECONDS);//指示客户机可以接收生存期不大于指定时间的响应。
-
builder.maxStale(, TimeUnit.SECONDS);//指示客户机可以接收超出超时期间的响应消息
-
builder.minFresh(, TimeUnit.SECONDS);//指示客户机可以接收响应时间小于当前时间加上指定时间的响应。
-
CacheControl cache = builder.build();//cacheControl
②CacheControl的两个常量:
-
public static final CacheControl FORCE_NETWORK = new Builder().noCache().build();//不使用缓存
-
public static final CacheControl FORCE_CACHE = new Builder()
-
.onlyIfCached()
-
.maxStale(Integer.MAX_VALUE, TimeUnit.SECONDS)
-
.build();//只使用缓存
③请求时如何使用:
-
final CacheControl.Builder builder = new CacheControl.Builder();
-
builder.maxAge(, TimeUnit.MILLISECONDS);
-
CacheControl cache = builder.build();
-
final Request request = new Request.Builder().cacheControl(cache).url(requestUrl).build();
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
小结
有了这么多优秀的开发工具,可以做出更高质量的Android应用。
当然了,“打铁还需自身硬”,想要写出优秀的代码,最重要的一点还是自身的技术水平,不然用再好的工具也不能发挥出它的全部实力。
在这里我也分享一份大佬自己收录整理的Android学习PDF+架构视频+面试文档+源码笔记,还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料这些都是我闲暇还会反复翻阅的精品资料。在脑图中,每个知识点专题都配有相对应的实战项目,可以有效的帮助大家掌握知识点。
总之也是在这里帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习
链图片转存中…(img-baEwDs6u-1710846255684)]
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-WR2Eb8FL-1710846255685)]
小结
有了这么多优秀的开发工具,可以做出更高质量的Android应用。
当然了,“打铁还需自身硬”,想要写出优秀的代码,最重要的一点还是自身的技术水平,不然用再好的工具也不能发挥出它的全部实力。
在这里我也分享一份大佬自己收录整理的Android学习PDF+架构视频+面试文档+源码笔记,还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料这些都是我闲暇还会反复翻阅的精品资料。在脑图中,每个知识点专题都配有相对应的实战项目,可以有效的帮助大家掌握知识点。
总之也是在这里帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习
如果你有需要的话,可以点击这里领取