-
Notifications
You must be signed in to change notification settings - Fork 0
/
feed.xml
407 lines (290 loc) · 18 KB
/
feed.xml
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
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>lrhehe's blog</title>
<link>http://blog.lrhehe.com/</link>
<description>Recent content on lrhehe's blog</description>
<generator>Hugo -- gohugo.io</generator>
<language>zh-CN</language>
<copyright>Copyright (c) 2016. All rights reserved.</copyright>
<lastBuildDate>Sat, 02 Apr 2016 12:24:04 +0800</lastBuildDate>
<atom:link href="http://blog.lrhehe.com/feed/index.xml" rel="self" type="application/rss+xml" />
<item>
<title>Android 插件化开发</title>
<link>http://blog.lrhehe.com/post/Andriod%20%E6%8F%92%E4%BB%B6%E5%8C%96%E5%BC%80%E5%8F%91/</link>
<pubDate>Sat, 02 Apr 2016 12:24:04 +0800</pubDate>
<guid>http://blog.lrhehe.com/post/Andriod%20%E6%8F%92%E4%BB%B6%E5%8C%96%E5%BC%80%E5%8F%91/</guid>
<description>
<h2 id="一-背景:c600ce58f5db185da8297985ea4b3e2d">一、背景</h2>
<p>在<a href="www.yudanfudao.com">猿辅导</a> android app 的实际开发维护中遇到两个问题:</p>
<ol>
<li>代码维护问题:代码主要有两个部分,直播相关和直播无关。开发人员已经分为客户端和直播两个小组,但是两边的代码还在一个仓库中,不方便维护。</li>
<li>上线问题:每次上线,都需要两个小组的人员一起守候到很晚(现在采用了灰度发布,好很多了)。</li>
</ol>
<p>而理想情况是:</p>
<ol>
<li>代码分离,各自维护一个仓库,客户端依赖直播库</li>
</ol>
<p>为了代码分离,我们将所有直播相关内容拆分出来,得到直播库。
2. 独立实时上线,直播库可随时上线,而不需要客户端发布新版</p>
<p>为了独立实时上线,需要将直播部分做成猿辅导 app 的插件。</p>
<h2 id="二-现存方案比较:c600ce58f5db185da8297985ea4b3e2d">二、现存方案比较</h2>
<p>下图是来自 <a href="https://github.com/wequick/Small">Small</a> 框架作者给出的<a href="https://github.com/wequick/Small/tree/master/Android">各个现有框架在 9 个方面的支持情况</a>:</p>
<table>
<thead>
<tr>
<th></th>
<th>DyLA</th>
<th>DiLA</th>
<th>ACDD</th>
<th>DyAPK</th>
<th>DPG</th>
<th>APF</th>
<th>Small</th>
</tr>
</thead>
<tbody>
<tr>
<td>加载非独立插件[1]</td>
<td>×</td>
<td>x</td>
<td>√</td>
<td>√</td>
<td>×</td>
<td>√</td>
<td>√</td>
</tr>
<tr>
<td>加载.so插件</td>
<td>×</td>
<td>×</td>
<td>! [2]</td>
<td>×</td>
<td>×</td>
<td>×</td>
<td>√</td>
</tr>
<tr>
<td>Activity生命周期</td>
<td>√</td>
<td>√</td>
<td>√</td>
<td>√</td>
<td>×</td>
<td>√</td>
<td>√</td>
</tr>
<tr>
<td>Service动态注册</td>
<td>×</td>
<td>×</td>
<td>√</td>
<td>×</td>
<td>×</td>
<td>√</td>
<td>x [3]</td>
</tr>
<tr>
<td>资源分包共享[4]</td>
<td>×</td>
<td>×</td>
<td>! [5]</td>
<td>![5]</td>
<td>×</td>
<td>! [6]</td>
<td>√</td>
</tr>
<tr>
<td>公共插件打包共享[7]</td>
<td>×</td>
<td>×</td>
<td>×</td>
<td>×</td>
<td>×</td>
<td>×</td>
<td>√</td>
</tr>
<tr>
<td>支持AppCompat[8]</td>
<td>×</td>
<td>×</td>
<td>×</td>
<td>×</td>
<td>×</td>
<td>×</td>
<td>√</td>
</tr>
<tr>
<td>支持本地网页组件</td>
<td>×</td>
<td>×</td>
<td>×</td>
<td>×</td>
<td>×</td>
<td>×</td>
<td>√</td>
</tr>
<tr>
<td>支持联调插件[9]</td>
<td>×</td>
<td>x</td>
<td>×</td>
<td>×</td>
<td>×</td>
<td>×</td>
<td>√</td>
</tr>
</tbody>
</table>
<blockquote>
<p>[1] 独立插件:一个完整的apk包,可以独立运行。比如从你的程序跑起淘宝、QQ,但这加载起来是要闹哪样?非独立插件:依赖于宿主,宿主是个壳,插件可使用其资源代码并分离之以最小化,这才是业务需要嘛。</p>
<p>&ndash; _“所有不能加载非独立插件的插件化框架都是耍流氓”_。</p>
<p>[2] ACDD加载.so用了Native方法(libdexopt.so),不是Java层,源码似乎未共享。</p>
<p>[3] Service更新频度低,可预先注册在宿主的manifest中,如果没有很好的理由说服我,现不支持。</p>
<p>[4] 要实现宿主、各个插件资源可互相访问,需要对他们的资源进行分段处理以避免冲突。</p>
<p>[5] 这些框架修改aapt源码、重编、覆盖SDK Manager下载的aapt,我只想说_“杀(wan)鸡(de)焉(kai)用(xin)牛(jiu)刀(hao)”<em>。 Small使用gradle-small-plugin,在后期修改二进制文件,实现了</em><strong>PP</strong>_段分区。</p>
<p>[6] 使用public-padding对资源id的_<strong>TT</strong>_段进行分区,分开了宿主和插件。但是插件之间无法分段。</p>
<p>[7] 除了宿主提供一些公共资源与代码外,我们仍需封装一些业务层面的公共库,这些库被其他插件所依赖。公共插件打包的目的就是可以单独更新公共库插件,并且相关插件不需要动到。</p>
<p>[8] AppCompat: Android Studio默认添加的主题包,Google主推的Metrial Design包也依赖于此。大势所趋。</p>
<p>[9] 联调插件:使用Android Studio调试宿主时,可直接在插件代码中添加断点调试。</p>
</blockquote>
<p>我们需要:</p>
<ol>
<li>支持 native 库 (猿辅导直播用到 native 库)</li>
<li>支持非独立插件并支持动态更新插件 (直播库是非独立插件,需要独立实时上线)</li>
<li>迁移简单方便 (避免引入过多的坑)</li>
<li>活跃度高 (方便学习,交流,和解决问题)</li>
</ol>
<p>最后,选定 Small 框架进行进一步了解。</p>
<h2 id="三-插件化原理:c600ce58f5db185da8297985ea4b3e2d">三、插件化原理</h2>
<h3 id="android-相关基础:c600ce58f5db185da8297985ea4b3e2d">Android 相关基础</h3>
<p>要想去弄明白 Android 插件化的原理,需要先了解一些 Android 基础知识。一个是 Android 打包 apk 的流程,另外一个是 Android 加载 apk 的流程。</p>
<h4 id="打包流程:c600ce58f5db185da8297985ea4b3e2d">打包流程</h4>
<p>可以参见:</p>
<p><a href="http://developer.android.com/sdk/installing/studio-build.html">Build System Overview</a></p>
<p><a href="http://blog.csdn.net/jason0539/article/details/44917745">android Apk打包过程概述_android是如何打包apk的</a></p>
<p>简单来说,一个apk主要是两部分,代码和资源。使用 aapt 命令可以方便查看其中内容:</p>
<pre><code>&gt; aapt l sample.apk
AndroidManifest.xml
assets/bundle.json
res/anim/abc_fade_in.xml
...
resources.arsc
classes.dex
META-INF/MANIFEST.MF
META-INF/CERT.SF
META-INF/CERT.RSA
</code></pre>
<p>class.dex 文件是代码部分。</p>
<p>而资源复杂一点,可以参考老罗的一篇博客:<a href="http://blog.csdn.net/luoshengyang/article/details/8744683">Android应用程序资源的编译和打包过程分析</a></p>
<p>简单来说就是资源分为 res 和 assets。</p>
<p>assets 和 res 里面的 raw 类型,还有二进制图片文件会保持不变,打包到 apk 中。</p>
<p>res 中的所有资源都会被分配一个 id,layout 中使用 @+id 的也会生成一个 id,保存为 resources.arsc 打包进 apk。之后,所有 res 中的 xml 文件中的对应的地方会被对应的 id 替换,并重新编译成二进制文件。</p>
<h4 id="加载过程:c600ce58f5db185da8297985ea4b3e2d">加载过程</h4>
<p>主要为代码加载和资源加载</p>
<p>代码加载可以参考:<a href="http://daojin.iteye.com/blog/1847093">安卓高手之路之ClassLoader(二)</a></p>
<p>也可以继续参考老罗的博客:<a href="http://blog.csdn.net/luoshengyang/article/details/6689748">Android应用程序启动过程源代码分析</a></p>
<p>简单来说就是:</p>
<p>代码加载可以通过 DexClassLoader 加载 class.dex (系统启动应用程序略有不同,可细看上面的文章)</p>
<p>资源加载通过 AssetManager 加载资源文件</p>
<h3 id="要解决的问题:c600ce58f5db185da8297985ea4b3e2d">要解决的问题</h3>
<p>插件化简单说就是从一个主应用 (宿主) 中去启动其他未在系统安装的 apk 插件或者库文件。所以会有下面一些要解决的问题:</p>
<ol>
<li>动态加载插件中的 class</li>
<li>动态加载插件中的资源,这里要保证能够和宿主还有其他已加载的插件的兼容</li>
<li>启动未注册的 activity</li>
<li>生命周期管理</li>
</ol>
<h3 id="常见解决方案:c600ce58f5db185da8297985ea4b3e2d">常见解决方案</h3>
<ol>
<li>动态加载插件中的 class</li>
</ol>
<p>使用 DexClassLoader 加载对应插件的代码</p>
<p>可以参考 Small 项目:<a href="https://github.com/wequick/Small/wiki/Android-dynamic-load-classes">Dynamic load classes</a></p>
<ol>
<li>动态加载插件中的资源</li>
</ol>
<p>方案1: 使用 AssetManager 加载对应的插件的资源文件,不过为了保证宿主还有各插件之间的资源文件 id 不冲突,在编译阶段,修改了输出文件中的资源文件 id。可以参考 Small 项目:<a href="https://github.com/wequick/Small/tree/master/Android/DevSample/buildSrc">Gradle-Small-Plugin</a></p>
<p>可以参考 Small 项目:<a href="https://github.com/wequick/Small/wiki/Android-dynamic-load-resources">Dynamic load resources</a></p>
<p>方案2: 新建一套 AssetManager 和 Resources,在插件 Activity 打开的时候,将</p>
<ol>
<li>启动未注册的 activity 和生命周期管理</li>
</ol>
<p>方案1:使用代理 Activity,可以参考 <a href="https://github.com/singwhatiwanna/dynamic-load-apk">Dynamic-load-Apk</a> 项目</p>
<p>方案2:瞒天过海,简单讲就是预先注册一些 Activity (比如A, A1, &hellip;) 作为壳,然后在准备启动插件 Actvity B 的时候,将其替换成 A 传给系统,但是在系统去生成 A 的时候,生成一个 B 出来,传给系统。这样就实现了未注册 Activity 的启动,也保证了其生命周期。不过在启动一些特殊 flag 的 Activity 时,比如 singleInstance,singleTask 和 singleTop 的 activity,存在限制。</p>
<p>可以参考 Small 项目:<a href="https://github.com/wequick/Small/wiki/Android-dynamic-register-activities">Dynamic register activities</a></p>
<p>##四、针对模块拆分和动态更新的最简方案</p>
<p>根据代码分离,实时上线的需求,设计了下面的方案:</p>
<h3 id="针对场景:c600ce58f5db185da8297985ea4b3e2d">针对场景</h3>
<ol>
<li>模块拆分:将工程中模块(application 或者 library)打包成独立插件 (plugin),宿主(Host)启动后进行加载</li>
<li>动态更新:可下载插件,对原插件进行动态更新(重启应用可生效)</li>
</ol>
<h3 id="原理:c600ce58f5db185da8297985ea4b3e2d">原理</h3>
<ol>
<li>使用 DexClassLoader 加载插件代码</li>
<li>给每个插件建立一套 AssetManager 和 Resources,借鉴 <a href="https://github.com/singwhatiwanna/dynamic-load-apk">Dynamic-load-Apk</a> 项目</li>
<li>代理 Instrumentation(借鉴 <a href="https://github.com/wequick/Small">Small</a> 项目),在启动插件 Activity 的时候设置对应的资源</li>
</ol>
<h3 id="优势:c600ce58f5db185da8297985ea4b3e2d">优势</h3>
<p>采用最简单的方案,引入最少的坑</p>
<ol>
<li>不用对现有代码做修改</li>
<li>不用对资源做修改</li>
<li>原生的 Activity 启动方式(存在局限,见局限1)</li>
</ol>
<h3 id="局限:c600ce58f5db185da8297985ea4b3e2d">局限</h3>
<ol>
<li>插件的 Activity 和 Service 要在宿主中进行注册</li>
<li>由于每个插件一套独立资源,宿主,插件之间不能够互相访问资源</li>
</ol>
<p>具体参见:<a href="https://github.com/lrhehe/AndroidHostPlugin">AndroidHostPlugin</a></p>
</description>
</item>
<item>
<title>使用GitHub Pages和Wercker搭建自动部署的个人博客</title>
<link>http://blog.lrhehe.com/post/build-autodeploy-user-gh-page/</link>
<pubDate>Wed, 27 Jan 2016 00:00:00 +0000</pubDate>
<guid>http://blog.lrhehe.com/post/build-autodeploy-user-gh-page/</guid>
<description>
<h3 id="what:04f2d6a4e2142a2a79db56be6c61559b">What</h3>
<p> 用markdown写博客,用git进行版本控制,用<a href="https://gohugo.io/">Hugo</a>生成网站,用<a href="http://wercker.com/">Wercker</a>自动化部署到 GitHubPage</p>
<figure >
<img src="http://blog.lrhehe.com/img/my-gh-page-flow.png" />
<figcaption>
<h4>My GithubPages</h4>
</figcaption>
</figure>
<h3 id="why:04f2d6a4e2142a2a79db56be6c61559b">Why</h3>
<p> 一直都想好好维护自己的个人博客,曾尝试用自建的WordPress写过几篇文章,后无疾而终。原因种种,种种原因,就不写在这里了。然而理想依然在,折腾无止境。</p>
<p> 想了一下,我对这一次搭建的个人博客有下面一些要求:</p>
<ul>
<li><p>使用git管理</p></li>
<li><p>使用markdown编辑</p></li>
<li><p>越简单越好</p></li>
</ul>
<p> 前两个要求是因为不想学习新软件的使用,而是花时间在一些能一直用的技术上。最后一个要求是因为懒,不想维护服务器,不想做各种配置,这些复杂的东西实在太容易忘记了。</p>
<h3 id="how:04f2d6a4e2142a2a79db56be6c61559b">How</h3>
<p> 作为一个软件开发人员,最熟的就是代码仓库了,如果维护个人博客就像维护一个代码仓库一样就好了。写上一篇博客,commit,push,然后大功告成!想想就开心啊。</p>
<p> 为了不维护服务器,选用了Github Pages,感谢非常有良心的github;使用<a href="https://gohugo.io/">Hugo</a>(对golang感兴趣,所以选了这个)来作为website engine,负责将文件构建成网站;使用<a href="http://wercker.com/">Wercker</a>来做自动化部署,每次push新的commit都会重新编译和部署网站。</p>
<p> 具体实现主要参考了Hugo的文档</p>
<ol>
<li><p>本地新建Hugo Project,push到Github,这是代码仓库</p></li>
<li><p>在Github上新建 [your username].github.io,这是博客仓库</p></li>
<li><p>连接代码仓库到Wercker,配置自动部署,将代码仓库的public文件夹内容push到博客仓库即可</p></li>
</ol>
<p></p>
<p>可以参考:</p>
<p><a href="https://gohugo.io/tutorials/github-pages-blog/">Hosting on GithubPages</a></p>
<p><a href="https://gohugo.io/tutorials/automated-deployments/">Automated deployments with Wercker</a></p>
<p><a href="https://github.com/lrhehe/my-hugo-github-pages">my-hugo-github-pages</a></p>
<p><a href="https://github.com/lrhehe/lrhehe.github.io">lrhehe.github.io</a></p>
<h3 id="conclude:04f2d6a4e2142a2a79db56be6c61559b">Conclude</h3>
<ul>
<li>清楚需求很重要,根据需求来选合适的实现方案</li>
<li>连接的红利,Wrecker 和 Github 相连,让世界变简单</li>
</ul>
</description>
</item>
</channel>
</rss>