-
Notifications
You must be signed in to change notification settings - Fork 0
/
connectivity-in-android.html
270 lines (193 loc) · 13.3 KB
/
connectivity-in-android.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
<!DOCTYPE html>
<html>
<head>
<!-- [[! Document Settings ]] -->
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<!-- [[! Page Meta ]] -->
<title>优化App网络连通性问题</title>
<meta name="description" content="与机器,人,神共舞 - 编程,读书,思考,旅行,与机器对话,与人交谈,对神发问,探索,体验人生美丽的风景" />
<meta name="HandheldFriendly" content="True" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="shortcut icon" href="/assets/images/favicon.ico" >
<!-- [[! Styles'n'Scripts ]] -->
<link rel="stylesheet" type="text/css" href="/assets/css/screen.css" />
<link rel="stylesheet" type="text/css"
href="//fonts.googleapis.com/css?family=Merriweather:300,700,700italic,300italic|Open+Sans:700,400" />
<link rel="stylesheet" type="text/css" href="/assets/css/syntax.css" />
<!-- [[! Ghost outputs important style and meta data with this tag ]] -->
<link rel="canonical" href="/" />
<meta name="referrer" content="origin" />
<link rel="next" href="/page2/" />
<meta property="og:site_name" content="与机器,人,神共舞" />
<meta property="og:type" content="website" />
<meta property="og:title" content="与机器,人,神共舞" />
<meta property="og:description" content="编程,读书,思考,旅行,与机器对话,与人交谈,对神发问,探索,体验人生美丽的风景" />
<meta property="og:url" content="/" />
<meta property="og:image" content="/assets/images/cover1.jpg" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="与机器,人,神共舞" />
<meta name="twitter:description" content="编程,读书,思考,旅行,与机器对话,与人交谈,对神发问,探索,体验人生美丽的风景" />
<meta name="twitter:url" content="/" />
<meta name="twitter:image:src" content="/assets/images/cover1.jpg" />
<script type="application/ld+json">
{
"@context": "http://schema.org",
"@type": "Website",
"publisher": "Tao's Page",
"url": "/",
"image": "/assets/images/cover1.jpg",
"description": "编程,读书,思考,旅行,与机器对话,与人交谈,对神发问,探索,体验人生美丽的风景"
}
</script>
<meta name="generator" content="Jekyll 3.0.0" />
<link rel="alternate" type="application/rss+xml" title="与机器,人,神共舞" href="/rss.xml" />
</head>
<body class="home-template nav-closed">
<div class="nav">
<h3 class="nav-title">Menu</h3>
<a href="#" class="nav-close">
<span class="hidden">Close</span>
</a>
<ul>
<li class="nav-home " role="presentation"><a href="/">Home</a></li>
<li class="nav-about " role="presentation"><a href="/about.html">About</a></li>
<li class="nav-fables " role="presentation"><a href="/tag/machine/">Machine</a></li>
<li class="nav-speeches " role="presentation"><a href="/tag/human/">Human</a></li>
<li class="nav-fiction " role="presentation"><a href="/tag/god/">God</a></li>
<li class="nav-author " role="presentation"><a href="/author/hetao/">Author</a></li>
</ul>
<a class="subscribe-button icon-feed" href="/vocab.html">Apps</a>
</div>
<span class="nav-cover"></span>
<div class="site-wrapper">
<!-- [[! Everything else gets inserted here ]] -->
<!-- default -->
<!-- The comment above "< default" means - insert everything in this file into -->
<!-- the [body] of the default.hbs template, which contains our header/footer. -->
<!-- Everything inside the #post tags pulls data from the post -->
<!-- #post -->
<header class="main-header post-head no-cover">
<nav class="main-nav clearfix">
</nav>
</header>
<main class="content" role="main">
<article class="post tag-machine">
<header class="post-header">
<h1 class="post-title">优化App网络连通性问题</h1>
<section class="post-meta">
<!-- <a href='/'>Tao He</a> -->
<time class="post-date" datetime="2019-05-29">29 May 2019</time>
<!-- [[tags prefix=" on "]] -->
on
<a href='/tag/machine'>Machine</a>
</section>
</header>
<section class="post-content">
<p>最近遇到一个棘手的问题,我们开发的一款App在中东那边出现大量的UnknownHostException, 导致App在中东那边体验很糟,很快这个问题就被抛给了我们技术优化组,我和另外一名同事就开始定位问题的原因并且试图提出一个可行的解决方案。一般理解,出现UnkownHostException就是DNS失败了,在我们开始解决这个问题的时候,App的网络库已逐渐开始使用OkHttp,我们想到自定义DNS过程,在系统DNS失败的情况下再尝试其他DNS方式,降低UnknownHostException出现的频率,优化App网络连通性。
为了实现我们的技术方案,我们针对OkHttp的DNS做了如下几件事:</p>
<ul>
<li>创建OkHttpClient时自定义DNS</li>
</ul>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>OkHttpClient.Builder builder = new OkHttpClient.Builder()
// 实现OKHttp DNS接口, 改变默认的DNS行为
.dns(OkHttpDns.getInstance())
</code></pre></div></div>
<p>上面的OkHttpDns须实现Dns接口</p>
<ul>
<li>实现自定义DNS</li>
</ul>
<p>目前我们的设计是使用责任链模式实现四层DNS的lookup:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
Local Cache --> System DNS --> GA --> Google DNS
</code></pre></div></div>
<p>其中第一层是本地DNS缓存(Local Cache),整个缓存只是内存缓存,DNS过程开始时,先去本地缓存找,如果在缓存中没有命中,就走系统DNS, 系统DNS如果也失败,整个链条就继续往下,到GA,到Google DNS,如果到Google DNS还没有解析成功,仍旧抛出<code class="language-plaintext highlighter-rouge">UnknownHostException</code>,DNS过程失败。基本的代码逻辑如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>private OkHttpDns() {
//使用责任链模式实现四层DNS的lookup: Local Cache --> System DNS --> GA --> Google DNS
mDnsChain = new CacheDnsHandler();
SystemDnsHandler okhttp = new SystemDnsHandler();
GADnsHandler ga = new GADnsHandler();
GoogleHttpDnsHandler google = new GoogleHttpDnsHandler();
mDnsChain.setTarget(okhttp);
okhttp.setTarget(ga);
ga.setTarget(google);
}
@Override
public List<InetAddress> lookup(String hostname) throws UnknownHostException {
// IP直连的情况,直接返回
if (InetAddressValidator.isIPAddress(hostname)) {
return Arrays.asList(InetAddress.getAllByName(hostname));
}
List<InetAddress> allDNSResult = new ArrayList<>();
List<InetAddress> list = mDnsChain.lookup(hostname, allDNSResult);
if (list == null) {
// DNS完全失败后,清空黑名单,删除local cache相应的条目
IPStatusCache.getInstance().clear();
if (allDNSResult.isEmpty()) {
throw new UnknownHostException("Broken system behaviour for dns lookup of " + hostname);
} else {
return allDNSResult;
}
}
return list;
}
</code></pre></div></div>
<ul>
<li>
<p>添加黑名单机制,进一步优化DNS
一次连接成功后缓存DNS结果,host+ip为key,IPStatus为value,连接失败后会更新DNS缓存的失败次数,超过5次则认为进入了黑名单,在每次DNS完全失败后清空本地DNS缓存,防止所有的缓存都进入黑名单,缓存失效。</p>
</li>
<li>
<p>处理IP直连的情况
这种情况的处理很简单,检查传入的host是不是ip,如果是就直接返回。</p>
</li>
</ul>
<p>经过这样的优化以后,<code class="language-plaintext highlighter-rouge">UnknownHostException</code>在请求失败中的比重和请求的总失败率大幅下降,验证了我们这个技术方案的合理性,可以说网络连通性大大提高,再次回顾这个方案,突然发现它其实一个通用的解决方案,虽然我们这次解决的是海外,如中东地区的连通性问题,其实这个方案完全可以移植到国内,只要将Google HttpDNS换成国内的HttpDNS即可,整体的DNS流程可以不做任何改动即可成为一个完整的App DNS解决方案。</p>
</section>
<footer class="post-footer">
<!-- Everything inside the #author tags pulls data from the author -->
<!-- #author-->
<!-- Add Disqus Comments -->
</footer>
</article>
</main>
<aside class="read-next">
<!-- [[! next_post ]] -->
<a class="read-next-story no-cover" href="/upgrade-to-http2">
<section class="post">
<h2>HTTP/2 从初探到实际场景的运用</h2>
<p>HTTP/2其实出来已经很长时间了,但是一个新的协议,新的标准从出现到大量被采用,总是会经历一个过程。我们的产品最近基于网络请求优化的要求,需要将之前的HTTP/1都升级到支持HTTP/2,这个事情其实需要两方同时进行,服务端和客服端需要同时支持HTT/2。在做这个事情之前需要首先调研HTTP/2,了解它的特性和优缺点。 对HTTP2的基本的调研结果如下: #### HTTP/2的由来 HTTP/2 的前身是 SPDY协议,第一版草稿就是基于 SPDY3 规范修改制定而来。HTTP/2维持了原来 HTTP 的范式(不改动 HTTP/1.x 的语义、方法、状态码、URI 以及首部字段等等) ####...</p>
</section>
</a>
<!-- [[! /next_post ]] -->
<!-- [[! prev_post ]] -->
<a class="read-next-story prev no-cover" href="/sumsung-foldable-adapt">
<section class="post">
<h2>三星折叠屏适配小计</h2>
<p>三星要在近期发布折叠屏手机,消息一出,我们就得抓紧做适配,也没有什么现成的经验作参考,尤其令人头疼的是还没有真机供我们测试。没办法,兵来将挡,水来土掩,进过一周多的调研和开发,终于做完了大部分App页面的适配工作,也因为做这个适配的过程踩了很多坑,特写下这篇小记。 在我们开始做适配前,首先要解决没有真机可做测试的问题,无法验证适配的效果,还好三星也想到了这个问题,在官网提供了一个模拟器应用,Foldable Emulator, 通过这个模拟器App可以在Fold和Unfold模式之间自由切换,验证适配效果。当我们适配工作结束后在真机上测试时发现,这个模拟器的仿真度极高,基本没有出现和真机上效果有出入的地方。 在解决了没有真机的问题之后,我们就开始着手真正的适配工作。首先遇到的问题就是折叠屏手机需要在Fold和Unfold之间频繁切换,而这种切换的效果和横竖屏切换是一样的,默认都会导致Activity重建,而Activity重建又会导致一系列的连锁反应,如需要恢复大量数据、重新建立网络连接或执行其他密集操作,因此在适配前就需要规划哪些页面(Activity)需要重建,哪些不需要重建,所以需要按Activity是否重建这两种情况来分析切屏的后果和应对方法。 重建Activity 因配置变更而引起的完全重启可能会给用户留下应用运行缓慢的体验。 此外,依靠系统通过onSaveInstanceState() 回调保存的 Bundle,可能无法完全恢复 Activity 状态,因为它并非设计用于携带大型对象(例如位图),而且其中的数据必须先序列化,再进行反序列化,这可能会消耗大量内存并使得配置变更速度缓慢。 在这种情况下,如果 Activity 因配置变更而重启,则可通过保留...</p>
</section>
</a>
<!-- [[! /prev_post ]] -->
</aside>
<!-- /post -->
<footer class="site-footer clearfix">
<section class="copyright"><a href="/">与机器,人,神共舞</a> © 2024</section>
</footer>
</div>
<!-- [[! Ghost outputs important scripts and data with this tag ]] -->
<script type="text/javascript" src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
<!-- [[! The main JavaScript file for Casper ]] -->
<script type="text/javascript" src="/assets/js/jquery.fitvids.js"></script>
<script type="text/javascript" src="/assets/js/index.js"></script>
<!-- Add Google Analytics -->
<!-- Google Analytics Tracking code -->
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-78960009-1', 'auto');
ga('send', 'pageview');
</script>
</body>
</html>