最后
由于题目很多整理答案的工作量太大,所以仅限于提供知识点,详细的很多问题和参考答案我都整理成了 PDF文件
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
File file = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DCIM), “building.jpg”);
RequestBody requestBody = RequestBody.create(JPEG, file);
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
Post提交流
RequestBody requestBody = new RequestBody() {
@Nullable
@Override
public MediaType contentType() {
return null;
}
@Override
public void writeTo(@NotNull BufferedSink bufferedSink) throws IOException {
bufferedSink.writeUtf8(requestString);
}
};
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
使用Gson解析response
String url = “https://api.github.com/gists/c2a7c39532239ff261be”;
class Gist{
Map<String,GistFile> files;
}
class GistFile{
String content;
}
Gson gson = new Gson();
Gist gist = gson.fromJson(response.body().charStream(),Gist.class);
for(Map.Entry<String,GistFile> entry:gist.files.entrySet()){
Log.i(TAG,entry.getKey()+ " "+entry.getValue().content);
}
设置超时
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(3, TimeUnit.SECONDS)
.build();
配置新client
.newBuilder()
会返回一个配置相同的buidler
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(3, TimeUnit.SECONDS)
.build();
OkHttpClient client2 = client.newBuilder()
.connectTimeout(5, TimeUnit.SECONDS)
.build();
拦截器
拦截器(Interceptor
)是OkHttp的概念,也是核心功能。OkHttp有两种拦截器,分别是应用拦截器和网络拦截器。拦截器的主要目的在于重写request
和response
,可以在发出request
前修改headers或body,也可以在收到response
前修改headers或body。我们完全可以在用户收到reponse
前将其修改成一个完全不一样的新response
,这一功能使得我们可以进行后续的缓存策略修改或是使用gzip压缩requestBody等操作。应用拦截器在用户发出一次请求后的全过程中仅调用一次,而网络拦截器可能因为重定向等问题多次调用,例如有一次重定向就会调用两次。拦截器可以设置多个,并按添加顺序进行拦截。下图来自OkHttp文档:
两种拦截器区别如下,参考OkHttp文档原文:
Application interceptors
- Don’t need to worry about intermediate responses like redirects and retries.
- Are always invoked once, even if the HTTP response is served from the cache.
- Observe the application’s original intent. Unconcerned with OkHttp-injected headers like
If-None-Match
.- Permitted to short-circuit and not call
Chain.proceed()
.- Permitted to retry and make multiple calls to
Chain.proceed()
.- Can adjust
Call
timeouts usingwithConnectTimeout
,withReadTimeout
,withWriteTimeout
.Network Interceptors
- Able to operate on intermediate responses like redirects and retries.
- Not invoked for cached responses that short-circuit the network.
- Observe the data just as it will be transmitted over the network.
- Access to the
Connection
that carries the request.
个人翻译如下:
应用拦截器
- 使用时不需要考虑例如重定向、重试等中转请求带来的影响
- 全过程只拦截一次,即使拦截的
response
来自缓存- 可处理来自Applcation(参考拦截器图解)的本意。(例如
no-cache
)不涉及OkHttp的头部注入例如If-None-Match
头部(这是在core注入的)- 可以不调用
Chain.proceed()
(例如return
一个来自缓存的response
,但不能return null
)- 可以重试和多次调用
Chain.proceed()
- 可通过
withConnectTimeout
,withReadTimeout
,withWriteTimeout
调整Call
的超时时间.网络拦截器
- 可处理例如重定向、重试等中转请求
- 不涉及缓存的调用
- 可处理来自服务器的原始响应
- 可对最终发出前的请求做读写
- 实现拦截器
以上所说拦截器可对处于中间时期的request
和response
做修改,就是在chain.proceed(request)
的前后完成的。
chain.proceed(request)
会返回通过core或服务器处理后得到的response
,这个方法会阻塞线程。
String url = “http://publicobject.com/helloworld.txt”;
class LoggingInterceptor implements Interceptor {
@Override public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
//do something to rewrite request
long t1 = System.nanoTime();
Log.i(TAG,String.format(“Sending request %s on %s%n%s”,
request.url(), chain.connection(), request.headers()));
Response response = chain.proceed(request);
long t2 = System.nanoTime();
Log.i(TAG,String.format(“Received response for %s in %.1fms%n%s”,
response.request().url(), (t2 - t1) / 1e6d, response.headers()));
//do something to rewrite response
return response;
}
}
- 设置应用拦截器
两种拦截器在实现的时候没有区别,充当那种拦截器取决于调用的方法是.addInterceptor()
或是.addNetworkInterceptor()
。.addInterceptor()
表示设置应用拦截器,.addNetworkInterceptor()
则是网络拦截器。
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new LoggingInterceptor())
.build();
- 设置网络拦截器
OkHttpClient client = new OkHttpClient.Builder()
.addNetworkInterceptor(new LoggingInterceptor())
.build();
缓存处理
OkHttp默认不使用缓存,可以调用.cache()
开启,但.cache()
仅能设置缓存区大小和缓存读写的位置。Cache-Control
头部是Http协议定义的,而OkHttp完全遵循Http协议,所以OkHttp的缓存策略是由请求头或响应头中的Cache-Control
头部而定的。如果服务器返回的response
已经带有Cache-Control响应头,在buidler中调用.cache()
即可使用缓存。反之当收到的response
没有设置Cache-Control
时,可以在拦截器里手动添加,不同参数对应不同的缓存策略。不论response
是否有Cache-Control
,始终可以在发出request
时添加例如Cache-control: no-cache
来控制缓存使用与否。
启用缓存
String url = “http://publicobject.com/helloworld.txt”;
int _10MB = 10 * 1024 * 1024;
File cacheDir = getCacheDir();
Cache cache = new Cache(cacheDir, _10MB);
OkHttpClient client = new OkHttpClient.Builder()
.cache(cache)
.build();
缓存策略
Http协议的Cache-Control
的参数有很多,可设置多个参数,多个参数间用逗号分隔开。以下主要介绍其中几种的含义
Cache-Control: max-age=3600
设置缓存过期时间为一小时。单位为秒,用于response
Cache-Control: max-stale=3600
表示接受使用过期的缓存,最长时间为过期后的一小时,单位为秒。用于request
Cache-control: no-cache
先不使用本地缓存,向服务器验证缓存是否过期后决定缓存使用与否,且并不取消本次response
的缓存。用于request
Cache-control: no-store
本次请求不缓存得到的response,也表示本次请求不读取缓存。用于request
Cache-control: only-if-cached
仅尝试使用缓存。用于request
Cache-control: public
表明响应可以被任何对象(包括:发送请求的客户端,代理服务器,等等)缓存,即使是通常不可缓存的内容。(例如该响应没有max-age
指令或Expires
消息头, 该响应对应的请求方法是 POST )Cache-control: private
表明响应只能被单个用户缓存,不能作为共享缓存(即代理服务器不能缓存它)。私有缓存可以缓存响应内容,比如:对应用户的本地浏览器。Cache-Control: min-fresh=3600
表示客户端希望获取一个能在指定的秒数内保持其最新状态的响应。Cache-control: must-revalidate
一旦资源过期(比如已经超过max-age
),在成功向原始服务器验证之前,缓存不能用该资源响应后续请求。
此外与缓存有关的header可能还有Expires
和Pragma
,这里暂不介绍
- 直接修改Cache-Control头部定义缓存策略
class CacheInterceptor implements Interceptor {
@Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
request = request.newBuilder()
.header(“Cache-Control”, “max-stale=3600”)
.build();
return chain.proceed(request);
}
}
Interceptor interceptor = new CacheInterceptor();
mClient = new OkHttpClient.Builder()
.cache(cache)
.addInterceptor(interceptor)
.build();
- 使用CacheControl.Builder()定义缓存策略
CacheControl
类只能在拦截器中使用,其实质只是在请求头或响应头为Cache-Control
添加不同的参数而已,并没有其他作用
class ForceCacheInterceptor implements Interceptor {
@Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
CacheControl cacheControl = new CacheControl.Builder()
.onlyIfCached()
.build();
request = request.newBuilder()
.cacheControl(cacheControl)
.build();
return chain.proceed(request);
}
}
Interceptor interceptor = new ForceCacheInterceptor();
mClient = new OkHttpClient.Builder()
.cache(cache)
.addInterceptor(interceptor)
.build();
CacheControl.Builder的常用方法
- .maxAge(3600, TimeUnit.SECONDS);
- .maxStale(3600, TimeUnit.SECONDS);
- .noCache();
- .noStore();
- .onlyIfCached();
含义参考Cache-Control
参数介绍
- 使用CacheControl的伴生对象定义缓存策略
CacheControl
的伴生对象有两个,CacheControl.FORCE_CACHE
和CacheControl.FORCE_NETWORK
,分别表示强制使用缓存和强制使用网络。
public class ForceNetworkInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request.Builder builder = chain.request().newBuilder();
if (!NetworkUtils.internetAvailable()) {
builder.cacheControl(CacheControl.FORCE_NETWORK);
}
return chain.proceed(builder.build());
}
}
Interceptor interceptor = new ForceNetworkInterceptor();
mClient = new OkHttpClient.Builder()
.cache(cache)
.addInterceptor(interceptor)
.build();
CacheControl.FORCE_CACHE
的本质是将Cache-Control
请求头设为"max-stale=2147483647, only-if-cached"
。
重新查看上图就可以知道,Application发出的请求是被OkHttp core处理,而OkHttp core发出的请求将提交给服务器,如果我们希望本次请求强制使用缓存,就应该使用应用拦截器而不是网络拦截器,这段请求头告诉OkHttp本次请求仅使用缓存的响应。
CacheControl.FORCE_NETWORK
的本质是将Cache-Control请求头设为"no-cache"
。与FORCE_CACHE
同理,它也应该使用应用拦截器,这段请求头告诉OkHttp本次请求仅使用来自网络的响应。
缓存流程
决定一次请求是否使用缓存的流程,主要的几个步骤如下(任何一步决定使用网络时将不再检查后续步骤)
-
检查
response
是否包含Date
,Expires
,Last-Modified
,ETag
,Age
这些请求头。若都不包含则使用网络。 -
检查
request
的Cache-Control
请求头,"no-cache"
使用网络,"only-if-cached"
使用缓存。 -
检查
response
的ETag
响应头,若存在则使用网络,并且本次请求会带有与ETag
值相同的If-None-Match
请求头。若实际数据没有变化,服务器处理后会给出304 Not Modified状态码,表示资源没有修改,并且不会返回body,指示客户端使用缓存,所以此时OkHttp也会使用缓存。 -
检查
response
的Last-Modified
响应头,若存在则使用网络,并且本次请求会带有与Last-Modified
值相同的If-Modified-Since
请求头。后续同ETag
。 -
检查
response
的Date
响应头,若存在则使用网络,并且本次请求会带有与Date
值相同的If-Modified-Since
请求头。后续同ETag
。 -
检查
response
的max-age
,如果过期则使用网络,否则使用缓存。但也可能因为其他参数如max-stale
等影响最终计算结果。
缓存总结
一次完整的涉及缓存的网络请求大致如下图,其中成功的结果有两个(绿框),分别是使用缓存和使用服务器的新数据。在Force cache后找不到缓存就会失败(红框)。从初始阶段向下看,第一步判断是否调用.cache()
开启了缓存功能。第二步检查之前是否缓存过,两者任意一者不满足则使用网络。第三步判断是否需要验证,与ETag
等有关,存在则使用网络向服务器验证,服务器若返回304则response
完全从缓存中取出。这步操作同普通请求一样,可能涉及无网络问题。当无网络时可以Force cache进行处理,最后则是成功或失败时的异常处理。下图来自Medium
- 当存储缓存时
如果此时要修改response
的头部,应该使用网络拦截器修改response
。
只要在构建client
的时候调用了.cache()
,那么通过这个client
得到的响应一定会被缓存,但之后不一定会被使用。存储缓存时与Cache-Control
请求头或响应头都无关,Cache-Control
只有当读取缓存时才会用到。
- 当读取缓存时
应该使用应用拦截器修改request
。
想要强制使用缓存,有以下3种方式:
- 使用
CacheControl.FORCE_CACHE
- 调用
CacheControl.Builder().onlyIfCached()
- 直接添加
Cache-Control: "only-if-cached"
请求头
但如果缓存不存在,这次请求就会失败并抛出IOException
,并且得到一个带有504 Gateway Timeout的response
。
想要强制使用网络,有以下3种方式:
- 使用
CacheControl.FORCE_NETWORK
- 调用
CacheControl.Builder().noCache()
- 直接添加
Cache-Control: "no-cache"
请求头
如果你在请求头没有指定任何有关缓存的参数,OkHttp将按照缓存中response
的数个响应头进行不同的处理,可能使用缓存,也可能向服务器验证response
后决定是否使用缓存,或是进行一次普通的请求。
最后
配合各种资料辅助学习
在当下这个信息共享的时代,很多资源都可以在网络上找到,只取决于你愿不愿意找或是找的方法对不对了
很多朋友不是没有资料,大多都是有几十上百个G,但是杂乱无章,不知道怎么看从哪看起,甚至是看后就忘
如果大家觉得自己在网上找的资料非常杂乱、不成体系的话,我也分享一套给大家,比较系统,我平常自己也会经常研读。
2020最新上万页的大厂面试真题
七大模块学习资料:如NDK模块开发、Android框架体系架构…
只有系统,有方向的学习,才能在段时间内迅速提高自己的技术。
尾声
如果你想成为一个优秀的 Android 开发人员,请集中精力,对基础和重要的事情做深度研究。
对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。
这里,笔者分享一份从架构哲学的层面来剖析的视频及资料分享给大家梳理了多年的架构经验,筹备近6个月最新录制的,相信这份视频能给你带来不一样的启发、收获。
PS:之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
架构篇
《Jetpack全家桶打造全新Google标准架构模式》
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
[外链图片转存中…(img-MpqdKdmV-1715830533596)]
架构篇
《Jetpack全家桶打造全新Google标准架构模式》
[外链图片转存中…(img-KSNwr8ss-1715830533596)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!