Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b2938f4

Browse files
committed
Merge pull request hehonghui#597 from chaossss/master
待校对
2 parents ab9185d + d0ffec9 commit b2938f4

11 files changed

+474
-0
lines changed
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
IndeterminateProgressbar解析 – Part 1
2+
---
3+
4+
> * 原文链接 : [Indeterminate – Part 1](https://blog.stylingandroid.com/indeterminate-part-1/)
5+
* 原文作者 : [Mark Allison](https://blog.stylingandroid.com/)
6+
* 译文出自 : [开发技术前线 www.devtf.cn](http://www.devtf.cn)
7+
* 转载声明: 本译文已授权[开发者头条](http://toutiao.io/download)享有独家转载权,未经允许,不得转载!
8+
* 译者 : [chaossss](https://github.com/chaossss)
9+
* 校对者: [desmond1121](https://github.com/desmond1121)
10+
* 状态 : 完成
11+
12+
13+
14+
15+
IndeterminateProgressBar 能在用户进行某项不定时长的耗时操作时提供绝佳的用户体验,之前我有教过大家怎么创建[水平的 IndeterminateProgressBar](http://blog.csdn.net/u012403246/article/details/49582789),今天我就来教大家实现圆形的 IndeterminateProgressBar,这个控件将支持 API 11(Honeycomb)以上的设备。
16+
17+
这系列博文将有别于传统的自定义控件方法,而是从 Lollipop+ 提供的 API 实现去理解其运作机制,最后我会将从中学习到的知识应用到开发中,创造一个在低版本设备中效果类似的控件。
18+
19+
在开始研究之前可以看看我们期望实现的效果:
20+
21+
[Youtube视频](https://youtu.be/g6Zo6WDS2Gg)
22+
23+
所以我们要做的就是:进度条在旋转,然后由长变短最后消失,再出现,由短变长。不妨看看[ Google 的实现](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/res/res/drawable/progress_medium_material.xml)
24+
25+
```xml
26+
<?xml version="1.0" encoding="utf-8"?>
27+
<!-- Copyright (C) 2014 The Android Open Source Project
28+
Licensed under the Apache License, Version 2.0 (the "License");
29+
you may not use this file except in compliance with the License.
30+
You may obtain a copy of the License at
31+
http://www.apache.org/licenses/LICENSE-2.0
32+
Unless required by applicable law or agreed to in writing, software
33+
distributed under the License is distributed on an "AS IS" BASIS,
34+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
35+
See the License for the specific language governing permissions and
36+
limitations under the License.
37+
-->
38+
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
39+
android:drawable="@drawable/vector_drawable_progress_bar_medium" >
40+
<target
41+
android:name="progressBar"
42+
android:animation="@anim/progress_indeterminate_material" />
43+
<target
44+
android:name="root"
45+
android:animation="@anim/progress_indeterminate_rotation_material" />
46+
</animated-vector>
47+
```
48+
49+
在这里他们使用了 AnimatedVectorDrawable - Lollipop+ 才有的特性,在低版本设备中将使用 Holo 风格的 drawable 资源。因此这个效果应用了两个动画 - 一个完成进度条长度变化,另一个完成旋转,[具体实现](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/res/res/drawable/vector_drawable_progress_bar_medium.xml)可以参考:
50+
51+
```xml
52+
<!--
53+
Copyright (C) 2014 The Android Open Source Project
54+
Licensed under the Apache License, Version 2.0 (the "License");
55+
you may not use this file except in compliance with the License.
56+
You may obtain a copy of the License at
57+
http://www.apache.org/licenses/LICENSE-2.0
58+
Unless required by applicable law or agreed to in writing, software
59+
distributed under the License is distributed on an "AS IS" BASIS,
60+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
61+
See the License for the specific language governing permissions and
62+
limitations under the License.
63+
-->
64+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
65+
android:height="48dp"
66+
android:width="48dp"
67+
android:viewportHeight="48"
68+
android:viewportWidth="48"
69+
android:tint="?attr/colorControlActivated">
70+
<group
71+
android:name="root"
72+
android:translateX="24.0"
73+
android:translateY="24.0" >
74+
<path
75+
android:name="progressBar"
76+
android:fillColor="#00000000"
77+
android:pathData="M0, 0 m 0, -19 a 19,19 0 1,1 0,38 a 19,19 0 1,1 0,-38"
78+
android:strokeColor="@color/white"
79+
android:strokeLineCap="square"
80+
android:strokeLineJoin="miter"
81+
android:strokeWidth="4"
82+
android:trimPathEnd="0"
83+
android:trimPathOffset="0"
84+
android:trimPathStart="0" />
85+
</group>
86+
</vector>
87+
```
88+
89+
我不想在这里解释 pathData,但它确实绘制了一个圆。但控件不会被渲染成一个圆,因为 trimPath 相关的值都为 0(其实是因为 trimPathEnd="0",使得 Path 起点处的绘制被停止)。
90+
91+
值得一提的是 path 的命名及其成员元素都与 Animator 关联并应用于 AnimatedVectorDrawable。旋转 Animator 也被应用到其中(在这里都是很常见的方法,所以我不打算过多地解释,有兴趣的话可以[看这里](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/res/res/anim/progress_indeterminate_rotation_material.xml))。但用于进度条长度变化的 Aniamtor 值得学习:
92+
93+
```xml
94+
<?xml version="1.0" encoding="utf-8"?>
95+
<!--
96+
Copyright (C) 2014 The Android Open Source Project
97+
Licensed under the Apache License, Version 2.0 (the "License");
98+
you may not use this file except in compliance with the License.
99+
You may obtain a copy of the License at
100+
http://www.apache.org/licenses/LICENSE-2.0
101+
Unless required by applicable law or agreed to in writing, software
102+
distributed under the License is distributed on an "AS IS" BASIS,
103+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
104+
See the License for the specific language governing permissions and
105+
limitations under the License.
106+
-->
107+
<set xmlns:android="http://schemas.android.com/apk/res/android" >
108+
<objectAnimator
109+
android:duration="1333"
110+
android:interpolator="@interpolator/trim_start_interpolator"
111+
android:propertyName="trimPathStart"
112+
android:repeatCount="-1"
113+
android:valueFrom="0"
114+
android:valueTo="0.75"
115+
android:valueType="floatType" />
116+
<objectAnimator
117+
android:duration="1333"
118+
android:interpolator="@interpolator/trim_end_interpolator"
119+
android:propertyName="trimPathEnd"
120+
android:repeatCount="-1"
121+
android:valueFrom="0"
122+
android:valueTo="0.75"
123+
android:valueType="floatType" />
124+
<objectAnimator
125+
android:duration="1333"
126+
android:interpolator="@android:anim/linear_interpolator"
127+
android:propertyName="trimPathOffset"
128+
android:repeatCount="-1"
129+
android:valueFrom="0"
130+
android:valueTo="0.25"
131+
android:valueType="floatType" />
132+
</set>
133+
```
134+
135+
在这里用了三个 Animator,三者并行运行动画效果,且每一个修改 path 元素中 trimPath 前缀的值,最终就实现了我们想要的效果。
136+
137+
其中第一和第二个 ObjectAnimator 控制圆的起点和终点,绘制出两点间的圆弧。所以当我们以不同的速率改变这两个 Animator 的值时,就会得到我们所说的变长变短的效果。除了应用的插值器,它们对应的参数都是相同的不同。
138+
139+
第三个 Animator 用于显示间距,以显示长度变化时,长度渐变消失的效果。
140+
141+
关注我的读者都知道我很喜欢用插值器,因为它们实在是太好用了!在下一篇博文中我会解释其中的细节。
142+
143+
因为这篇博文都是基于[ Google 源码](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/res/res/anim/progress_indeterminate_rotation_material.xml)进行的,所以我就不提供源码了,但后面的文章我保证都会有源码!

issue-42/剖析okhttp缓存机制.md

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
剖析OkHttp缓存机制
2+
---
3+
4+
> * 原文链接 : [WHAT’S UNDER THE HOOD OF THE OKHTTP’S CACHE?](http://www.schibsted.pl/2016/02/hood-okhttps-cache/)
5+
* 原文作者 : [Damian Petla](http://www.schibsted.pl/author/petla/)
6+
* 译文出自 : [开发技术前线 www.devtf.cn](http://www.devtf.cn)
7+
* 转载声明: 本译文已授权[开发者头条](http://toutiao.io/download)享有独家转载权,未经允许,不得转载!
8+
* 译者 : [chaossss](https://github.com/chaossss)
9+
* 校对者: [desmond1121](https://github.com/desmond1121)
10+
* 状态 : 完成
11+
12+
13+
14+
现在应用市场上的 App 无一不需要网络操作,这些应用的开发者大多数都选择结合使用 [OkHttp](http://square.github.io/okhttp/)[Retrofit](http://square.github.io/retrofit/) 来完成网络操作。okHttp 最为人称道的一个特性就是它的缓存机制,而我将在本篇博文对其进行剖析。
15+
16+
每次我用 OkHttp 时我都需要一些时间想想我将怎么使用它,我该用哪一个 HTTP 报头,作为一个客户端 App 我有哪些职责,我期望从服务器上获得什么,等等……所以对我来说,这篇博文将是 OkHttp 缓存特性的引用文档,我写这篇文档不是因为我在之后的开发中可能会接触 OkHttp 的缓存机制,而是因为我很有可能在下次需要使用它的时候却把这些知识忘了。
17+
18+
这篇博文用于帮助熟悉 OkHttp 的高级工程师,我希望这篇博文的读者最起码应该知道怎么使用 OkHttp 以及怎么开启缓存。如果你不知道的话,就去 OkHttp 的维基页面学习吧。当然了,这里面的解释可能有一部分是错的,我只是尝试去解释一些我所理解的内容,所以如果你发现我某些部分的解释是有问题的,请通过邮件或其他方式告诉我。
19+
20+
##分析的起点
21+
22+
我将会从以下类开始我的分析工作:CacheStrategy 和 HttpEngine,其中 CacheStrategy 包含缓存机制的所有逻辑,HttpEngine 是 CacheStrategy 被调用的地方。
23+
24+
- **CacheStrategy.Factory** 理解缓存候选响应的报头并将其转换为类成员的构造器
25+
- **CacheStrategy.getCandidate()** - 检查缓存候选响应,如果需要的话讲修改原始请求的报头
26+
27+
这两部分内容是 OkHttp 整个缓存机制最关键的部分,而且几乎包含了所有我们想要了解的信息。虽说其他方法也很重要,但如果你去分析的话会发现最终还是会回到这里。
28+
29+
##什么是缓存候选响应?
30+
31+
第一次进行 HTTP 请求时不会存在任何已缓存内容,相关 API 只在有缓存内容的时候才会被调用,这点相信不需要我再多解释了。
32+
33+
一旦响应被存储我们就能尝试将它用到后续的调用中,当然了,我们不可能将所有响应码对应的响应都存储下来。我们只存储以下响应码对应的响应:200, 203, 204, 300, 301, 404, 405, 410, 414, 501, 308。除此以外还有 302, 307,但存储它们对应的响应时必须满足以下条件之一:
34+
35+
- contains **Expires** header OR
36+
- **CacheControl** contains **max-age** OR
37+
- **CacheControl** contains **public** OR
38+
- **CacheControl** contains **private**
39+
40+
- 包含 **Expires** 报头
41+
- **CacheControl** 包含 **max-age**
42+
- **CacheControl** 包含 **public**
43+
- **CacheControl** 包含 **private**
44+
45+
需要注意:OkHttp 的缓存机制不支持缓存部分报文内容。
46+
47+
当我们重复某个请求,OkHttp 会判断是否已经有已缓存的响应,后文中我将称其为缓存候选响应。
48+
49+
##CacheStrategy 是什么?
50+
51+
CacheStrategy 需要一个新的报文和一个缓存候选响应,评估这两个 HTTP 报头是否有效并比较它们。
52+
53+
首先,存放在缓存候选响应报头中的部分成员是:
54+
55+
- Date
56+
- Expires
57+
- Last-Modified
58+
- ETag
59+
- Age
60+
61+
下面是需要检查的条件的汇总列表:
62+
63+
1. 判断缓存候选响应是否存在。
64+
2. 如果接收的是 HTTPS 请求,如果需要的话,判断缓存候选响应是否已进行握手。
65+
3. 判断缓存候选响应是否已缓存;这和 OkHttp 存储响应时完成的工作是相同的。
66+
4. 如果没有缓存,在请求报头的 Cache-Control 中检查对应内容,如果该标记为 true,缓存候选响应将不会被使用,后面的检查也会跳过。
67+
5. 在请求报头中查找 If-Modified-Since 或 If-None-Match,如果找到其中之一,缓存候选响应将不会被使用,后面的检查也会跳过。
68+
6. 进行一些计算以得到诸如缓存响应缓存响应存活时间,缓存存活时间,缓存最大失活时间。我不希望在此解释所有对应实现的细节,因为这会让博文变得冗长,你只需要知道以上提到的报文内容(例如:Date, Expires 等等…)还有请求 Cache-Control 中的 max-age, min-fresh, max-stale 这部分相关的计算耗时是毫秒级的。完成检查最简单的办法是写这样的伪代码:if ("cache candidate's no-cache" && "cache candidate's age" + "request's min-fresh" < "cache candidate's fresh lifetime" + "request's max-stale")
69+
70+
如果上述条件被满足,那么已缓存的响应报文将会被使用,后面的检查将跳过。
71+
7. 在需要进行网络操作时,下一次检查会判断它是否为“条件请求”。条件请求指的是:发送报文请求服务器响应,而服务器可能会也可能不会返回新的内容,或让我们使用已缓存的报文。
72+
73+
#WE LOOK FOR ANDROID DEVELOPERS!
74+
##条件请求
75+
76+
下一节看起来是新的内容,但在代码结构中和上一节所解释内容是在相同的方法里的。
77+
78+
1. 如果缓存候选响应包含 ETag 报头,那么新的 If-None-Match 请求报文会使用相同的 ETag 值。
79+
2. 如果上一点没有被满足,且缓存候选响应包含 Last-Modified,那么新的 If-Modified-Since 请求报文会使用该值。
80+
3. 如果以上两点都没有被满足,且缓存候选响应包含 Date,那么新的 If-Modified-Since 请求报文会使用该值。
81+
82+
##还有更多!
83+
84+
上述描述过程的最后,也就是 **CacheStrategy.getCandidate()** 方法返回后,还会有一个检查。OkHttp 会在此时判断报头的 **Cache-Strategy** 是否包含 “**only-if-cached**“ 参数。如果有的话,不会请求新的数据。OkHttp 会强制使用缓存候选响应(如果有且可用),不然的话会抛出 HTTP **504 Unsatisfiable Request** 错误。
85+
86+
##怎么运作?
87+
88+
你可能自己已经知道,进行 HTTP 操作时就是设置正确的报头。但不添加额外的报头内容,使用默认的 HTTP 调用也能完成网络操作,我会在后面提及这部分内容。
89+
90+
##无网络环境
91+
92+
刚开始我遇到的问题就包含这个,那时我天真地以为只要允许进行缓存就会解决这个问题,然而并不行,我知道的两种解决方法是:
93+
94+
- 使用 **Cache-Control : only-if-cached header**,报文将永远也不会到达服务器。只有存在可用缓存响应时才会检查并返回它。不然的话,会抛出 504 错误,所以开发的时候别忘了处理这个异常。
95+
- 使用 **Cache-Control : max-stale=[seconds]** 报头,这种办法更灵活一些,它向客户端表明可以接收缓存响应,只要该缓存响应没有超出其生命周期。而该缓存响应是否可用由 OkHttp 检查,所以不需要与服务器建立连接来判断。但即使如此,当缓存响应超出其生命周期,网络操作还是会进行,然后得到服务器返回的新内容。
96+
97+
##关心用户的开销和运行效率
98+
99+
上面提到的解决方法依赖 OkHttp 的验证机制能在离线时完成一些操作。通常 App 都是在线状态,而我们更希望得到新的数据,这样会一次又一次地从服务器上获取相同的内容,给用户产生冗余的开销并降低 App 的运行效率。
100+
101+
理想状态下,我们发送一个请求,得到真正的新数据。当服务器的数据没有发生改变,将得到 304 响应码,而 OkHttp 返回缓存响应。如果服务器支持缓存的话,这种情况是可以实现的。所以你需要做的大体就是和你的同事研究 **ETag****Last-Modified-Since** 是否被支持,如果支持的话,OkHttp 会帮你解决剩下的麻烦!
102+
103+
**NOTE**: 请不要自己设置 **If-Modified-Since****If-None-Match**。前文已经提到,设置这两个报头会跳过后续的许多检查,进行网络操作。
104+
105+
##强制网络操作
106+
107+
你也可以强制进行网络操作,但就我个人而言,我还没有这样用过 OkHttp,不过我发现了一些可能的应用场景。例如你在报头中使用了 max-stale,过了很久服务器接收到了该报文。当你想允许用户在某些特殊情况下强行进行刷新时,很简单,在请求中使用 Cache-Control : no-cache,你就能从服务器得到新的数据了。
108+
109+
##结论
110+
111+
在我看来,OkHttp 非常易用,极大简化了网络操作。而且有助于理解数据流,哪些报头能被设置以及它们的用处。我希望这篇博文能给你提供一些帮助。
112+
113+
祝你好运!

0 commit comments

Comments
 (0)