-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
526 lines (316 loc) · 474 KB
/
atom.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
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>班德尔城</title>
<subtitle>微笑,简单,奋发</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="https://tiimor.cn/"/>
<updated>2019-10-07T09:47:37.933Z</updated>
<id>https://tiimor.cn/</id>
<author>
<name>Tiimor</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>Git-subtree&submodule拆分与管理</title>
<link href="https://tiimor.cn/Git-subtree&submodule%E6%8B%86%E5%88%86%E4%B8%8E%E7%AE%A1%E7%90%86/"/>
<id>https://tiimor.cn/Git-subtree&submodule拆分与管理/</id>
<published>2019-04-21T10:00:02.000Z</published>
<updated>2019-10-07T09:47:37.933Z</updated>
<content type="html"><![CDATA[<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8D%E4%B8%8D%E7%95%99%E6%B1%A1%E7%82%B9%EF%BC%8C%E9%97%AE%E5%BF%83%E6%97%A0%E6%84%A7.jpg" alt="Android" title="不留污点,问心无愧"></p><p><strong>随着业务需求越来越庞大,Git仓库包含很多可以独立拆分的Module,因此拆分出独立的模块出来,将子目录作为一个新的仓库,并且保留Log记录,同时方便其他工程调用</strong></p><a id="more"></a><h1 id="仓库拆分"><a href="#仓库拆分" class="headerlink" title="仓库拆分"></a>仓库拆分</h1><h2 id="手动强拆"><a href="#手动强拆" class="headerlink" title="手动强拆"></a>手动强拆</h2><p>直接copy一份目标目录代码,非常简单,但是会导致Log记录缺失。</p><h2 id="git-filter-branch"><a href="#git-filter-branch" class="headerlink" title="git filter-branch"></a>git filter-branch</h2><ul><li><p>clone一份原仓库,并且删除remote</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">></span><span class="bash"> git <span class="built_in">clone</span> <big-repo> <new-repo></span></span><br><span class="line"><span class="meta">></span><span class="bash"> <span class="built_in">cd</span> <new-repo></span></span><br><span class="line"><span class="meta">></span><span class="bash"> git remote rm origin</span></span><br></pre></td></tr></tbody></table></figure></li><li><p>拆分</p><figure class="highlight vim"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">// 会带着<span class="keyword">tag</span>记录</span><br><span class="line">> git <span class="built_in">filter</span>-branch --<span class="keyword">tag</span>-name-<span class="built_in">filter</span> <span class="keyword">cat</span> --prune-<span class="built_in">empty</span> --subdirectory-<span class="built_in">filter</span> <span class="symbol"><name-of-folder></span> -- --<span class="keyword">all</span></span><br><span class="line">// 没有带<span class="keyword">tag</span></span><br><span class="line">> git <span class="built_in">filter</span>-branch -<span class="keyword">f</span> --prune-<span class="built_in">empty</span> --subdirectory-<span class="built_in">filter</span> <span class="symbol"><name-of-folder></span></span><br></pre></td></tr></tbody></table></figure></li></ul><p>参数说明:<br></p><figure class="highlight vim"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">这条命令同样会过滤所有历史提交,只保留所有对指定子目录有影响的提交,并将该子目录设为该仓库的根目录。这里说明各下个参数的作用:</span><br><span class="line">--<span class="keyword">tag</span>-name-<span class="built_in">filter</span> 该参数控制我们要如何处理旧的 <span class="keyword">tag</span>,<span class="keyword">cat</span> 即表示原样输出;</span><br><span class="line">--prune-<span class="built_in">empty</span> 删除空的(对子目录没有影响的)提交;</span><br><span class="line">--subdirectory-<span class="built_in">filter</span> 指定子目录路径;</span><br><span class="line">-- --<span class="keyword">all</span> 该参数必须跟在 -- 后面,表示对所有分支进行操作。如果你只想保存当前分支,也可以不添加此参数。</span><br></pre></td></tr></tbody></table></figure><p></p><ul><li><p>清理.git<br>当上述命令执行完毕后,就可以看到本地的新仓库已经是原仓库子目录中的内容了,且保留了关于该子目录所有的提交历史。不过只是这样的话新仓库中的<br>.git 目录里还是保存有不少无用的东西,我们需要将其清除掉以减小新仓库的体积(如果你用subtree 的方法的话是不需要执行这一步的)。<br>依次执行以下命令:</p><figure class="highlight jboss-cli"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">> git reset <span class="params">--hard</span></span><br><span class="line">> git for-each-ref <span class="params">--format=</span><span class="string">"%(refname)"</span> refs/original/ |xargs -n 1 git update-ref -d</span><br><span class="line">> git reflog expire <span class="params">--expire=now</span> <span class="params">--all</span></span><br><span class="line">> git gc <span class="params">--aggressive</span> <span class="params">--prune=now</span></span><br></pre></td></tr></tbody></table></figure></li><li><p>推送新仓库到远端</p><figure class="highlight haxe"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">cd到<<span class="keyword">new</span><span class="type"></span>-repo></span><br><span class="line"><span class="comment">// 添加远端地址:</span></span><br><span class="line">> git remote add origin <<span class="keyword">new</span><span class="type"></span>-git-url></span><br><span class="line"><span class="comment">// 推送到远端:</span></span><br><span class="line">> git push -u origin master</span><br></pre></td></tr></tbody></table></figure></li></ul><h2 id="git-subtree"><a href="#git-subtree" class="headerlink" title="git subtree"></a>git subtree</h2><p>git subtree比上面的方法都简单,需要高版本的git支持,1.8</p><ul><li>进入<big-repo> 所在的目录,创建一个<name-of-new-branch>的临时分支<figure class="highlight livecodeserver"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">> git subtree <span class="built_in">split</span> -P <name-<span class="keyword">of</span>-<span class="built_in">folder</span>> -b <name-<span class="keyword">of</span>-<span class="built_in">new</span>-branch></span><br></pre></td></tr></tbody></table></figure></name-of-new-branch></big-repo></li></ul><p><strong>说明:</strong>分离目录<name-of-folder>,把它作为一个名字是<name-of-new-branch>的branch<br><strong>注意:</strong>有时候偶尔会没有拆分,会拉取原仓库所有的log,这个时候就要注意一下,拆分的目录的Log会少一些;不成功多尝试几次</name-of-new-branch></name-of-folder></p><ul><li><p>创建一个新的 git 仓库,用于分离的目录</p><figure class="highlight haxe"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">> mkdir <<span class="keyword">new</span><span class="type"></span>-repo></span><br><span class="line">> cd ../<<span class="keyword">new</span><span class="type"></span>-repo></span><br><span class="line">> git init</span><br></pre></td></tr></tbody></table></figure></li><li><p>拉取原仓库</path/to/big-repo>的临时分支<name-of-new-branch>到新的仓库<new-repo>中 master 分支</new-repo></name-of-new-branch></p><figure class="highlight applescript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">> git pull </path/<span class="keyword">to</span>/big-repo> <<span class="built_in">name</span>-<span class="keyword">of</span>-new-branch></span><br></pre></td></tr></tbody></table></figure></li><li><p>推送到远程</p><figure class="highlight haxe"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">cd到<<span class="keyword">new</span><span class="type"></span>-repo></span><br><span class="line"><span class="comment">// 添加远端地址:</span></span><br><span class="line">> git remote add origin <<span class="keyword">new</span><span class="type"></span>-git-url></span><br><span class="line"><span class="comment">// 推送到远端:</span></span><br><span class="line">> git push -u origin master</span><br></pre></td></tr></tbody></table></figure></li></ul><p><img src="http://cdn.tiimor.cn/images/git-subtree.png" alt="subtree"></p><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8D%E4%B8%8D%E7%95%99%E6%B1%A1%E7%82%B9%EF%BC%8C%E9%97%AE%E5%BF%83%E6%97%A0%E6%84%A7.jpg" alt="Android" title="不留污点,问心无愧"></p>
<p><strong>随着业务需求越来越庞大,Git仓库包含很多可以独立拆分的Module,因此拆分出独立的模块出来,将子目录作为一个新的仓库,并且保留Log记录,同时方便其他工程调用</strong></p>
</summary>
<category term="git" scheme="https://tiimor.cn/tags/git/"/>
</entry>
<entry>
<title>Gradle多渠道代码</title>
<link href="https://tiimor.cn/Gradle%E5%A4%9A%E6%B8%A0%E9%81%93%E4%BB%A3%E7%A0%81/"/>
<id>https://tiimor.cn/Gradle多渠道代码/</id>
<published>2019-03-17T06:48:47.000Z</published>
<updated>2019-03-24T04:39:00.858Z</updated>
<content type="html"><![CDATA[<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8D%E9%AD%94%E5%B9%BB.jpg" alt="Android" title="魔幻"></p><p><strong>一套代码衍生多套代码,打包不同的APK</strong></p><a id="more"></a><p>当遇到需求,一套代码根据不同的客户打包成不同的logo或者不同的UI风格时,Android Studio中搭配gradle,使用productFlavors可以很好解决。<br>buildType也可以,但是一般处理的是编译配置。</p><p>常见的几种情况:</p><ul><li>资源文件assets、res,替换logo、图片、strings</li><li>AndroidManifest文件</li><li>java类文件</li></ul><p><strong>注意:</strong>资源文件、AndroidManifest文件在编译都可以使用main目录里的资源,因为他们是合并、替换或者覆盖;但是java文件必须在每个flavor中存在,main目录则不能存有,会报错,因为java类只能是唯一。</p><p>异常<br></p><figure class="highlight stylus"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1</span><span class="selector-class">.All</span> flavors must now belong to <span class="selector-tag">a</span> named flavor dimension. Learn more at https:<span class="comment">//d.android.com/r/tools/flavorDimensions-missing-error-message.html</span></span><br><span class="line">解决方法:defaultConfig{}加入flavorDimensions <span class="string">"default"</span></span><br><span class="line"></span><br><span class="line"><span class="number">2</span><span class="selector-class">.Duplicate</span> class</span><br><span class="line">解决方法:去掉main中重复的java类</span><br></pre></td></tr></tbody></table></figure><p></p><p>示例<br></p><figure class="highlight puppet"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">// 多渠道</span><br><span class="line"><span class="keyword">productFlavors</span> {</span><br><span class="line"> <span class="literal">system</span> {</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">normal</span> {</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">// 搭配productFlavors,指定资源</span><br><span class="line"><span class="keyword">sourceSets</span> {</span><br><span class="line"> <span class="literal">system</span> {</span><br><span class="line"> <span class="literal">manifest</span>.srcFile <span class="string">'src/main/AndroidManifest_system.xml'</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">normal</span> {</span><br><span class="line"> <span class="literal">manifest</span>.srcFile <span class="string">'src/main/AndroidManifest.xml'</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p></p><h1 id="资源"><a href="#资源" class="headerlink" title="资源"></a>资源</h1><h2 id="简单的资源替代"><a href="#简单的资源替代" class="headerlink" title="简单的资源替代"></a>简单的资源替代</h2><p>替换字符串,在生成的BuildConfig类中可以找到。<strong>这种方法只适用少数的替换</strong>。<br></p><figure class="highlight less"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">productFlavors</span> {</span><br><span class="line"> <span class="selector-tag">A</span> {</span><br><span class="line"> <span class="comment">// 替换字符串变量</span></span><br><span class="line"> <span class="selector-tag">buildConfigField</span>(<span class="string">"String"</span>, <span class="string">"name"</span>, <span class="string">"A"</span>)</span><br><span class="line"> <span class="comment">// 替换资源字符串:R.string.type</span></span><br><span class="line"> <span class="selector-tag">resValue</span>(<span class="string">"string"</span>, <span class="string">"type"</span>, <span class="string">"A"</span>)</span><br><span class="line"> }</span><br><span class="line"> <span class="selector-tag">B</span> {</span><br><span class="line"> <span class="selector-tag">buildConfigField</span>(<span class="string">"String"</span>, <span class="string">"name"</span>, <span class="string">"B"</span>)</span><br><span class="line"> <span class="selector-tag">resValue</span>(<span class="string">"string"</span>, <span class="string">"type"</span>, <span class="string">"B"</span>)</span><br><span class="line"> }</span><br><span class="line"> <span class="selector-tag">C</span> {</span><br><span class="line"> <span class="selector-tag">buildConfigField</span>(<span class="string">"String"</span>, <span class="string">"name"</span>, <span class="string">"C"</span>)</span><br><span class="line"> <span class="selector-tag">resValue</span>(<span class="string">"string"</span>, <span class="string">"type"</span>, <span class="string">"C"</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p></p><h2 id="多渠道资源替换"><a href="#多渠道资源替换" class="headerlink" title="多渠道资源替换"></a>多渠道资源替换</h2><p>创建多个flavor时,指向对应的资源目录:该资源目录在编译时,会与main里面的资源目录进行合并或者替换。</p><ul><li><p>合并<br>如strings.xml,先合并main和当前flavor文件里资源;遇到id名相同时,则会用flavor的资源替换带main里面的资源。</p></li><li><p>替换<br>如assets、图片名字相同时,会被flavor的资源文件替换。</p></li></ul><figure class="highlight stylus"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">productFlavors {</span><br><span class="line"> A {</span><br><span class="line"> }</span><br><span class="line"> B {</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 两种写法,可以指定main里面的资源路径</span></span><br><span class="line">sourceSets{</span><br><span class="line"> A<span class="selector-class">.res</span><span class="selector-class">.srcDirs</span>=[<span class="string">'src/main/res-a'</span>]</span><br><span class="line"> B<span class="selector-class">.res</span><span class="selector-class">.srcDirs</span>=[<span class="string">'src/main/res-b'</span>]</span><br><span class="line"> C<span class="selector-class">.res</span><span class="selector-class">.srcDirs</span>=[<span class="string">'src/main/res-c'</span>]</span><br><span class="line">}</span><br><span class="line">sourceSets {</span><br><span class="line"> A {</span><br><span class="line"> res<span class="selector-class">.srcDirs</span> = [<span class="string">'src/main/res-a'</span>]</span><br><span class="line"> }</span><br><span class="line"> B {</span><br><span class="line"> res<span class="selector-class">.srcDirs</span> = [<span class="string">'src/main/res-b'</span>]</span><br><span class="line"> }</span><br><span class="line"> C {</span><br><span class="line"> manifest<span class="selector-class">.srcFile</span> <span class="string">'src/main/AndroidManifest_c.xml'</span></span><br><span class="line"> res<span class="selector-class">.srcDirs</span> = [<span class="string">'src/main/res-c'</span>]</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>或者创建与main同级的目录,表示一个flavor<br><img src="http://cdn.tiimor.cn/images/gradle%E5%A4%9A%E6%B8%A0%E9%81%93%E8%B5%84%E6%BA%90.png" alt="gradle多渠道资源"></p><h1 id="AndroidManifest"><a href="#AndroidManifest" class="headerlink" title="AndroidManifest"></a>AndroidManifest</h1><p>该文件多渠道与资源文件多渠道有些类似,但是它是合并节点的形式。</p><p>默认的main里面有一个AndroidManifest文件,编译时的flavor也存在另外一个AndroidManifest,则会合并,根据节点<strong>tools:node</strong>配置,判断是否覆盖(replace)或者是合并(merge)等等,默认是merge。<br></p><figure class="highlight xml"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">activity</span> <span class="attr">android:name</span>=<span class="string">".MainActivity"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">tools:node</span>=<span class="string">"merge"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">intent-filter</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">action</span> <span class="attr">android:name</span>=<span class="string">"android.intent.action.MAIN"</span>/></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">category</span> <span class="attr">android:name</span>=<span class="string">"android.intent.category.LAUNCHER"</span>/></span></span><br><span class="line"> <span class="tag"></<span class="name">intent-filter</span>></span></span><br><span class="line"><span class="tag"></<span class="name">activity</span>></span></span><br></pre></td></tr></tbody></table></figure><p></p><p>有时多个AndroidManifest时,修改flavor,但是没有达到效果,则要查看是否是<strong>tools:node</strong>配置的原因。</p><h1 id="java文件"><a href="#java文件" class="headerlink" title="java文件"></a>java文件</h1><p>java文件比较特殊,如果想对文件A.java进行多渠道,需要去掉main里面的文件的A.java,然后在其他所有的flavor里加入A.java。这样flavor多的时候,修改A中公共的代码部分比较麻烦,目前还没好的办法。</p><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8D%E9%AD%94%E5%B9%BB.jpg" alt="Android" title="魔幻"></p>
<p><strong>一套代码衍生多套代码,打包不同的APK</strong></p>
</summary>
<category term="Android联盟" scheme="https://tiimor.cn/categories/Android%E8%81%94%E7%9B%9F/"/>
<category term="Gradle" scheme="https://tiimor.cn/tags/Gradle/"/>
<category term="多渠道" scheme="https://tiimor.cn/tags/%E5%A4%9A%E6%B8%A0%E9%81%93/"/>
</entry>
<entry>
<title>Retrofit封装</title>
<link href="https://tiimor.cn/Retrofit%E5%B0%81%E8%A3%85/"/>
<id>https://tiimor.cn/Retrofit封装/</id>
<published>2018-12-22T05:25:13.000Z</published>
<updated>2019-03-17T06:47:42.382Z</updated>
<content type="html"><![CDATA[<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8D%E6%88%91%E8%82%9A%E5%AD%90%E9%87%8C%E6%9C%89%E4%B8%AA%E5%B0%8Fandroid.jpg" alt="Android" title="我肚子里有个小android"></p><p><strong>封装Retrofit,统一管理网络请求</strong></p><a id="more"></a><p><strong><a href="https://github.com/VeiZhang/RetrofitClient" title="RetrofitClient" target="_blank" rel="noopener">传送门</a></strong></p><h2 id="实现功能"><a href="#实现功能" class="headerlink" title="实现功能"></a>实现功能</h2><ul><li>get请求封装</li><li>请求头和参数统一配置,分开配置</li><li>异步统一回调接口</li><li>单个请求、单个界面请求、所有请求取消</li><li>缓存策略:在线缓存、离线缓存</li><li>下载</li><li>上传</li></ul><h2 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h2><h3 id="引用"><a href="#引用" class="headerlink" title="引用"></a>引用</h3><figure class="highlight gradle"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">compile</span> <span class="string">'com.excellence:retrofit:_latestVersion'</span></span><br></pre></td></tr></tbody></table></figure><h3 id="权限"><a href="#权限" class="headerlink" title="权限"></a>权限</h3><figure class="highlight routeros"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><uses-permission android:<span class="attribute">name</span>=<span class="string">"android.permission.INTERNET"</span>/></span><br><span class="line"><uses-permission android:<span class="attribute">name</span>=<span class="string">"android.permission.ACCESS_NETWORK_STATE"</span>/></span><br><span class="line"><uses-permission android:<span class="attribute">name</span>=<span class="string">"android.permission.WRITE_EXTERNAL_STORAGE"</span>/></span><br></pre></td></tr></tbody></table></figure><h3 id="网络请求"><a href="#网络请求" class="headerlink" title="网络请求"></a>网络请求</h3><ul><li><p>初始化<br>可以在Application中初始化</p><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 默认支持:ScalarsConverterFactory、RxJavaCallAdapterFactory</span></span><br><span class="line"><span class="comment">// addLog : 是否开启Log打印</span></span><br><span class="line"><span class="comment">// cacheEnable : 是否开启缓存,默认不开启:开启后,默认每个请求都缓存</span></span><br><span class="line"><span class="comment">// 单个请求的缓存控制,可设置HttpRequest.Builder#cacheEnable</span></span><br><span class="line"><span class="comment">// cache : 自定义缓存目录,设置缓存目录后,缓存自动开启,无需设置cacheEnable</span></span><br><span class="line">···</span><br><span class="line"><span class="keyword">new</span> RetrofitUtils.Builder(<span class="keyword">this</span>).baseUrl(BASE_URL).addLog(<span class="keyword">true</span>).cacheEnable(<span class="keyword">true</span>).build();</span><br></pre></td></tr></tbody></table></figure></li><li><p>创建请求</p><ul><li><p>GET</p><figure class="highlight css"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">new</span> <span class="selector-tag">HttpRequest</span><span class="selector-class">.Builder</span>()<span class="selector-class">.tag</span>(<span class="selector-tag">this</span>)<span class="selector-class">.url</span>(<span class="selector-tag">REQUEST_URL</span>)<span class="selector-class">.build</span>()<span class="selector-class">.get</span>();</span><br></pre></td></tr></tbody></table></figure></li><li><p>POST</p><figure class="highlight css"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">new</span> <span class="selector-tag">HttpRequest</span><span class="selector-class">.Builder</span>()<span class="selector-class">.tag</span>(<span class="selector-tag">this</span>)<span class="selector-class">.url</span>(<span class="selector-tag">REQUEST_URL</span>)<span class="selector-class">.build</span>()<span class="selector-class">.postForm</span>();</span><br></pre></td></tr></tbody></table></figure></li><li><p>下载</p><figure class="highlight css"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">new</span> <span class="selector-tag">HttpRequest</span><span class="selector-class">.Builder</span>()<span class="selector-class">.tag</span>(<span class="selector-tag">this</span>)<span class="selector-class">.url</span>(<span class="selector-tag">REQUEST_URL</span>)<span class="selector-class">.build</span>()<span class="selector-class">.download</span>();</span><br></pre></td></tr></tbody></table></figure></li><li><p>上传</p><figure class="highlight css"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">new</span> <span class="selector-tag">HttpRequest</span><span class="selector-class">.Builder</span>()<span class="selector-class">.tag</span>(<span class="selector-tag">this</span>)<span class="selector-class">.url</span>(<span class="selector-tag">REQUEST_URL</span>)<span class="selector-class">.build</span>()<span class="selector-class">.uploadFile</span>();</span><br></pre></td></tr></tbody></table></figure></li></ul></li></ul><h3 id="使用注意"><a href="#使用注意" class="headerlink" title="使用注意"></a>使用注意</h3><ul><li><p>缓存默认位置<code>/sdcard/Android/data/YourPackageName/cache/</code>,可以初始化时自定义</p></li><li><p>如果想针对单个请求,不使用缓存,可以考虑添加头信息<code>mHeaders.put(DOWNLOAD, DOWNLOAD)</code>,当做下载请求,则不会使用缓存机制</p></li></ul><h3 id="混淆"><a href="#混淆" class="headerlink" title="混淆"></a>混淆</h3><figure class="highlight haml"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">###-----------保持 retrofit client 不被混淆------------</span><br><span class="line">-<span class="ruby">keep <span class="class"><span class="keyword">class</span> <span class="title">com</span>.<span class="title">excellence</span>.<span class="title">retrofit</span>.<span class="title">RetrofitHttpService</span> { *;</span> }</span></span><br><span class="line"><span class="ruby"></span></span><br><span class="line"><span class="ruby"><span class="comment">###-----------保持 retrofit 不被混淆------------</span></span></span><br><span class="line"><span class="ruby">-dontwarn retrofit2.**</span></span><br><span class="line"><span class="ruby">-keep <span class="class"><span class="keyword">class</span> <span class="title">retrofit2</span>.** { *;</span> }</span></span><br><span class="line"><span class="ruby">-dontwarn javax.annotation.**</span></span><br><span class="line"><span class="ruby"></span></span><br><span class="line"><span class="ruby"><span class="comment">###-----------保持 okhttp 不被混淆------------</span></span></span><br><span class="line"><span class="ruby">-dontwarn com.squareup.okhttp3.**</span></span><br><span class="line"><span class="ruby">-keep <span class="class"><span class="keyword">class</span> <span class="title">com</span>.<span class="title">squareup</span>.<span class="title">okhttp3</span>.** { *;</span>}</span></span><br><span class="line"><span class="ruby">-dontwarn okio.**</span></span><br></pre></td></tr></tbody></table></figure><h2 id="版本更新"><a href="#版本更新" class="headerlink" title="版本更新"></a>版本更新</h2><table><thead><tr><th>版本</th><th>描述</th></tr></thead><tbody><tr><td><a href="https://bintray.com/veizhang/maven/retrofit/1.0.5" target="_blank" rel="noopener">1.0.5</a></td><td>判断请求队列是否存在 <strong>2018-10-30</strong></td></tr><tr><td><a href="https://bintray.com/veizhang/maven/retrofit/1.0.4" target="_blank" rel="noopener">1.0.4</a></td><td>可自定义OkHttp,开放请求队列的增加与删除 <strong>2018-10-25</strong></td></tr><tr><td><a href="https://bintray.com/veizhang/maven/retrofit/1.0.3" target="_blank" rel="noopener">1.0.3</a></td><td>开放retrofit对象,可自定义创建请求Service <strong>2018-8-1</strong></td></tr><tr><td><a href="https://bintray.com/veizhang/maven/retrofit/1.0.2" target="_blank" rel="noopener">1.0.2</a></td><td>分离下载封装,优化请求接口 <strong>2018-7-5</strong></td></tr><tr><td><a href="https://bintray.com/veizhang/maven/retrofit/1.0.1" target="_blank" rel="noopener">1.0.1</a></td><td>优化请求和新增异常处理 <strong>2018-3-13</strong></td></tr><tr><td><a href="https://bintray.com/veizhang/maven/retrofit/1.0.0" target="_blank" rel="noopener">1.0.0</a></td><td>创建网络请求:GET、POST、下载、上传 <strong>2017-11-14</strong></td></tr></tbody></table><!-- 网站链接 --><!-- 图片链接 --><!-- 版本 --><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8D%E6%88%91%E8%82%9A%E5%AD%90%E9%87%8C%E6%9C%89%E4%B8%AA%E5%B0%8Fandroid.jpg" alt="Android" title="我肚子里有个小android"></p>
<p><strong>封装Retrofit,统一管理网络请求</strong></p>
</summary>
<category term="Android联盟" scheme="https://tiimor.cn/categories/Android%E8%81%94%E7%9B%9F/"/>
<category term="Retrofit" scheme="https://tiimor.cn/tags/Retrofit/"/>
<category term="网络请求" scheme="https://tiimor.cn/tags/%E7%BD%91%E7%BB%9C%E8%AF%B7%E6%B1%82/"/>
</entry>
<entry>
<title>Android图片加载策略-ImageLoader</title>
<link href="https://tiimor.cn/Android%E5%9B%BE%E7%89%87%E5%8A%A0%E8%BD%BD%E7%AD%96%E7%95%A5-ImageLoader/"/>
<id>https://tiimor.cn/Android图片加载策略-ImageLoader/</id>
<published>2018-12-22T05:24:38.000Z</published>
<updated>2019-03-17T06:47:42.362Z</updated>
<content type="html"><![CDATA[<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8D%E6%88%91%E5%B0%B1%E6%98%AF%E6%88%91%EF%BC%8C%E4%B8%8D%E4%B8%80%E6%A0%B7%E7%9A%84%E6%88%91.jpg" alt="Android" title="我就是我,不一样的我"></p><p><strong>封装图片加载,任意切换图片库</strong></p><a id="more"></a><p><strong><a href="https://github.com/VeiZhang/ImageLoader" title="ImageLoader" target="_blank" rel="noopener">传送门</a></strong></p><h2 id="统一管理图片加载库,随意切换图片加载框架"><a href="#统一管理图片加载库,随意切换图片加载框架" class="headerlink" title="统一管理图片加载库,随意切换图片加载框架"></a>统一管理图片加载库,随意切换图片加载框架</h2><ul><li>Fresco</li><li>Picasso</li><li>Glide</li><li>Universal-ImageLoader</li><li>Volley</li></ul><h2 id="封装"><a href="#封装" class="headerlink" title="封装"></a>封装</h2><ul><li>多个图片加载库切换</li><li>图片加载进度回调</li><li>自定义配置(如占位图片、错误占位图片、缓存目录、大小等)</li><li>清除缓存</li></ul><h2 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h2><ul><li><p><strong>独立依赖库</strong></p> <figure class="highlight clean"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">implementation</span> <span class="string">'com.excellence:imageloader:_latestVersion'</span></span><br><span class="line"><span class="comment">// 下面图库三选一,减小安装包大小</span></span><br><span class="line"><span class="keyword">implementation</span> <span class="string">'com.excellence:imageloader-fresco:_latestVersion'</span></span><br><span class="line"><span class="keyword">implementation</span> <span class="string">'com.excellence:imageloader-picasso:_latestVersion'</span></span><br><span class="line"><span class="keyword">implementation</span> <span class="string">'com.excellence:imageloader-glide:_latestVersion'</span></span><br></pre></td></tr></tbody></table></figure></li><li><p>权限</p> <figure class="highlight routeros"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><uses-permission android:<span class="attribute">name</span>=<span class="string">"android.permission.INTERNET"</span>/></span><br><span class="line"><uses-permission android:<span class="attribute">name</span>=<span class="string">"android.permission.ACCESS_NETWORK_STATE"</span>/></span><br><span class="line"><uses-permission android:<span class="attribute">name</span>=<span class="string">"android.permission.READ_EXTERNAL_STORAGE"</span>/></span><br><span class="line"><uses-permission android:<span class="attribute">name</span>=<span class="string">"android.permission.WRITE_EXTERNAL_STORAGE"</span>/></span><br></pre></td></tr></tbody></table></figure></li><li><p>API</p> <figure class="highlight groovy"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 初始化,不同的加载器,有部分独立的方法</span></span><br><span class="line"><span class="comment">// 可以自定义实现ImageLoader接口,创建新的图库加载器</span></span><br><span class="line">ImageLoaderOptions options = <span class="keyword">new</span> ImageLoaderOptions.Builder().isLogEnable(<span class="literal">true</span>).isCache(<span class="literal">false</span>).build();</span><br><span class="line">mImageLoader = <span class="keyword">new</span> FrescoImageLoader(<span class="keyword">this</span>, options);</span><br><span class="line">mImageLoader = <span class="keyword">new</span> PicassoImageLoader(<span class="keyword">this</span>, options);</span><br><span class="line">mImageLoader = <span class="keyword">new</span> GlideImageLoader(<span class="keyword">this</span>, options);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 统一的接口</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">ImageLoader</span></span></span><br><span class="line"><span class="class">{</span></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 加载资源图片</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> view</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> resId</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">void</span> loadImage(<span class="meta">@NonNull</span> ImageView view, <span class="meta">@DrawableRes</span> <span class="keyword">int</span> resId);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">void</span> loadImage(<span class="meta">@NonNull</span> ImageView view, <span class="meta">@DrawableRes</span> <span class="keyword">int</span> resId, IListener listener);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 加载资源图片,占位图片,错误图片</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> view</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> resId</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> placeholderResId</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> errorResId</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">void</span> loadImage(<span class="meta">@NonNull</span> ImageView view, <span class="meta">@DrawableRes</span> <span class="keyword">int</span> resId, <span class="meta">@DrawableRes</span> <span class="keyword">int</span> placeholderResId, <span class="meta">@DrawableRes</span> <span class="keyword">int</span> errorResId);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">void</span> loadImage(<span class="meta">@NonNull</span> ImageView view, <span class="meta">@DrawableRes</span> <span class="keyword">int</span> resId, <span class="meta">@DrawableRes</span> <span class="keyword">int</span> placeholderResId, <span class="meta">@DrawableRes</span> <span class="keyword">int</span> errorResId, IListener listener);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 加载本地图片</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> view</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> file</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">void</span> loadImage(<span class="meta">@NonNull</span> ImageView view, <span class="meta">@NonNull</span> File file);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">void</span> loadImage(<span class="meta">@NonNull</span> ImageView view, <span class="meta">@NonNull</span> File file, IListener listener);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 加载本地图片,占位图片,错误图片</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> view</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> file</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> placeholderResId</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> errorResId</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">void</span> loadImage(<span class="meta">@NonNull</span> ImageView view, <span class="meta">@NonNull</span> File file, <span class="meta">@DrawableRes</span> <span class="keyword">int</span> placeholderResId, <span class="meta">@DrawableRes</span> <span class="keyword">int</span> errorResId);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">void</span> loadImage(<span class="meta">@NonNull</span> ImageView view, <span class="meta">@NonNull</span> File file, <span class="meta">@DrawableRes</span> <span class="keyword">int</span> placeholderResId, <span class="meta">@DrawableRes</span> <span class="keyword">int</span> errorResId, IListener listener);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 加载网络图片</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> view</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> url</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">void</span> loadImage(<span class="meta">@NonNull</span> ImageView view, <span class="meta">@NonNull</span> String url);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">void</span> loadImage(<span class="meta">@NonNull</span> ImageView view, <span class="meta">@NonNull</span> String url, IListener listener);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 加载网络图片,占位图片,错误图片</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> view</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> url</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> placeholderResId</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> errorResId</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">void</span> loadImage(<span class="meta">@NonNull</span> ImageView view, <span class="meta">@NonNull</span> String url, <span class="meta">@DrawableRes</span> <span class="keyword">int</span> placeholderResId, <span class="meta">@DrawableRes</span> <span class="keyword">int</span> errorResId);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">void</span> loadImage(<span class="meta">@NonNull</span> ImageView view, <span class="meta">@NonNull</span> String url, <span class="meta">@DrawableRes</span> <span class="keyword">int</span> placeholderResId, <span class="meta">@DrawableRes</span> <span class="keyword">int</span> errorResId, IListener listener);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">void</span> clearCache();</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><h2 id="Fresco"><a href="#Fresco" class="headerlink" title="Fresco"></a><a href="https://github.com/facebook/fresco/" target="_blank" rel="noopener">Fresco</a></h2><figure class="highlight gradle"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">implementation <span class="string">'com.facebook.fresco:fresco:1.9.0'</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 在 API < 14 上的机器支持 WebP 时,需要添加</span></span><br><span class="line"><span class="keyword">compile</span> <span class="string">'com.facebook.fresco:animated-base-support:0.12.0'</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 支持 GIF 动图,需要添加</span></span><br><span class="line"><span class="keyword">compile</span> <span class="string">'com.facebook.fresco:animated-gif:0.12.0'</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 支持 WebP (静态图+动图),需要添加</span></span><br><span class="line"><span class="keyword">compile</span> <span class="string">'com.facebook.fresco:animated-webp:0.12.0'</span></span><br><span class="line"><span class="keyword">compile</span> <span class="string">'com.facebook.fresco:webpsupport:0.12.0'</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 仅支持 WebP 静态图,需要添加</span></span><br><span class="line"><span class="keyword">compile</span> <span class="string">'com.facebook.fresco:webpsupport:0.12.0'</span></span><br></pre></td></tr></tbody></table></figure><figure class="highlight abnf"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Uri uri = Uri.parse(<span class="string">"https://raw.githubusercontent.com/facebook/fresco/gh-pages/static/logo.png"</span>)<span class="comment">;</span></span><br><span class="line">SimpleDraweeView draweeView = (SimpleDraweeView) findViewById(R.id.my_image_view)<span class="comment">;</span></span><br><span class="line">draweeView.setImageURI(uri)<span class="comment">;</span></span><br></pre></td></tr></tbody></table></figure><h2 id="Picasso"><a href="#Picasso" class="headerlink" title="Picasso"></a><a href="https://github.com/square/picasso" target="_blank" rel="noopener">Picasso</a></h2><figure class="highlight 1c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">implementation 'com.squareup.picasso:picasso:2.<span class="number">7182</span>8'</span><br></pre></td></tr></tbody></table></figure><figure class="highlight css"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">Picasso</span><span class="selector-class">.get</span>()</span><br><span class="line"> <span class="selector-class">.load</span>(<span class="selector-tag">url</span>)</span><br><span class="line"> <span class="selector-class">.resize</span>(50, 50)</span><br><span class="line"> <span class="selector-class">.centerCrop</span>()</span><br><span class="line"> <span class="selector-class">.into</span>(<span class="selector-tag">imageView</span>)</span><br></pre></td></tr></tbody></table></figure><p>缓存路径:<code>data/data/your package name/cache/picasso-cache/(默认路径)</code></p><h2 id="Glide"><a href="#Glide" class="headerlink" title="Glide"></a><a href="https://github.com/bumptech/glide/" target="_blank" rel="noopener">Glide</a></h2><figure class="highlight clean"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">implementation</span> <span class="string">'com.github.bumptech.glide:glide:4.8.0'</span></span><br><span class="line">annotationProcessor <span class="string">'com.github.bumptech.glide:compiler:4.8.0'</span></span><br></pre></td></tr></tbody></table></figure><figure class="highlight css"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">Glide</span><span class="selector-class">.with</span>(<span class="selector-tag">getContext</span>())</span><br><span class="line"> <span class="selector-class">.load</span>(<span class="selector-tag">url</span>)</span><br><span class="line"> <span class="selector-class">.skipMemoryCache</span>(<span class="selector-tag">true</span>)</span><br><span class="line"> <span class="selector-class">.placeholder</span>(<span class="selector-tag">drawable</span>)</span><br><span class="line"> <span class="selector-class">.centerCrop</span>()</span><br><span class="line"> <span class="selector-class">.animate</span>(<span class="selector-tag">animator</span>)</span><br><span class="line"> <span class="selector-class">.into</span>(<span class="selector-tag">img</span>);</span><br></pre></td></tr></tbody></table></figure><h2 id="Universal-ImageLoader"><a href="#Universal-ImageLoader" class="headerlink" title="Universal-ImageLoader"></a><a href="https://github.com/nostra13/Android-Universal-Image-Loader" target="_blank" rel="noopener">Universal-ImageLoader</a></h2><h2 id="Volley"><a href="#Volley" class="headerlink" title="Volley"></a><a href="https://github.com/google/volley/" target="_blank" rel="noopener">Volley</a></h2><h2 id="版本更新"><a href="#版本更新" class="headerlink" title="版本更新"></a>版本更新</h2><table><thead><tr><th>版本</th><th>描述</th></tr></thead><tbody><tr><td><a href="https://bintray.com/veizhang/maven/imageloader/1.0.0" target="_blank" rel="noopener">1.0.0</a></td><td>封装Fresco、Picasso、Glide图库,简单加载图片 <strong>2018-10-11</strong></td></tr></tbody></table><h2 id="感谢"><a href="#感谢" class="headerlink" title="感谢"></a>感谢</h2><blockquote><ul><li><a href="https://github.com/ladingwu/ImageLoaderFramework" title="图库封装" target="_blank" rel="noopener">ladingwu</a></li><li><a href="https://github.com/hpdx/fresco-helper" title="Fresco封装" target="_blank" rel="noopener">hpdx</a></li><li><a href="https://github.com/peng8350/LoadingProgress" title="图库加载进度" target="_blank" rel="noopener">peng8350</a></li></ul></blockquote><!-- 引用网站链接 --><!-- 图片链接 --><!-- 版本 --><!-- 大神引用 --><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8D%E6%88%91%E5%B0%B1%E6%98%AF%E6%88%91%EF%BC%8C%E4%B8%8D%E4%B8%80%E6%A0%B7%E7%9A%84%E6%88%91.jpg" alt="Android" title="我就是我,不一样的我"></p>
<p><strong>封装图片加载,任意切换图片库</strong></p>
</summary>
<category term="Android联盟" scheme="https://tiimor.cn/categories/Android%E8%81%94%E7%9B%9F/"/>
<category term="ImageLoader" scheme="https://tiimor.cn/tags/ImageLoader/"/>
</entry>
<entry>
<title>RxJava+Retrofit常用的使用场景</title>
<link href="https://tiimor.cn/RxJava-Retrofit%E5%B8%B8%E7%94%A8%E7%9A%84%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF/"/>
<id>https://tiimor.cn/RxJava-Retrofit常用的使用场景/</id>
<published>2018-12-22T05:18:56.000Z</published>
<updated>2019-03-17T06:47:42.383Z</updated>
<content type="html"><![CDATA[<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8Dgoodbye%20my%20love.jpg" alt="Android" title="goodbye my love"></p><p><strong>记录RxJava + Retrofit一些常用的使用场景</strong></p><a id="more"></a><h1 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h1><p><strong>RxJava是非常强大的函数响应式编程库,Retrofit是非常强大的网络请求框架。强强结合,最佳结合体验,让代码更丝滑。</strong></p><p>RxJava的强大,不仅仅是结合网络请求,在其他的场景,如按钮防抖动、定时器、数据库异步操作等等,应用也非常广泛,<strong>强烈建议学习使用</strong>。</p><h1 id="使用场景"><a href="#使用场景" class="headerlink" title="使用场景"></a>使用场景</h1><h2 id="轮询请求"><a href="#轮询请求" class="headerlink" title="轮询请求"></a>轮询请求</h2><ul><li><p>定时轮询</p><figure class="highlight typescript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 每隔5分钟请求Token</span></span><br><span class="line">Observable.interval(<span class="number">5</span>, TimeUnit.MINUTES).flatMap(<span class="keyword">new</span> Func1<<span class="built_in">Object</span>, Observable<<span class="built_in">Object</span>>>() {</span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> Observable<<span class="built_in">Object</span>> call(<span class="built_in">Object</span> obj) {</span><br><span class="line"><span class="keyword">return</span> mService.getToken();</span><br><span class="line">}</span><br><span class="line">}).observeOn(AndroidSchedulers.mainThread())</span><br><span class="line"> .subscribeOn(Schedulers.io())</span><br><span class="line"> .subscribe();</span><br></pre></td></tr></tbody></table></figure></li><li><p>条件轮询</p><figure class="highlight typescript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 结果不为空时,则停止,否则继续请求</span></span><br><span class="line">Observable.from(list).flatMap(<span class="keyword">new</span> Func1<<span class="built_in">Object</span>, Observable<<span class="built_in">Object</span>>>() {</span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> Observable<<span class="built_in">Object</span>> call(<span class="built_in">Object</span> obj) {</span><br><span class="line"><span class="keyword">return</span> mService.getToken();</span><br><span class="line">}</span><br><span class="line">}).takeUntil(<span class="keyword">new</span> Func1<Response<<span class="built_in">String</span>>, <span class="built_in">Boolean</span>>() {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="built_in">Boolean</span> call(Response<<span class="built_in">String</span>> result) {</span><br><span class="line"> <span class="keyword">return</span> result.code() == HTTP_OK && isNotEmpty(result);</span><br><span class="line"> }</span><br><span class="line">}).observeOn(AndroidSchedulers.mainThread())</span><br><span class="line"> .subscribeOn(Schedulers.io())</span><br><span class="line"> .subscribe();</span><br></pre></td></tr></tbody></table></figure></li></ul><h2 id="链式请求"><a href="#链式请求" class="headerlink" title="链式请求"></a>链式请求</h2><p>RxJava配合链式请求,就不需要使用嵌套的形势,一层嵌一层在回调里去请求了。<br></p><figure class="highlight typescript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 登陆 -> 请求Token -> 请求分类 -> 请求列表</span></span><br><span class="line">mService.login(mUrl).flatMap(<span class="keyword">new</span> Func1<<span class="built_in">Object</span>, Observable<<span class="built_in">Object</span>>>() {</span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> Observable<<span class="built_in">Object</span>> call(<span class="built_in">Object</span> obj) {</span><br><span class="line"><span class="keyword">return</span> mService.getToken();</span><br><span class="line">}</span><br><span class="line">}).flatMap(<span class="keyword">new</span> Func1<<span class="built_in">Object</span>, Observable<<span class="built_in">Object</span>>>() {</span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> Observable<<span class="built_in">Object</span>> call(<span class="built_in">Object</span> obj) {</span><br><span class="line"><span class="keyword">return</span> mService.getCategory();</span><br><span class="line">}</span><br><span class="line">}).flatMap(<span class="keyword">new</span> Func1<<span class="built_in">Object</span>, Observable<<span class="built_in">Object</span>>>() {</span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> Observable<<span class="built_in">Object</span>> call(<span class="built_in">Object</span> obj) {</span><br><span class="line"><span class="keyword">return</span> mService.getList();</span><br><span class="line">}</span><br><span class="line">}).observeOn(AndroidSchedulers.mainThread())</span><br><span class="line"> .subscribeOn(Schedulers.io())</span><br><span class="line"> .subscribe();</span><br></pre></td></tr></tbody></table></figure><p></p><h2 id="多个请求结果合并"><a href="#多个请求结果合并" class="headerlink" title="多个请求结果合并"></a>多个请求结果合并</h2><figure class="highlight golo"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">Observable</span><Object> observable1 = mServer.getCategory();</span><br><span class="line"><span class="keyword">Observable</span><Object> observable2 = mServer.getChannel();</span><br><span class="line"></span><br><span class="line"><span class="keyword">Observable</span>.zip(observable1, observable2).subscribe();</span><br></pre></td></tr></tbody></table></figure><h2 id="多个请求结果不合并"><a href="#多个请求结果不合并" class="headerlink" title="多个请求结果不合并"></a>多个请求结果不合并</h2><p><strong>该场景会存在一个问题,如果中途某个请求出现异常,则会中断,后续不再请求。暂时没有想到什么办法避免或者是出现异常继续请求。</strong></p><figure class="highlight lasso"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">Observable.from(appInfoList).flatMap(<span class="literal">new</span> Func1<AppLog, Observable<LogInfo>>() {</span><br><span class="line"> @Override</span><br><span class="line"> <span class="keyword">public</span> Observable<LogInfo> call(AppLog appLog) {</span><br><span class="line"> <span class="built_in">Map</span><<span class="built_in">String</span>, <span class="built_in">String</span>> <span class="keyword">params</span> = <span class="literal">new</span> HashMap<>();</span><br><span class="line"> <span class="keyword">return</span> mInstance.mHttpService.registerLog(REGISTER_APP_LOG, <span class="keyword">params</span>);</span><br><span class="line"> }</span><br><span class="line"> }).subscribe();</span><br></pre></td></tr></tbody></table></figure><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8Dgoodbye%20my%20love.jpg" alt="Android" title="goodbye my love"></p>
<p><strong>记录RxJava + Retrofit一些常用的使用场景</strong></p>
</summary>
<category term="Android联盟" scheme="https://tiimor.cn/categories/Android%E8%81%94%E7%9B%9F/"/>
<category term="RxJava" scheme="https://tiimor.cn/tags/RxJava/"/>
<category term="Retrofit" scheme="https://tiimor.cn/tags/Retrofit/"/>
</entry>
<entry>
<title>Android功守道-so库混淆加密</title>
<link href="https://tiimor.cn/Android%E5%8A%9F%E5%AE%88%E9%81%93-so%E5%BA%93%E6%B7%B7%E6%B7%86%E5%8A%A0%E5%AF%86/"/>
<id>https://tiimor.cn/Android功守道-so库混淆加密/</id>
<published>2018-12-22T05:17:04.000Z</published>
<updated>2019-03-17T06:47:42.360Z</updated>
<content type="html"><![CDATA[<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8Dcolorful%20android.jpg" alt="Android" title="colorful android"></p><p><strong>Android NDK开发中加密so库,旨为保护自己的开发成果。</strong></p><a id="more"></a><h1 id="so反编译"><a href="#so反编译" class="headerlink" title="so反编译"></a>so反编译</h1><p>使用<strong><a href="https://baike.baidu.com/item/IDA%20Pro/3175923?fr=aladdin" target="_blank" rel="noopener">IDA</a></strong>工具反编译so文件,破解分析时,很容易查看到so文件里,被Java层调用的函数,以及一些加密算法等等信息,进一步破解应用代码。<br>但是这种是我们不想见到的,我们希望把一些核心功能或者数据加密,如key,这就引入了so文件加固。</p><h1 id="so加固"><a href="#so加固" class="headerlink" title="so加固"></a>so加固</h1><p>原理:JNI_OnLoad方法,当在系统中调用System.loadLibrary函数时,该函数会找到对应的动态库,然后首先试图找到”JNI_OnLoad”函数,如果该函数存在,则调用它。JNI_OnLoad可以和JNIEnv的registerNatives函数结合起来,实现动态的函数替换。</p><ol><li><p>CMakeList开启配置,隐藏符号表<br> 编辑Android Studio工具的CMakeList文件,添加下面两个语句:</p> <figure class="highlight applescript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">set</span>(CMAKE_C_VISIBILITY_PRESET hidden)<span class="comment"># C语言写法</span></span><br><span class="line"><span class="keyword">set</span>(CMAKE_CXX_VISIBILITY_PRESET hidden)<span class="comment"># C++写法</span></span><br></pre></td></tr></tbody></table></figure></li><li><p>注册JNI_OnLoad方法,函数对照表</p><ul><li><p>记录调用C代码的Java文件路径</p><figure class="highlight cpp"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">const</span> <span class="keyword">char</span> *JNI_REG_CLASS = <span class="string">"com/excellence/test/tool/KeyUtils"</span>;</span><br></pre></td></tr></tbody></table></figure></li><li><p>gMethods方法替换<br>将native方法getKey,指向gK,这样反编译就查不到getKey方法。</p><figure class="highlight actionscript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> JNINativeMethod gMethods[] = {</span><br><span class="line"> {<span class="string">"getKey"</span>, <span class="string">"(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"</span>, (<span class="keyword">void</span> *) gK},</span><br><span class="line">};</span><br></pre></td></tr></tbody></table></figure><p>JNINativemethod中结构体的定义:</p><figure class="highlight cpp"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> {</span></span><br><span class="line"><span class="comment">// Java中函数的名字</span></span><br><span class="line"><span class="keyword">const</span> <span class="keyword">char</span>* name;</span><br><span class="line"><span class="comment">// 描述了Java中函数的参数和返回值</span></span><br><span class="line"><span class="keyword">const</span> <span class="keyword">char</span>* signature; </span><br><span class="line"><span class="comment">// fnPtr是函数指针,指向native函数。前面都要接 (void *)</span></span><br><span class="line"><span class="keyword">void</span>* fnPtr; </span><br><span class="line">} JNINativeMethod;</span><br></pre></td></tr></tbody></table></figure></li><li><p>在JNI调用的C文件里注册</p><figure class="highlight cpp"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">int</span> <span class="title">registerNativeMethods</span><span class="params">(JNIEnv *env, <span class="keyword">const</span> <span class="keyword">char</span> *className, </span></span></span><br><span class="line"><span class="function"><span class="params">JNINativeMethod *gMethods, <span class="keyword">int</span> numMethods)</span> </span>{</span><br><span class="line"> jclass clazz;</span><br><span class="line"> clazz = env->FindClass(className);</span><br><span class="line"> <span class="keyword">if</span> (clazz == <span class="literal">NULL</span>) {</span><br><span class="line"> <span class="keyword">return</span> JNI_FALSE;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (env->RegisterNatives(clazz, gMethods, numMethods) < <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">return</span> JNI_FALSE;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> JNI_TRUE;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">int</span> <span class="title">registerNatives</span><span class="params">(JNIEnv *env)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (!registerNativeMethods(env, JNI_REG_CLASS, gMethods,</span><br><span class="line"> <span class="keyword">sizeof</span>(gMethods) / <span class="keyword">sizeof</span>(gMethods[<span class="number">0</span>])))</span><br><span class="line"> <span class="keyword">return</span> JNI_FALSE;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> JNI_TRUE;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function">jint <span class="title">JNI_OnLoad</span><span class="params">(JavaVM *vm, <span class="keyword">void</span> *reserved)</span> </span>{</span><br><span class="line"> JNIEnv *env;</span><br><span class="line"> <span class="keyword">if</span> (vm->GetEnv((<span class="keyword">void</span> **) (&env), JNI_VERSION_1_6) != JNI_OK) {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line"> }</span><br><span class="line"> assert(env != <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//注册</span></span><br><span class="line"> <span class="keyword">if</span> (!registerNatives(env)) {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> JNI_VERSION_1_6;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul></li></ol><p><strong>这样加固了so文件,就可以做到一定的保护作用,使用IDA工具查看,会起到混淆的作用。</strong></p><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8Dcolorful%20android.jpg" alt="Android" title="colorful android"></p>
<p><strong>Android NDK开发中加密so库,旨为保护自己的开发成果。</strong></p>
</summary>
<category term="Android联盟" scheme="https://tiimor.cn/categories/Android%E8%81%94%E7%9B%9F/"/>
<category term="Android" scheme="https://tiimor.cn/tags/Android/"/>
<category term="混淆" scheme="https://tiimor.cn/tags/%E6%B7%B7%E6%B7%86/"/>
</entry>
<entry>
<title>AndroidFFmpeg命令使用</title>
<link href="https://tiimor.cn/AndroidFFmpeg%E5%91%BD%E4%BB%A4%E4%BD%BF%E7%94%A8/"/>
<id>https://tiimor.cn/AndroidFFmpeg命令使用/</id>
<published>2018-09-01T08:18:05.000Z</published>
<updated>2019-03-17T06:47:42.355Z</updated>
<content type="html"><![CDATA[<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8D%E6%B0%B4%E7%9A%84%E4%B8%96%E7%95%8C%EF%BC%8C%E6%88%91%E7%9F%A5%E9%81%93.jpg" alt="Android" title="水的世界,我知道"></p><p><strong>FFmpeg命令在Android中的使用</strong> </p><a id="more"></a><p>项目传送门:<a href="https://github.com/VeiZhang/AndroidFFmpeg" target="_blank" rel="noopener">FFmpeg for Android</a></p><figure class="highlight clean"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">implementation</span> <span class="string">'com.excellence:ffmpeg:_latestVersion'</span></span><br></pre></td></tr></tbody></table></figure><h2 id="AndroidFFmpeg使用"><a href="#AndroidFFmpeg使用" class="headerlink" title="AndroidFFmpeg使用"></a>AndroidFFmpeg使用</h2><figure class="highlight lasso"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 初始化,默认:不限制并发线程数;指令超时10s终止</span></span><br><span class="line">FFmpeg.init(context);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 自定义初始化参数:超时1s终止</span></span><br><span class="line">FFmpeg.init(context, <span class="literal">new</span> CommanderOptions.Builder().setTimeOut(<span class="number">1000</span>).build())</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取FFmpeg工具路径</span></span><br><span class="line">FFmpeg.checkFFmpeg()</span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建执行命令</span></span><br><span class="line">FFmpeg.addTask(cmd, <span class="literal">new</span> IListener() {</span><br><span class="line"> @Override</span><br><span class="line"> <span class="keyword">public</span> <span class="literal">void</span> onPre(<span class="built_in">String</span> command) {</span><br><span class="line"> <span class="keyword">Log</span>.i(<span class="built_in">TAG</span>, <span class="string">"onPre: "</span> + command);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> @Override</span><br><span class="line"> <span class="keyword">public</span> <span class="literal">void</span> onProgress(<span class="built_in">String</span> message) {</span><br><span class="line"> <span class="keyword">Log</span>.i(<span class="built_in">TAG</span>, <span class="string">"onProgress: "</span> + message);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> @Override</span><br><span class="line"> <span class="keyword">public</span> <span class="literal">void</span> onError(Throwable t) {</span><br><span class="line"> t.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> @Override</span><br><span class="line"> <span class="keyword">public</span> <span class="literal">void</span> onSuccess(<span class="built_in">String</span> message) {</span><br><span class="line"> <span class="keyword">Log</span>.i(<span class="built_in">TAG</span>, <span class="string">"onSuccess: "</span> + message);</span><br><span class="line"> }</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="comment">// 终止命令</span></span><br><span class="line">CommandTask.discard()</span><br><span class="line"></span><br><span class="line"><span class="comment">// 终止所有命令</span></span><br><span class="line">FFmpeg.destroy()</span><br></pre></td></tr></tbody></table></figure><h2 id="FFmpeg命令"><a href="#FFmpeg命令" class="headerlink" title="FFmpeg命令"></a>FFmpeg命令</h2><p><a href="http://ffmpeg.org/" target="_blank" rel="noopener">FFMpeg官网</a></p><p><a href="https://ffmpeg.zeranoe.com/builds/" target="_blank" rel="noopener">Windows工具下载</a>,解压后,把FFmpeg加入到环境变量中,可在Windows上使用FFmpeg</p><h3 id="命令选项"><a href="#命令选项" class="headerlink" title="命令选项"></a>命令选项</h3><ul><li><p>选项参数</p><p> 基本语法格式:</p> <figure class="highlight prolog"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ffmpeg [options] [[infile options] -i infile]... {[outfile options] outfile}...</span><br></pre></td></tr></tbody></table></figure></li><li><p>常用选项</p> <figure class="highlight diff"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="deletion">-version: 显示版本信息。</span></span><br><span class="line"></span><br><span class="line"><span class="deletion">-formats: 显示所有有效的格式。</span></span><br><span class="line"></span><br><span class="line"><span class="deletion">-decoders: 显示所有有效解码器。</span></span><br><span class="line"></span><br><span class="line"><span class="deletion">-encoders: 显示所有有效的编码器。</span></span><br><span class="line"></span><br><span class="line"><span class="deletion">-bsfs: 显示有效的数据流滤镜。</span></span><br><span class="line"></span><br><span class="line"><span class="deletion">-pix_fmts: 显示有效的像素格式。</span></span><br></pre></td></tr></tbody></table></figure></li><li><p>主要选项</p> <figure class="highlight less"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">-f</span> <span class="selector-tag">fmt</span>(input|output): 指定输入或者输出文件格式,可依据扩展名自动指定。</span><br><span class="line"></span><br><span class="line"><span class="selector-tag">-i</span> <span class="selector-tag">filename</span>(input): 指定输入文件。</span><br><span class="line"></span><br><span class="line"><span class="selector-tag">-y</span> (global): 默认自动覆盖输出文件。</span><br><span class="line"></span><br><span class="line"><span class="selector-tag">-n</span> (global): 不覆盖输出文件,如果输出文件已存在则立即退出。</span><br><span class="line"></span><br><span class="line"><span class="selector-tag">-t</span> <span class="selector-tag">duration</span>(input|output): 限制输入/输出的时间。</span><br><span class="line"> 如果用于输入选项,就是限定从输入中读取多少时间的数据;</span><br><span class="line"> 如果用于输出选项,则表示写入多少时间数据后就停止。</span><br><span class="line"> <span class="selector-tag">duration</span> 可以是以秒为单位的数值活着 <span class="selector-tag">hh</span><span class="selector-pseudo">:mm</span><span class="selector-pseudo">:ss</span><span class="selector-attr">[.xxx]</span> 格式的时间值。</span><br><span class="line"> <span class="selector-tag">-to</span> 和 <span class="selector-tag">-t</span> 是互斥的,<span class="selector-tag">-t</span> 有更高的优先级。</span><br><span class="line"></span><br><span class="line"><span class="selector-tag">-to</span> <span class="selector-tag">time_stop</span>(output): 写入 <span class="selector-tag">time_stop</span> 时间后就停止。</span><br><span class="line"> <span class="selector-tag">time_stop</span> 可以是以秒为单位的数值或者 <span class="selector-tag">hh</span><span class="selector-pseudo">:mm</span><span class="selector-pseudo">:ss</span><span class="selector-attr">[.xxx]</span> 格式的时间值。</span><br><span class="line"></span><br><span class="line"><span class="selector-tag">-fs</span> <span class="selector-tag">limit_size</span>(output): 设置输出文件大小限制,单位是字节(<span class="selector-tag">bytes</span>)。</span><br><span class="line"></span><br><span class="line"><span class="selector-tag">-ss</span> <span class="selector-tag">time_off</span>(input|output): 指定输入文件或输出文件的开始位置。</span><br></pre></td></tr></tbody></table></figure></li><li><p>视频选项</p> <figure class="highlight lua"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">-vframes number(<span class="built_in">output</span>): 设置输出文件的帧数。</span><br><span class="line"></span><br><span class="line">-r rate(<span class="built_in">input</span>|<span class="built_in">output</span>): 设置帧率(Hz 值)。</span><br><span class="line"></span><br><span class="line">-s size(<span class="built_in">input</span>|<span class="built_in">output</span>): 设置帧的尺寸。数据格式是 WxH,即宽度值x高度值。</span><br><span class="line"></span><br><span class="line">-aspect aspect(<span class="built_in">output</span>): 指定视频的长宽显示比例。格式为浮点数字符串或者 num:den 格式字符串。</span><br><span class="line"> 如<span class="string">"4:3"</span>,<span class="string">"16:9"</span>,<span class="string">"1.333"</span>等。</span><br><span class="line"></span><br><span class="line">-vcodec codec(<span class="built_in">output</span>): 设置视频编码器。</span><br></pre></td></tr></tbody></table></figure></li><li><p>音频选项</p> <figure class="highlight lua"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">-aframes number(<span class="built_in">output</span>): 设置输出文件的帧数。</span><br><span class="line"></span><br><span class="line">-ar rate(<span class="built_in">input</span>|<span class="built_in">output</span>): 设置音频的采样率,单位为 Hz。</span><br><span class="line"></span><br><span class="line">-aq quality(<span class="built_in">output</span>): 设置音频品质(编码指定为 VBR)。</span><br><span class="line"></span><br><span class="line">-ac channels(<span class="built_in">input</span>|<span class="built_in">output</span>): 设置音频通道数。</span><br><span class="line"></span><br><span class="line">-af filtergraph(<span class="built_in">output</span>): 对音频使用 filtergraph 滤镜效果。</span><br></pre></td></tr></tbody></table></figure></li></ul><h3 id="常用命令"><a href="#常用命令" class="headerlink" title="常用命令"></a>常用命令</h3><ul><li><p>获取视频信息</p> <figure class="highlight mipsasm"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ffprobe -v quiet -print_format <span class="keyword">json </span>-<span class="keyword">show_format </span>-<span class="keyword">show_streams </span>inputfile</span><br></pre></td></tr></tbody></table></figure></li><li><p>视频截图</p> <figure class="highlight avrasm"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">ffmpeg -i input_file -<span class="built_in">y</span> -f mjpeg -ss <span class="number">1</span> -t <span class="number">0.001</span> -s widthxheight output_file</span><br><span class="line"></span><br><span class="line"><span class="symbol">i:</span> 源文件</span><br><span class="line"><span class="symbol">y:</span> 覆盖输出文件</span><br><span class="line"><span class="symbol">f:</span> 截图格式</span><br><span class="line"><span class="symbol">ss:</span> 起始位置,单位秒</span><br><span class="line"><span class="symbol">t:</span> 截图时间,单位秒</span><br><span class="line"><span class="symbol">s:</span> 图片宽<span class="built_in">x</span>高</span><br></pre></td></tr></tbody></table></figure></li><li><p>每隔 1 秒截一张图</p> <figure class="highlight matlab"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ffmpeg -<span class="built_in">i</span> input.mp4 -f image2 -vf fps=fps=<span class="number">1</span> out<span class="comment">%d.jpg</span></span><br></pre></td></tr></tbody></table></figure></li><li><p>每隔 20 秒截一张图</p> <figure class="highlight lsl"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ffmpeg -i input.mp4 -f image2 -vf fps=fps=<span class="number">1</span>/<span class="number">20</span> out%d.jpg</span><br></pre></td></tr></tbody></table></figure></li><li><p>将视频的前 30 帧转换成一个 Gif</p> <figure class="highlight stylus"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ffmpeg -<span class="selector-tag">i</span> <span class="selector-tag">input</span><span class="selector-class">.mp4</span> -vframes <span class="number">30</span> -y -f gif output.gif</span><br></pre></td></tr></tbody></table></figure></li><li><p>从视频中生成 Gif</p> <figure class="highlight stylus"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ffmpeg -<span class="selector-tag">i</span> <span class="selector-tag">input</span><span class="selector-class">.mp4</span> -t <span class="number">10</span> -pix_fmt rgb24 output.gif</span><br></pre></td></tr></tbody></table></figure></li><li><p>转换视频为图片(每帧一张图)</p> <figure class="highlight css"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">ffmpeg</span> <span class="selector-tag">-i</span> <span class="selector-tag">input</span><span class="selector-class">.mp4</span> <span class="selector-tag">out</span>%<span class="selector-tag">d</span><span class="selector-class">.jpg</span></span><br></pre></td></tr></tbody></table></figure></li><li><p>图片转换为视频</p> <figure class="highlight css"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">ffmpeg</span> <span class="selector-tag">-f</span> <span class="selector-tag">image2</span> <span class="selector-tag">-i</span> <span class="selector-tag">out</span>%<span class="selector-tag">d</span><span class="selector-class">.jpg</span> <span class="selector-tag">-r</span> 25 <span class="selector-tag">video</span><span class="selector-class">.mp4</span></span><br></pre></td></tr></tbody></table></figure></li><li><p>提取视频的关键帧</p> <figure class="highlight llvm"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ffmpeg -i input.mp<span class="number">4</span> -vf <span class="keyword">select</span>='<span class="keyword">eq</span>(pict_type\,I)' -vsync <span class="number">2</span> -s <span class="number">160</span><span class="keyword">x</span><span class="number">90</span> -f image<span class="number">2</span> out-<span class="symbol">%02</span>d.jpeg</span><br></pre></td></tr></tbody></table></figure></li><li><p>分解视频音频流</p> <figure class="highlight stylus"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 分离视频流</span></span><br><span class="line">ffmpeg -<span class="selector-tag">i</span> input_file -vcodec copy -an output_file_video</span><br><span class="line"></span><br><span class="line"><span class="comment">// 分离音频流</span></span><br><span class="line">ffmpeg -<span class="selector-tag">i</span> input_file -vcodec copy -vn output_file_audio</span><br></pre></td></tr></tbody></table></figure></li><li><p>视频转码</p> <figure class="highlight stylus"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 转码为码流原始文件</span></span><br><span class="line">ffmpeg -<span class="selector-tag">i</span> <span class="selector-tag">input</span><span class="selector-class">.mp4</span> -vcodec h264 -an -f m4v test.<span class="number">264</span></span><br></pre></td></tr></tbody></table></figure></li><li><p>视频封装</p> <figure class="highlight stylus"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ffmpeg -<span class="selector-tag">i</span> video_file -<span class="selector-tag">i</span> audio_file -vcodec copy -acodec copy output_file</span><br></pre></td></tr></tbody></table></figure></li><li><p>视频录制</p> <figure class="highlight stylus"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 录制视频流</span></span><br><span class="line">ffmpeg -<span class="selector-tag">i</span> rtsp:<span class="comment">//hostname/stream -vcodec copy output.avi</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 通过电脑摄像头录制</span></span><br><span class="line">ffmpeg -f avfoundation -framerate <span class="number">30</span> -<span class="selector-tag">i</span> <span class="string">"0"</span> -f mpeg1video -<span class="selector-tag">b</span> <span class="number">500</span>k -r <span class="number">20</span> -vf scale=<span class="number">640</span>:<span class="number">360</span> output.avi</span><br></pre></td></tr></tbody></table></figure></li></ul><h2 id="版本更新"><a href="#版本更新" class="headerlink" title="版本更新"></a>版本更新</h2><table><thead><tr><th>版本</th><th>描述</th></tr></thead><tbody><tr><td><a href="https://bintray.com/veizhang/maven/ffmpeg/1.0.0" target="_blank" rel="noopener">1.0.0</a></td><td>集成FFmpeg命令行执行 <strong>2017-8-17</strong></td></tr></tbody></table><h2 id="感谢"><a href="#感谢" class="headerlink" title="感谢"></a>感谢</h2><blockquote><ul><li><a href="https://github.com/WritingMinds/ffmpeg-android-java" title="FFmpeg在Android中示例" target="_blank" rel="noopener">WritingMinds</a>, <a href="https://github.com/WritingMinds/ffmpeg-android" title="编译FFmpeg可执行文件" target="_blank" rel="noopener">WritingMinds</a></li><li><a href="https://github.com/hiliving/VideoEdit" title="FFmpeg命令示例" target="_blank" rel="noopener">hiliving</a></li><li><a href="https://github.com/c060604/ffmpeg-usage" title="FFmpeg命令讲解" target="_blank" rel="noopener">c060604</a></li></ul></blockquote><!-- 网站链接 --><!-- 图片链接 --><!-- 版本 --><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8D%E6%B0%B4%E7%9A%84%E4%B8%96%E7%95%8C%EF%BC%8C%E6%88%91%E7%9F%A5%E9%81%93.jpg" alt="Android" title="水的世界,我知道"></p>
<p><strong>FFmpeg命令在Android中的使用</strong> </p>
</summary>
<category term="Android联盟" scheme="https://tiimor.cn/categories/Android%E8%81%94%E7%9B%9F/"/>
<category term="Android" scheme="https://tiimor.cn/tags/Android/"/>
<category term="FFmpeg" scheme="https://tiimor.cn/tags/FFmpeg/"/>
</entry>
<entry>
<title>基于Android的HTTP下载速度提升</title>
<link href="https://tiimor.cn/%E5%9F%BA%E4%BA%8EAndroid%E7%9A%84HTTP%E4%B8%8B%E8%BD%BD%E9%80%9F%E5%BA%A6%E6%8F%90%E5%8D%87/"/>
<id>https://tiimor.cn/基于Android的HTTP下载速度提升/</id>
<published>2018-07-01T06:43:44.000Z</published>
<updated>2019-03-17T06:47:42.386Z</updated>
<content type="html"><![CDATA[<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8D%E5%8F%AA%E6%9C%89%E5%BF%83%E4%B8%AD%E6%9C%89%E7%88%B1%EF%BC%8C%E4%BD%A0%E6%89%8D%E4%BC%9A%E6%84%9F%E5%8F%97%E5%88%B0%E6%9B%B4%E5%A4%9A%E7%9A%84%E7%88%B1.jpg" alt="Android" title="只有心中有爱,你才会感受到更多的爱"></p><p><strong>只为极速下载</strong></p><a id="more"></a><h2 id="因素"><a href="#因素" class="headerlink" title="因素"></a>因素</h2><p>HTTP下载速度受限于两个因素,<strong>这里不讨论服务器限制以及多线程</strong></p><ul><li>带宽网速</li><li>文件写入速度</li></ul><h2 id="速度提升"><a href="#速度提升" class="headerlink" title="速度提升"></a>速度提升</h2><p>由于带宽是固定的,因此,文件读写速度是下载速度的关键。</p><h3 id="BufferedRandomAccessFile"><a href="#BufferedRandomAccessFile" class="headerlink" title="BufferedRandomAccessFile"></a><a href="https://github.com/VeiZhang/Downloader/blob/master/DownloaderLibrary/src/main/java/com/excellence/downloader/utils/BufferedRandomAccessFile.java" target="_blank" rel="noopener">BufferedRandomAccessFile</a></h3><p>普遍的,我们使用RandomAccessFile进行断点下载,对文件读写操作,线程对磁盘的读写非常频繁,导致写入文件非常慢,从而导致下载速度慢。因此,采用具有缓冲的RandomAccessFile,能快速降低磁盘的IO。</p><p>以下是测试速度对比,转载自<a href="https://blog.csdn.net/hpb21/article/details/51270873" target="_blank" rel="noopener">https://blog.csdn.net/hpb21/article/details/51270873</a></p><table><thead><tr><th>读</th><th>写</th><th>耗时(s)</th></tr></thead><tbody><tr><td>RandomAccessFile</td><td>RandomAccessFile</td><td>95.848</td></tr><tr><td>BufferedInputStream + DataInputStream</td><td>BufferedOutputStream + DataOutputStream</td><td>2.935</td></tr><tr><td>BufferedRandomAccessFile</td><td>BufferedRandomAccessFile</td><td>0.401</td></tr></tbody></table><h3 id="块传输"><a href="#块传输" class="headerlink" title="块传输"></a>块传输</h3><p>通过对比,FileChannel写文件速度优于普通的复制文件方法</p><p>写法<br></p><figure class="highlight arduino"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 使用块传输,直接通过追加的形式,写入到文件里</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * @param inputStream</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> dynamicTransmission(InputStream inputStream) throws Exception</span><br><span class="line">{</span><br><span class="line">FileOutputStream outputStream = <span class="keyword">new</span> FileOutputStream(mTempFile, true);</span><br><span class="line">FileChannel channel = outputStream.getChannel();</span><br><span class="line">ReadableByteChannel readableByteChannel = Channels.newChannel(inputStream);</span><br><span class="line">ByteBuffer <span class="built_in">buffer</span> = ByteBuffer.allocate(STREAM_LEN);</span><br><span class="line"><span class="keyword">int</span> <span class="built_in">read</span>;</span><br><span class="line"><span class="built_in">while</span> ((<span class="built_in">read</span> = readableByteChannel.<span class="built_in">read</span>(<span class="built_in">buffer</span>)) != <span class="number">-1</span>)</span><br><span class="line">{</span><br><span class="line"><span class="built_in">buffer</span>.flip();</span><br><span class="line">channel.<span class="built_in">write</span>(<span class="built_in">buffer</span>);</span><br><span class="line"><span class="built_in">buffer</span>.compact();</span><br><span class="line"></span><br><span class="line">mTaskEntity.downloadLen += <span class="built_in">read</span>;</span><br><span class="line">onProgressChange(mTaskEntity.fileSize, mTaskEntity.downloadLen);</span><br><span class="line"></span><br><span class="line"><span class="built_in">if</span> (mTaskEntity.isCancel)</span><br><span class="line">{</span><br><span class="line">onCancel();</span><br><span class="line"><span class="built_in">break</span>;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">outputStream.<span class="built_in">close</span>();</span><br><span class="line">channel.<span class="built_in">close</span>();</span><br><span class="line">readableByteChannel.<span class="built_in">close</span>();</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p></p><h2 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h2><p><strong><a href="https://github.com/VeiZhang/Downloader" target="_blank" rel="noopener">Downloader</a>,欢迎star!</strong></p><p>使用上述两种方式,下载速度明显提高了。</p><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8D%E5%8F%AA%E6%9C%89%E5%BF%83%E4%B8%AD%E6%9C%89%E7%88%B1%EF%BC%8C%E4%BD%A0%E6%89%8D%E4%BC%9A%E6%84%9F%E5%8F%97%E5%88%B0%E6%9B%B4%E5%A4%9A%E7%9A%84%E7%88%B1.jpg" alt="Android" title="只有心中有爱,你才会感受到更多的爱"></p>
<p><strong>只为极速下载</strong></p>
</summary>
<category term="Android联盟" scheme="https://tiimor.cn/categories/Android%E8%81%94%E7%9B%9F/"/>
<category term="Android" scheme="https://tiimor.cn/tags/Android/"/>
<category term="HTTP" scheme="https://tiimor.cn/tags/HTTP/"/>
<category term="Downloader" scheme="https://tiimor.cn/tags/Downloader/"/>
</entry>
<entry>
<title>AndroidSDK-原生Settings添加菜单项</title>
<link href="https://tiimor.cn/AndroidSDK-%E5%8E%9F%E7%94%9FSettings%E6%B7%BB%E5%8A%A0%E8%8F%9C%E5%8D%95%E9%A1%B9/"/>
<id>https://tiimor.cn/AndroidSDK-原生Settings添加菜单项/</id>
<published>2018-06-03T11:01:41.000Z</published>
<updated>2019-03-17T06:47:42.356Z</updated>
<content type="html"><![CDATA[<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8D%E6%AD%A3%E4%B8%AD%E9%9D%B6%E5%BF%83.jpg" alt="Android" title="正中靶心"></p><p><strong>Android N原生Settings应用添加自定义的一级菜单项</strong></p><a id="more"></a><h1 id="被动方式"><a href="#被动方式" class="headerlink" title="被动方式"></a>被动方式</h1><ul><li><p><strong>被动方式</strong>:修改被调用应用的AndroidManifest.xml</p><p> 根据菜单项<code>Google</code>的启发,因此猜想是否可以通过这种方式添加选项。</p><p> <img src="http://cdn.tiimor.cn/images/%E5%8E%9F%E7%94%9FSettings%E8%8F%9C%E5%8D%95%E4%BF%AE%E6%94%B9.png" alt="原生Settings菜单修改"></p></li></ul><ul><li><p>源码追踪</p><p> 追踪:<code>Settings\src\com\android\settings\SettingsActivity.java</code><br> 追踪父类:<code>SettingsLib\src\com\android\settingslib\drawer\SettingsDrawerActivity.java</code></p><p> <img src="http://cdn.tiimor.cn/images/%E5%8E%9F%E7%94%9FSettings%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90-%E6%B7%BB%E5%8A%A0%E8%8F%9C%E5%8D%951.png" alt="原生Settings源码分析-添加菜单1"></p><p> 最终定位:<code>SettingsLib\src\com\android\settingslib\drawer\TileUtils.java</code>,因此根据源码,反推出原生Settings通过遍历AndroidManifest.xml文件,添加符合要求的菜单项<br> <img src="http://cdn.tiimor.cn/images/%E5%8E%9F%E7%94%9FSettings%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90-%E6%B7%BB%E5%8A%A0%E8%8F%9C%E5%8D%952.png" alt="原生Settings源码分析-添加菜单2"></p></li></ul><ul><li><p>修改AndroidManifest.xml</p><p> <img src="http://cdn.tiimor.cn/images/%E5%8E%9F%E7%94%9FSettings-AndroidManifest%E4%BF%AE%E6%94%B9.png" alt="原生Settings-AndroidManifest修改"> </p> <figure class="highlight xml"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">activity</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:name</span>=<span class="string">".app.AppsActivity"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:launchMode</span>=<span class="string">"singleTask"</span>></span></span><br><span class="line"><span class="comment"><!-- 核心,特别重要,必须添加:用于Settings过滤出菜单项Category --></span></span><br><span class="line"> <span class="tag"><<span class="name">intent-filter</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">action</span> <span class="attr">android:name</span>=<span class="string">"com.android.settings.MANUFACTURER_APPLICATION_SETTING"</span> /></span></span><br><span class="line"> <span class="tag"></<span class="name">intent-filter</span>></span></span><br><span class="line"> <span class="comment"><!-- 设置显示的title --></span></span><br><span class="line"> <span class="tag"><<span class="name">meta-data</span> <span class="attr">android:name</span>=<span class="string">"com.android.settings.title"</span> <span class="attr">android:resource</span>=<span class="string">"@string/app_name"</span> /></span></span><br><span class="line"> <span class="comment"><!-- 设置显示的小标题 --></span></span><br><span class="line"> <span class="tag"><<span class="name">meta-data</span> <span class="attr">android:name</span>=<span class="string">"com.android.settings.summary"</span> <span class="attr">android:resource</span>=<span class="string">"@string/app_name"</span> /></span></span><br><span class="line"> <span class="comment"><!-- 设置显示的图标 --></span></span><br><span class="line"> <span class="tag"><<span class="name">meta-data</span> <span class="attr">android:name</span>=<span class="string">"com.android.settings.icon"</span> <span class="attr">android:resource</span>=<span class="string">"@drawable/load_err"</span> /></span></span><br><span class="line"> <span class="comment"><!-- 核心,特别重要,必须添加:表示添加一个菜单选项,用于Settings分类,归属于“个人”的选项下,也可以是其他的:系统等等 --></span></span><br><span class="line"> <span class="tag"><<span class="name">meta-data</span> <span class="attr">android:name</span>=<span class="string">"com.android.settings.category"</span> <span class="attr">android:value</span>=<span class="string">"com.android.settings.category.personal"</span>/></span></span><br><span class="line"><span class="tag"></<span class="name">activity</span>></span></span><br></pre></td></tr></tbody></table></figure></li><li><p>验证猜想</p><p> 通过反编译Google的apk发现,里面的AndroidManifest里面行数L2536-L2543的代码与上面修改的代码是一样的,因此,猜想是正确的。</p></li></ul><ul><li><p>拓展</p><ul><li><p>菜单项的所属分类<br>修改value值,可以选择分类:personal、system,其他的分类未验证</p><figure class="highlight groovy"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><meta-data <span class="string">android:</span>name=<span class="string">"com.android.settings.category"</span></span><br><span class="line"><span class="comment">// android:value="com.android.settings.category.system</span></span><br><span class="line"><span class="string">android:</span>value=<span class="string">"com.android.settings.category.personal"</span>/></span><br></pre></td></tr></tbody></table></figure></li><li><p>菜单项的顺序<br>修改优先级,可以调整顺序,等级越高的,在上面</p><figure class="highlight xml"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">intent-filter</span> <span class="attr">android:priority</span>=<span class="string">"4"</span>></span></span><br><span class="line"><span class="tag"></<span class="name">intent-filter</span>></span></span><br></pre></td></tr></tbody></table></figure></li></ul></li></ul><ul><li><p>注意</p><p> <strong>通过验证:需要预置apk才有效,安装的方式是没有作用</strong></p></li></ul><h1 id="主动方式"><a href="#主动方式" class="headerlink" title="主动方式"></a>主动方式</h1><ul><li><strong>主动的方式</strong>:通过修改原生Settings的代码,也可以添加一级、二级菜单项,但是这种方式比较繁琐复杂。如果只是简单在原生Settings上添加一级菜单项,建议使用<strong>被动方式</strong>。</li></ul><h1 id="感谢"><a href="#感谢" class="headerlink" title="感谢"></a>感谢</h1><p>非常感谢 <strong>迷途羔羊</strong> 的帮助<span class="github-emoji" style="color: transparent;background:no-repeat url(https://assets-cdn.github.com/images/icons/emoji/unicode/1f601.png?v8) center/contain" data-src="https://assets-cdn.github.com/images/icons/emoji/unicode/1f601.png?v8">😁</span><span class="github-emoji" style="color: transparent;background:no-repeat url(https://assets-cdn.github.com/images/icons/emoji/unicode/1f601.png?v8) center/contain" data-src="https://assets-cdn.github.com/images/icons/emoji/unicode/1f601.png?v8">😁</span></p><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8D%E6%AD%A3%E4%B8%AD%E9%9D%B6%E5%BF%83.jpg" alt="Android" title="正中靶心"></p>
<p><strong>Android N原生Settings应用添加自定义的一级菜单项</strong></p>
</summary>
<category term="Android联盟" scheme="https://tiimor.cn/categories/Android%E8%81%94%E7%9B%9F/"/>
<category term="AndroidSDK" scheme="https://tiimor.cn/tags/AndroidSDK/"/>
<category term="Settings" scheme="https://tiimor.cn/tags/Settings/"/>
</entry>
<entry>
<title>AndroidStudio统一管理gradle脚本</title>
<link href="https://tiimor.cn/AndroidStudio%E7%BB%9F%E4%B8%80%E7%AE%A1%E7%90%86gradle%E8%84%9A%E6%9C%AC/"/>
<id>https://tiimor.cn/AndroidStudio统一管理gradle脚本/</id>
<published>2018-06-01T03:10:02.000Z</published>
<updated>2019-04-13T02:03:10.809Z</updated>
<content type="html"><![CDATA[<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8D%E5%9C%A8%E4%B9%8E%E5%B1%B1%E6%B0%B4%E4%B9%8B%E9%97%B4%E4%B9%9F.jpg" alt="Android" title="在乎山水之间也"></p><p><a href="https://github.com/VeiZhang/build.gradle" target="_blank" rel="noopener">AndroidStudio统一管理gradle编译脚本</a></p><a id="more"></a><h2 id="目的"><a href="#目的" class="headerlink" title="目的"></a>目的</h2><p><strong>当工程集成很多Modules时,每个Module都有一个build.gradle,并且带有如下重复的代码;对每个build.gradle修改很麻烦,因此统一管理build.gradle文件是必要的</strong></p><figure class="highlight gradle"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 重复代码</span></span><br><span class="line">android {</span><br><span class="line"> compileSdkVersion <span class="number">25</span></span><br><span class="line"> buildToolsVersion <span class="string">"25.0.0"</span></span><br><span class="line"></span><br><span class="line"> defaultConfig {</span><br><span class="line"> minSdkVersion <span class="number">15</span></span><br><span class="line"> targetSdkVersion <span class="number">23</span></span><br><span class="line"> versionCode <span class="number">1</span></span><br><span class="line"> versionName <span class="string">"1.0"</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">dependencies</span> {</span><br><span class="line"> <span class="keyword">compile</span> <span class="keyword">fileTree</span>(dir: <span class="string">'libs'</span>, <span class="keyword">include</span>: [<span class="string">'*.jar'</span>])</span><br><span class="line"> testCompile <span class="string">'junit:junit:4.12'</span></span><br><span class="line"> <span class="keyword">compile</span> <span class="string">'com.android.support:appcompat-v7:24.2.1'</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li><strong>优化代码</strong></li><li><strong>使用相同的编译配置</strong></li><li><strong>统一管理远程依赖</strong></li><li>减少<code>sync project</code>次数</li></ul><h2 id="本地配置"><a href="#本地配置" class="headerlink" title="本地配置"></a>本地配置<a name="本地配置"></a></h2><ul><li><p>在项目根目录下创建config.gradle文件,作为管理配置文件</p> <figure class="highlight dart"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.text.SimpleDateFormat</span><br><span class="line"><span class="keyword">import</span> java.util.regex.Matcher</span><br><span class="line"><span class="keyword">import</span> java.util.regex.<span class="built_in">Pattern</span></span><br><span class="line"></span><br><span class="line"> ext {</span><br><span class="line"> <span class="comment">// 插件</span></span><br><span class="line"> plugins = [</span><br><span class="line"> application : <span class="string">"com.android.application"</span>,</span><br><span class="line"> <span class="keyword">library</span> : <span class="string">"com.android.library"</span>,</span><br><span class="line"> maven : <span class="string">"com.github.dcendents.android-maven"</span>,</span><br><span class="line"> bintray : <span class="string">"com.jfrog.bintray"</span>,</span><br><span class="line"> novoda : <span class="string">"com.novoda.bintray-release"</span>,</span><br><span class="line"> greendao : <span class="string">"org.greenrobot.greendao"</span>,</span><br><span class="line"> <span class="string">"greendao-gradle"</span> : <span class="string">"org.greenrobot:greendao-gradle-plugin:3.2.2"</span></span><br><span class="line"> ]</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 配置</span></span><br><span class="line"> android = [</span><br><span class="line"> <span class="comment"><span class="markdown">/<span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span>原生配置<span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span>/</span></span></span><br><span class="line"> compileSdkVersion : <span class="number">25</span>,</span><br><span class="line"> buildToolsVersion : <span class="string">"25.0.0"</span>,</span><br><span class="line"> minSdkVersion : <span class="number">17</span>,</span><br><span class="line"> targetSdkVersion : <span class="number">23</span>,</span><br><span class="line"> versionCode : getVersionCode(),</span><br><span class="line"> versionName : getVersionName(),</span><br><span class="line"></span><br><span class="line"> <span class="comment"><span class="markdown">/<span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span>自定义配置<span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span>/</span></span></span><br><span class="line"> androidSupportSdkVersion: <span class="string">"23.0.0"</span></span><br><span class="line"> ]</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 依赖</span></span><br><span class="line"> dependencies = [</span><br><span class="line"> <span class="comment"><span class="markdown">/<span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span>原生依赖<span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span>/</span></span></span><br><span class="line"> <span class="string">"appcompat-v7"</span> : <span class="string">"com.android.support:appcompat-v7:<span class="subst">${android[<span class="string">"androidSupportSdkVersion"</span>]}</span>"</span>,</span><br><span class="line"> <span class="string">"support-v4"</span> : <span class="string">"com.android.support:support-v4:<span class="subst">${android[<span class="string">"androidSupportSdkVersion"</span>]}</span>"</span>,</span><br><span class="line"> <span class="string">"cardview-v7"</span> : <span class="string">"com.android.support:cardview-v7:<span class="subst">${android[<span class="string">"androidSupportSdkVersion"</span>]}</span>"</span>,</span><br><span class="line"> <span class="string">"recyclerview-v7"</span> : <span class="string">"com.android.support:recyclerview-v7:<span class="subst">${android[<span class="string">"androidSupportSdkVersion"</span>]}</span>"</span>,</span><br><span class="line"> <span class="string">"design"</span> : <span class="string">"com.android.support:design:<span class="subst">${android[<span class="string">"androidSupportSdkVersion"</span>]}</span>"</span>,</span><br><span class="line"> <span class="string">"annotations"</span> : <span class="string">"com.android.support:support-annotations:<span class="subst">${android[<span class="string">"androidSupportSdkVersion"</span>]}</span>"</span>,</span><br><span class="line"> <span class="string">"gridlayout-v7"</span> : <span class="string">"com.android.support:gridlayout-v7:<span class="subst">${android[<span class="string">"androidSupportSdkVersion"</span>]}</span>"</span>,</span><br><span class="line"> <span class="string">"constraint-layout"</span> : <span class="string">"com.android.support.constraint:constraint-layout:1.0.2"</span>,</span><br><span class="line"></span><br><span class="line"> <span class="comment"><span class="markdown">/<span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span>第三方依赖<span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span>/</span></span></span><br><span class="line"> <span class="comment">// https://github.com/square/retrofit</span></span><br><span class="line"> <span class="string">"retrofit2"</span> : <span class="string">"com.squareup.retrofit2:retrofit:2.4.0"</span>,</span><br><span class="line"> <span class="string">"converter-scalars"</span> : <span class="string">"com.squareup.retrofit2:converter-scalars:2.4.0"</span>,</span><br><span class="line"> <span class="string">"converter-gson"</span> : <span class="string">"com.squareup.retrofit2:converter-gson:2.4.0"</span>,</span><br><span class="line"> <span class="string">"adapter-rxjava"</span> : <span class="string">"com.squareup.retrofit2:adapter-rxjava:2.4.0"</span>,</span><br><span class="line"> <span class="string">"adapter-rxjava2"</span> : <span class="string">"com.squareup.retrofit2:adapter-rxjava2:2.4.0"</span>,</span><br><span class="line"> <span class="comment">// https://github.com/square/okhttp</span></span><br><span class="line"> <span class="string">"okhttp"</span> : <span class="string">"com.squareup.okhttp3:okhttp:3.11.0"</span>,</span><br><span class="line"> <span class="comment">// https://github.com/greenrobot/greenDAO</span></span><br><span class="line"> <span class="string">"greendao"</span> : <span class="string">"org.greenrobot:greendao:3.2.2"</span>,</span><br><span class="line"> <span class="comment">// https://github.com/yuweiguocn/GreenDaoUpgradeHelper</span></span><br><span class="line"> <span class="string">"greendao-helper"</span> : <span class="string">"com.github.yuweiguocn:GreenDaoUpgradeHelper:v2.1.0"</span>,</span><br><span class="line"> <span class="comment">// https://github.com/bumptech/glide</span></span><br><span class="line"> <span class="string">"glide"</span> : <span class="string">"com.github.bumptech.glide:glide:4.8.0"</span>,</span><br><span class="line"> <span class="comment">// https://github.com/square/picasso</span></span><br><span class="line"> <span class="string">"picasso"</span> : <span class="string">"com.squareup.picasso:picasso:2.71828"</span>,</span><br><span class="line"> <span class="comment">// https://github.com/facebook/fresco</span></span><br><span class="line"> <span class="string">"fresco"</span> : <span class="string">"com.facebook.fresco:fresco:1.10.0"</span>,</span><br><span class="line"> <span class="comment">// https://github.com/greenrobot/EventBus</span></span><br><span class="line"> <span class="string">"eventbus"</span> : <span class="string">"org.greenrobot:eventbus:3.1.1"</span>,</span><br><span class="line"> <span class="comment">// https://github.com/BuglyDevTeam/Bugly-Android</span></span><br><span class="line"> <span class="string">"bugly"</span> : <span class="string">"com.tencent.bugly:crashreport:2.6.6.1"</span>,</span><br><span class="line"> <span class="string">"bugly-native"</span> : <span class="string">"com.tencent.bugly:nativecrashreport:3.3.1"</span>,</span><br><span class="line"> <span class="comment">// https://bintray.com/android/android-utils/com.android.volley.volley</span></span><br><span class="line"> <span class="string">"volley"</span> : <span class="string">"com.android.volley:volley:1.1.1"</span>,</span><br><span class="line"> <span class="comment">// https://github.com/ReactiveX/RxJava</span></span><br><span class="line"> <span class="string">"rxjava"</span> : <span class="string">"io.reactivex:rxjava:1.3.8"</span>,</span><br><span class="line"> <span class="string">"rxjava2"</span> : <span class="string">"io.reactivex.rxjava2:rxjava:2.2.2"</span>,</span><br><span class="line"> <span class="string">"rxandroid"</span> : <span class="string">"io.reactivex:rxandroid:2.1.0"</span>,</span><br><span class="line"> <span class="string">"rxandroid2"</span> : <span class="string">'io.reactivex.rxjava2:rxandroid:2.0.2'</span>,</span><br><span class="line"> <span class="comment">// https://github.com/JakeWharton/RxBinding</span></span><br><span class="line"> <span class="string">"rxbinding"</span> : <span class="string">'com.jakewharton.rxbinding2:rxbinding:2.2.0'</span>,</span><br><span class="line"> <span class="comment">// https://github.com/google/gson</span></span><br><span class="line"> <span class="string">"gson"</span> : <span class="string">"com.google.code.gson:gson:2.8.5"</span>,</span><br><span class="line"> <span class="comment">// https://github.com/apache/commons-lang</span></span><br><span class="line"> <span class="string">"commons-lang3"</span> : <span class="string">"org.apache.commons:commons-lang3:3.8"</span>,</span><br><span class="line"> <span class="comment">// https://github.com/square/leakcanary</span></span><br><span class="line"> <span class="string">"leakcanary"</span> : <span class="string">"com.squareup.leakcanary:leakcanary-android:1.6.2"</span>,</span><br><span class="line"> <span class="string">"leakcanary-release"</span> : <span class="string">"com.squareup.leakcanary:leakcanary-android-no-op:1.6.2"</span>,</span><br><span class="line"> <span class="string">"leakcanary-fragment"</span> : <span class="string">"com.squareup.leakcanary:leakcanary-support-fragment:1.6.2"</span>,</span><br><span class="line"> <span class="comment">// https://github.com/YoKeyword/Fragmentation</span></span><br><span class="line"> <span class="string">"fragmentation"</span> : <span class="string">"me.yokeyword:fragmentation:1.3.6"</span>,</span><br><span class="line"> </span><br><span class="line"> <span class="comment"><span class="markdown">/<span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span>个人依赖<span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span>/</span></span></span><br><span class="line"> <span class="comment">// https://github.com/VeiZhang/BaseToolsLibrary</span></span><br><span class="line"> <span class="string">"basetools"</span> : <span class="string">"com.excellence:basetools:1.2.6"</span>,</span><br><span class="line"> <span class="comment">// https://github.com/VeiZhang/Permission</span></span><br><span class="line"> <span class="string">"permission"</span> : <span class="string">"com.excellence:permission:1.0.1"</span>,</span><br><span class="line"> <span class="comment">// https://github.com/VeiZhang/RetrofitClient</span></span><br><span class="line"> <span class="string">"retrofit-client"</span> : <span class="string">"com.excellence:retrofit:1.0.5"</span>,</span><br><span class="line"> <span class="comment">// https://github.com/VeiZhang/QSkinLoader</span></span><br><span class="line"> <span class="string">"skinloader"</span> : <span class="string">"com.excellence:skinloader:1.2.2"</span>,</span><br><span class="line"> <span class="comment">// https://github.com/VeiZhang/ToastKit</span></span><br><span class="line"> <span class="string">"toast"</span> : <span class="string">"com.excellence:toast:1.1.0"</span>,</span><br><span class="line"> <span class="comment">// https://github.com/VeiZhang/MailSender</span></span><br><span class="line"> <span class="string">"mailsender"</span> : <span class="string">"com.excellence:mailsender:1.0.0"</span>,</span><br><span class="line"> <span class="comment">// https://github.com/VeiZhang/Downloader</span></span><br><span class="line"> <span class="string">"downloader"</span> : <span class="string">"com.excellence:downloader:1.2.0"</span>,</span><br><span class="line"> <span class="comment">// https://github.com/VeiZhang/AppStatistics</span></span><br><span class="line"> <span class="string">"app-statistics"</span> : <span class="string">"com.excellence:app-statistics:1.0.1"</span>,</span><br><span class="line"> <span class="comment">// https://github.com/VeiZhang/AndroidExec</span></span><br><span class="line"> <span class="string">"exec"</span> : <span class="string">"com.excellence:exec:1.1.0"</span>,</span><br><span class="line"> <span class="comment">// https://github.com/VeiZhang/AndroidFFmpeg</span></span><br><span class="line"> <span class="string">"ffmpeg"</span> : <span class="string">"com.excellence:ffmpeg:1.1.0"</span>,</span><br><span class="line"> <span class="comment">// https://github.com/VeiZhang/ImageLoader</span></span><br><span class="line"> <span class="string">"imageloader"</span> : <span class="string">"com.excellence:imageloader:1.0.0"</span>,</span><br><span class="line"> <span class="string">"imageloader-fresco"</span> : <span class="string">"com.excellence:imageloader-fresco:1.0.0"</span>,</span><br><span class="line"> <span class="string">"imageloader-picasso"</span> : <span class="string">"com.excellence:imageloader-picasso:1.0.0"</span>,</span><br><span class="line"> <span class="string">"imageloader-glide"</span> : <span class="string">"com.excellence:imageloader-glide:1.0.0"</span></span><br><span class="line"> ]</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="markdown">/<span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">***APP版本控制的通用方法**</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span>*/</span></span></span><br><span class="line"><span class="comment"><span class="markdown">/**</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> * svn</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> * 直接读取svn版本号作为版本控制</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> */</span></span></span><br><span class="line"><span class="comment"><span class="markdown">/**</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> * git</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> *</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> * git tag作为版本名称</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> * git 版本号有两种方法</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> * ①版本号作为我们内部开发的标识,一般它是+1递增的,每一次发版我们就会打一个tag,tag的数量也会增加1个,和我们版本号的递增逻辑是符合的,tag数量+1,版本号也会跟着+1</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> * ②还有一种是把提交次数作为versionCode,不推荐。相比较①,②比较难找,因为tag的数量不会很多</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> */</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="markdown">/**</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> * 获取版本</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> * @return</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> */</span></span></span><br><span class="line">def getVersionName() {</span><br><span class="line"> def date = getDate()</span><br><span class="line"> def version = getSvnVersionCode()</span><br><span class="line"> <span class="keyword">if</span> (version != <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"1.0.<span class="subst">${version}</span> [<span class="subst">${date}</span>]"</span></span><br><span class="line"> }</span><br><span class="line"> version = getGitTag()</span><br><span class="line"> <span class="keyword">if</span> (version != <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"<span class="subst">${version}</span> [<span class="subst">${date}</span>]"</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (version == <span class="number">0</span>) {</span><br><span class="line"> version = <span class="number">1</span></span><br><span class="line"> }</span><br><span class="line"> <span class="comment"><span class="markdown">/**</span></span></span><br><span class="line"><span class="comment"><span class="markdown"><span class="code"> * 错误的版本信息,请检查</span></span></span></span><br><span class="line"><span class="comment"><span class="markdown"><span class="code"> */</span></span></span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"0.<span class="subst">${version}</span> [<span class="subst">${date}</span>]"</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="markdown">/**</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> * 获取版本号</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> * @return</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> */</span></span></span><br><span class="line">def getVersionCode() {</span><br><span class="line"> def versionCode = getSvnVersionCode()</span><br><span class="line"> <span class="keyword">if</span> (versionCode == <span class="number">0</span>) {</span><br><span class="line"> versionCode = getGitVersionCode()</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (versionCode == <span class="number">0</span>) {</span><br><span class="line"> versionCode = <span class="number">1</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> versionCode</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="markdown">/<span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">***读取Git信息**</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span>*/</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="markdown">/**</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> * 读取git tag</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> * @return tag</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> */</span></span></span><br><span class="line">def getGitTag() {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> def stdout = <span class="keyword">new</span> ByteArrayOutputStream()</span><br><span class="line"> exec {</span><br><span class="line"> commandLine <span class="string">'git'</span>, <span class="string">'describe'</span>, <span class="string">'--abbrev=0'</span>, <span class="string">'--tags'</span></span><br><span class="line"> standardOutput = stdout</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> stdout.toString().split(<span class="string">"\n"</span>)</span><br><span class="line"> } <span class="keyword">catch</span> (e) {</span><br><span class="line"> println e.getMessage()</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="markdown">/**</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> * 以git tag的数量作为其版本号</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> * @return tag的数量</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> */</span></span></span><br><span class="line">def getGitVersionCode() {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> def stdout = <span class="keyword">new</span> ByteArrayOutputStream()</span><br><span class="line"> exec {</span><br><span class="line"> commandLine <span class="string">'git'</span>, <span class="string">'tag'</span>, <span class="string">'--list'</span></span><br><span class="line"> standardOutput = stdout</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> stdout.toString().split(<span class="string">"\n"</span>).size()</span><br><span class="line"> } <span class="keyword">catch</span> (e) {</span><br><span class="line"> println e.getMessage()</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="markdown">/<span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">***读取SVN信息**</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span>*/</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="markdown">/**</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> * 根据svn提交版本生成版本号</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> * @return</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> */</span></span></span><br><span class="line">def getSvnVersionCode() {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> def process = (<span class="string">"svnversion -c "</span> + getBuildDir().parent).execute()</span><br><span class="line"> process.waitFor()</span><br><span class="line"> def version = process.<span class="keyword">in</span>.text</span><br><span class="line"> <span class="built_in">Pattern</span> pattern = <span class="built_in">Pattern</span>.compile(<span class="string">"(\\d+:)?(\\d+)\\D"</span>)</span><br><span class="line"> Matcher matcher = pattern.matcher(version)</span><br><span class="line"> <span class="keyword">if</span> (matcher.find()) {</span><br><span class="line"> version = matcher.group(matcher.groupCount())</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> Integer.parseInt(version)</span><br><span class="line"> } <span class="keyword">catch</span> (e) {</span><br><span class="line"> println e.getMessage()</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="markdown">/**</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> * 获取日期</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> * @return</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> */</span></span></span><br><span class="line">def getDate() {</span><br><span class="line"> <span class="built_in">String</span> date = <span class="keyword">new</span> SimpleDateFormat(<span class="string">"MMddyyyy"</span>).format(<span class="keyword">new</span> Date())</span><br><span class="line"> <span class="keyword">return</span> date</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p> <img src="https://github.com/VeiZhang/build.gradle/blob/master/images/config.gradle.png?raw=true" alt="config.gradle" title="config.gradle"></p></li><li><p>在项目根目录的build.gradle中引用,供其他的Module使用</p><p> <strong>注意:</strong> </p><ol><li><strong>如果想使用ext的值,则只能在项目根目录的build.gradle中引用</strong></li><li><p>想让单独的Module使用,则在该Module的build.gradle里引入,但是此时不能使用ext的值,否则会提示无法找到”Error:Cannot get property ‘xxx’ on extra properties extension as it does not exist”</p><figure class="highlight sqf"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">apply</span> <span class="keyword">from</span>: <span class="string">"config.gradle"</span></span><br></pre></td></tr></tbody></table></figure><p><img src="https://github.com/VeiZhang/build.gradle/blob/master/images/config引用.png?raw=true" alt="config引用" title="config引用"></p></li></ol></li></ul><ul><li><p>在Module目录的build.gradle中使用变量</p><p> <img src="https://github.com/VeiZhang/build.gradle/blob/master/images/config使用.png?raw=true" alt="config使用" title="config使用"></p></li></ul><h2 id="远程配置"><a href="#远程配置" class="headerlink" title="远程配置"></a>远程配置</h2><p>远程配置配置其他步骤与<a href="#本地配置">本地配置</a>是一样,不同的是引用的方式,导入的不是路径里的文件,而是一个文件链接</p><figure class="highlight n1ql"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">apply from: "https://github.com/VeiZhang/<span class="keyword">build</span>.gradle/blob/master/config.gradle?<span class="keyword">raw</span>=<span class="literal">true</span><span class="string">"</span></span><br></pre></td></tr></tbody></table></figure><p><img src="https://github.com/VeiZhang/build.gradle/blob/master/images/%E8%BF%9C%E7%A8%8B%E9%85%8D%E7%BD%AE%E5%BC%95%E7%94%A8.png?raw=true" alt="远程配置引用" title="远程配置引用"></p><h2 id="继承方式"><a href="#继承方式" class="headerlink" title="继承方式"></a>继承方式</h2><p>本地、远程的配置都只是保存了变量,但是如果想引用gradle文件里面的函数,其实很简单,与上面是相同的做法,必须注意的是,<strong>apply from:</strong> 放置的顺序很重要!</p><p><strong>注意</strong>:引入位置在文件的开头、中间、结尾等处使用。因为gradle脚本编译是顺序执行的,如果父脚本与子脚本有相同的方法,此时父脚本引入的顺序就非常重要,不同的位置,执行先后不一样。</p><p><img src="https://github.com/VeiZhang/build.gradle/blob/master/images/%E7%BB%A7%E6%89%BF%E6%96%B9%E5%BC%8F.png?raw=true" alt="继承方式" title="继承方式"></p><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8D%E5%9C%A8%E4%B9%8E%E5%B1%B1%E6%B0%B4%E4%B9%8B%E9%97%B4%E4%B9%9F.jpg" alt="Android" title="在乎山水之间也"></p>
<p><a href="https://github.com/VeiZhang/build.gradle" target="_blank" rel="noopener">AndroidStudio统一管理gradle编译脚本</a></p>
</summary>
<category term="Android联盟" scheme="https://tiimor.cn/categories/Android%E8%81%94%E7%9B%9F/"/>
<category term="AndroidStudio" scheme="https://tiimor.cn/tags/AndroidStudio/"/>
<category term="gradle" scheme="https://tiimor.cn/tags/gradle/"/>
</entry>
<entry>
<title>Android二维码生成与扫描</title>
<link href="https://tiimor.cn/Android%E4%BA%8C%E7%BB%B4%E7%A0%81%E7%94%9F%E6%88%90%E4%B8%8E%E6%89%AB%E6%8F%8F/"/>
<id>https://tiimor.cn/Android二维码生成与扫描/</id>
<published>2018-03-04T05:57:16.000Z</published>
<updated>2019-03-17T06:47:42.359Z</updated>
<content type="html"><![CDATA[<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8D%E5%B0%8F%E5%8F%AF%E7%88%B1.jpg" alt="Android" title="小可爱"></p><p><strong>老板,请尽情用红包来蹂躏我吧!!!<span class="github-emoji" style="color: transparent;background:no-repeat url(https://assets-cdn.github.com/images/icons/emoji/unicode/1f618.png?v8) center/contain" data-src="https://assets-cdn.github.com/images/icons/emoji/unicode/1f618.png?v8">😘</span><span class="github-emoji" style="color: transparent;background:no-repeat url(https://assets-cdn.github.com/images/icons/emoji/unicode/1f618.png?v8) center/contain" data-src="https://assets-cdn.github.com/images/icons/emoji/unicode/1f618.png?v8">😘</span><span class="github-emoji" style="color: transparent;background:no-repeat url(https://assets-cdn.github.com/images/icons/emoji/unicode/1f618.png?v8) center/contain" data-src="https://assets-cdn.github.com/images/icons/emoji/unicode/1f618.png?v8">😘</span></strong></p><a id="more"></a><p>扫码场景现在很普遍,支付宝付款、微信付款、扫码等,两种扫码方式:<strong><a href="https://github.com/zxing/zxing" target="_blank" rel="noopener">zxing</a></strong>、<strong><a href="https://github.com/ZBar/ZBar" target="_blank" rel="noopener">zbar</a></strong>。了解与使用,方便集成到项目中。</p><h1 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h1><ul><li><p>zxing</p><ul><li>google推出用于识别多种格式条形码的开源项目,维护中</li><li>支持更多的码制:datamatix、PDF417</li></ul></li><li><p>zbar</p><ul><li>主要用C来写,速度极快,推出iPhone的SDK和Android的调用方法JNI,不在维护</li><li>不能很好支持PDF417,但是在源码中有对PDF417码的处理</li></ul></li></ul><p>比较两者的扫码速度,实践证明,<strong>zbar的扫码速度优于zxing</strong>。</p><h1 id="感谢、集成"><a href="#感谢、集成" class="headerlink" title="感谢、集成"></a>感谢、集成</h1><p>由于源代码并不是所有的模块都是需要的,因此裁剪,优化是非常必要的。<br>感谢<strong><a href="https://github.com/bingoogolapple/BGAQRCode-Android" target="_blank" rel="noopener">bingoogolapple</a></strong>,裁剪了源代码,并且打包成依赖库:<strong>生成二维码、扫描二维码</strong>。两种扫码方式之间切换非常方便。</p><blockquote><ul><li>zxing<figure class="highlight nginx"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">dependencies</span> {</span><br><span class="line"> <span class="attribute">implementation</span> <span class="string">'cn.bingoogolapple:bga-qrcode-zxing:latestVersion'</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul></blockquote><blockquote><ul><li>zbar<figure class="highlight nginx"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">dependencies</span> {</span><br><span class="line"> <span class="attribute">implementation</span> <span class="string">'cn.bingoogolapple:bga-qrcode-zbar:latestVersion'</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul></blockquote><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8D%E5%B0%8F%E5%8F%AF%E7%88%B1.jpg" alt="Android" title="小可爱"></p>
<p><strong>老板,请尽情用红包来蹂躏我吧!!!:kissing_heart::kissing_heart::kissing_heart:</strong></p>
</summary>
<category term="Android联盟" scheme="https://tiimor.cn/categories/Android%E8%81%94%E7%9B%9F/"/>
<category term="Android" scheme="https://tiimor.cn/tags/Android/"/>
<category term="二维码" scheme="https://tiimor.cn/tags/%E4%BA%8C%E7%BB%B4%E7%A0%81/"/>
<category term="zxing" scheme="https://tiimor.cn/tags/zxing/"/>
<category term="zbar" scheme="https://tiimor.cn/tags/zbar/"/>
</entry>
<entry>
<title>Merry Christmas-2017</title>
<link href="https://tiimor.cn/Merry-Christmas/"/>
<id>https://tiimor.cn/Merry-Christmas/</id>
<published>2017-12-25T11:58:55.000Z</published>
<updated>2019-03-17T06:47:42.381Z</updated>
<content type="html"><![CDATA[<p><img src="http://cdn.tiimor.cn/images/Merry%20Christmas-2017.png" alt="Merry Christmas" title="Merry Christmas"></p><a id="more"></a><figure class="highlight coq"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"> __________________________________________________</span><br><span class="line">| <span class="type">_</span> |<span class="type"></span></span><br><span class="line"><span class="type">| /|,/ _</span> <span class="keyword">_</span> <span class="keyword">_</span> / ` /<span class="keyword">_</span> <span class="keyword">_</span> . <span class="keyword">_</span> <span class="keyword">_</span>/<span class="keyword">_</span> <span class="keyword">_</span> <span class="keyword">_</span> <span class="keyword">_</span> <span class="keyword">_</span>|<span class="type"></span></span><br><span class="line"><span class="type">|/ / /_</span>' / / /<span class="keyword">_</span>/ /<span class="keyword">_</span>, / / / / <span class="keyword">_</span>\ / / / / /<span class="keyword">_</span>| <span class="type">_</span>\ |<span class="type"></span></span><br><span class="line"><span class="type">| _</span>/ |<span class="type"></span></span><br><span class="line"><span class="type">| ~~** Tiimor</span> **~~ |<span class="type"></span></span><br><span class="line"><span class="type">|__________________________________________________</span>|<span class="type"></span></span><br><span class="line"><span class="type"></span></span><br><span class="line"><span class="type"></span></span><br><span class="line"><span class="type"> ___</span></span><br><span class="line"> /` `'.</span><br><span class="line"> / <span class="keyword">_</span>..---;</span><br><span class="line"> | <span class="type">/__</span>...<span class="keyword">_</span>/ .--.-.</span><br><span class="line"> |<span class="type">.' e</span> e | <span class="type">___</span>\<span class="keyword">_</span>|<span class="type">/____</span></span><br><span class="line"> (<span class="keyword">_</span>)'--.o.--| <span class="type">| | |</span></span><br><span class="line"><span class="type"> .-( `-' = `-|____</span>| <span class="type">|____</span>|<span class="type"></span></span><br><span class="line"><span class="type"> / ( |____</span> ____|<span class="type"></span></span><br><span class="line"><span class="type"> | ( |_</span> | <span class="type">| __</span>|<span class="type"></span></span><br><span class="line"><span class="type"> | '-.--';/'/__</span> | <span class="type">| ( `|</span></span><br><span class="line"><span class="type"> | '. \ )"";--`\ /</span></span><br><span class="line"><span class="type"> \ ; |--' `;.-'</span></span><br><span class="line"><span class="type"> |`-.__</span> ..-'--'`;..--'`</span><br><span class="line"></span><br><span class="line"> :*~*:.<span class="keyword">_</span>.:*~*:.<span class="keyword">_</span>.:*~*:.<span class="keyword">_</span>.:*~*:.<span class="keyword">_</span>.:*~*:.<span class="keyword">_</span>.:*~*:.<span class="keyword">_</span>.:*~*</span><br></pre></td></tr></tbody></table></figure><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
<p><img src="http://cdn.tiimor.cn/images/Merry%20Christmas-2017.png" alt="Merry Christmas" title="Merry Christmas"></p>
</summary>
</entry>
<entry>
<title>Android功守道-反编译VS混淆与加壳</title>
<link href="https://tiimor.cn/Android%E5%8A%9F%E5%AE%88%E9%81%93-%E5%8F%8D%E7%BC%96%E8%AF%91VS%E6%B7%B7%E6%B7%86%E4%B8%8E%E5%8A%A0%E5%A3%B3/"/>
<id>https://tiimor.cn/Android功守道-反编译VS混淆与加壳/</id>
<published>2017-12-19T12:18:41.000Z</published>
<updated>2019-03-17T06:47:42.361Z</updated>
<content type="html"><![CDATA[<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8D%E8%97%8F%E5%9C%A8%E5%A2%99%E4%B8%AD%E7%9A%84android.jpg" alt="Android" title="藏在墙中的android"></p><p><strong>反编译并不是为了去破解人家辛辛苦苦开发的app;混淆与加壳也是为了在一定程度上保护自己的开发成果。反编译、混淆、加壳是非常有用的技能。</strong></p><a id="more"></a><p>记录一下个人日常使用</p><h1 id="反编译"><a href="#反编译" class="headerlink" title="反编译"></a>反编译</h1><h2 id="反编译工具"><a href="#反编译工具" class="headerlink" title="反编译工具"></a>反编译工具</h2><ul><li><p><strong><a href="http://note.youdao.com/noteshare?id=4407e5ea540038d4dd16e05b22ca89bb&sub=4C55C31576284FEA8373E50938A16608" target="_blank" rel="noopener">apktool</a></strong><br>资源文件获取,可以提取出图片文件和布局文件进行使用查看:<code>apktool.bat d -f [apk文件] [输出文件夹]</code></p></li><li><p><strong><a href="http://note.youdao.com/noteshare?id=5bc5444d04b4c162b78c80ca6b503acf&sub=6E2DCBD46DD44889980AA4FF7C3700F7" target="_blank" rel="noopener">dex2jar</a></strong><br>将apk反编译成Java源码(classes.dex转化成jar文件):<code>dex2jar.bat [apk文件]</code></p></li><li><p><strong><a href="http://note.youdao.com/noteshare?id=2564f7947bbba25c2541d2eccf4a2d10&sub=1055626537D747098912D757314E9FB9" target="_blank" rel="noopener">jd-gui</a></strong><br>查看APK中classes.dex转化成出的jar文件,即源码文件:用jd-gui.exe打开上面生成的classes.dex即可</p></li></ul><p>注:<strong>AndroidKiller</strong>是一个反编译工具,一步到位,看Java源码的时候,需要借助smail转java的工具。另外将test.apk的apk文件更换后缀名为test.zip,可以提取出资源图片文件等等。</p><h2 id="未混淆"><a href="#未混淆" class="headerlink" title="未混淆"></a>未混淆</h2><p>未混淆的app,反编译可以看到源码:<br><img src="http://cdn.tiimor.cn/images/%E6%9C%AA%E6%B7%B7%E6%B7%86.png" alt="未混淆"></p><h2 id="混淆"><a href="#混淆" class="headerlink" title="混淆"></a>混淆</h2><p>混淆app之后,反编译得到的源码:<br><img src="http://cdn.tiimor.cn/images/%E6%B7%B7%E6%B7%86.png" alt="混淆"></p><h1 id="混淆-1"><a href="#混淆-1" class="headerlink" title="混淆"></a>混淆</h1><p>开启App的混淆,在一定程度上保护自己辛苦开发的成果,即反编译出来的源码是<code>a b c...</code>这样的,但是仍然可以被反编译看到AndroidManifest和资源文件。</p><h2 id="开启混淆"><a href="#开启混淆" class="headerlink" title="开启混淆"></a>开启混淆</h2><p>在Android Studio的app工程目录里,release版本开启代码混淆,然后混淆的配置写在该目录里的<code>proguard-rules.pro</code>文件里</p><figure class="highlight actionscript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">release {</span><br><span class="line"> <span class="comment">// proguard files</span></span><br><span class="line"> minifyEnabled <span class="literal">true</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// add proguard cfg</span></span><br><span class="line"> proguardFiles getDefaultProguardFile(<span class="string">'proguard-android-optimize.txt'</span>), <span class="string">'proguard-rules.pro'</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="配置proguard-rules"><a href="#配置proguard-rules" class="headerlink" title="配置proguard-rules"></a>配置proguard-rules</h2><p>配置该文件是为了防止某些功能的正常使用,代码过于混淆,会导致功能无法使用,这个时候就要保持不被混淆。比较全面的通用配置(常用的第三方框架)如下:</p><figure class="highlight crystal"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Add project specific ProGuard rules here.</span></span><br><span class="line"><span class="comment"># By default, the flags in this file are appended to flags specified</span></span><br><span class="line"><span class="comment"># in E:\AndroidTools\AndroidStudio\sdk/tools/proguard/proguard-android.txt</span></span><br><span class="line"><span class="comment"># You can edit the include path and order by changing the proguardFiles</span></span><br><span class="line"><span class="comment"># directive in build.gradle.</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># For more details, see</span></span><br><span class="line"><span class="comment"># http://developer.android.com/guide/developing/tools/proguard.html</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Add any project specific keep options here:</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">###-----------指定代码的压缩级别------------</span></span><br><span class="line">-optimizationpasses <span class="number">5</span></span><br><span class="line"><span class="comment">###-----------是否使用大小写混合------------</span></span><br><span class="line">-dontusemixedcaseclassnames</span><br><span class="line"><span class="comment">###-----------混淆时是否做预校验------------</span></span><br><span class="line">-dontpreverify</span><br><span class="line"><span class="comment">###-----------混淆时是否记录日志------------</span></span><br><span class="line">-verbose</span><br><span class="line"><span class="comment">###-----------忽略警告------------</span></span><br><span class="line">-ignorewarnings</span><br><span class="line">-keepattributes EnclosingMethod</span><br><span class="line"></span><br><span class="line"><span class="comment">###-----------保证异常时显示行号------------</span></span><br><span class="line">-renamesourcefileattribute SourceFile</span><br><span class="line">-keepattributes SourceFile,LineNumberTable</span><br><span class="line"></span><br><span class="line"><span class="comment">###-----------注解------------</span></span><br><span class="line">-keepattributes *Annotation*</span><br><span class="line"></span><br><span class="line"><span class="comment">###-----------泛型------------</span></span><br><span class="line">-keepattributes Signature</span><br><span class="line"></span><br><span class="line"><span class="comment">###-----------异常------------</span></span><br><span class="line">-keepattributes Exceptions</span><br><span class="line"></span><br><span class="line"><span class="comment">###-----------去掉代码里的Log------------</span></span><br><span class="line">-assumenosideeffects <span class="class"><span class="keyword">class</span> <span class="title">android</span>.<span class="title">util</span>.<span class="title">Log</span> {</span></span><br><span class="line"> public static boolean isLoggable(java.lang.String,int);</span><br><span class="line"> public static *** d(...);</span><br><span class="line">public static *** v(...);</span><br><span class="line">public static *** i(...);</span><br><span class="line">public static *** w(...);</span><br><span class="line">public static *** e(...);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">###-----------混淆时所采用的算法------------</span></span><br><span class="line">-optimizations !code/simplification/arithmetic,!field/*,!<span class="class"><span class="keyword">class</span>/<span class="title">merging</span>/*</span></span><br><span class="line"></span><br><span class="line"><span class="comment">###-----------保持Activity类不被混淆------------</span></span><br><span class="line">-keep public <span class="class"><span class="keyword">class</span> * <span class="title">extends</span> <span class="title">android</span>.<span class="title">app</span>.<span class="title">Activity</span></span></span><br><span class="line"><span class="comment">###-----------保持AppCompatActivity类不被混淆------------</span></span><br><span class="line">-keep public <span class="class"><span class="keyword">class</span> * <span class="title">extends</span> <span class="title">android</span>.<span class="title">support</span>.<span class="title">v7</span>.<span class="title">app</span>.<span class="title">AppCompatActivity</span></span></span><br><span class="line"><span class="comment">###-----------保持DialogFragment类不被混淆------------</span></span><br><span class="line">-keep public <span class="class"><span class="keyword">class</span> * <span class="title">extends</span> <span class="title">android</span>.<span class="title">app</span>.<span class="title">DialogFragment</span></span></span><br><span class="line"><span class="comment">###-----------保持Application类不被混淆------------</span></span><br><span class="line">-keep public <span class="class"><span class="keyword">class</span> * <span class="title">extends</span> <span class="title">android</span>.<span class="title">app</span>.<span class="title">Application</span></span></span><br><span class="line"><span class="comment">###-----------保持Service类不被混淆------------</span></span><br><span class="line">-keep public <span class="class"><span class="keyword">class</span> * <span class="title">extends</span> <span class="title">android</span>.<span class="title">app</span>.<span class="title">Service</span></span></span><br><span class="line"><span class="comment">###-----------保持BroadcastReceiver类不被混淆------------</span></span><br><span class="line">-keep public <span class="class"><span class="keyword">class</span> * <span class="title">extends</span> <span class="title">android</span>.<span class="title">content</span>.<span class="title">BroadcastReceiver</span></span></span><br><span class="line"><span class="comment">###-----------保持ContentProvider类不被混淆------------</span></span><br><span class="line">-keep public <span class="class"><span class="keyword">class</span> * <span class="title">extends</span> <span class="title">android</span>.<span class="title">content</span>.<span class="title">ContentProvider</span></span></span><br><span class="line"><span class="comment">###-----------保持BackupAgentHelper类不被混淆------------</span></span><br><span class="line">-keep public <span class="class"><span class="keyword">class</span> * <span class="title">extends</span> <span class="title">android</span>.<span class="title">app</span>.<span class="title">backup</span>.<span class="title">BackupAgentHelper</span></span></span><br><span class="line"><span class="comment">###-----------保持Preference类不被混淆------------</span></span><br><span class="line">-keep public <span class="class"><span class="keyword">class</span> * <span class="title">extends</span> <span class="title">android</span>.<span class="title">preference</span>.<span class="title">Preference</span></span></span><br><span class="line"><span class="comment">###-----------保持ILicensingService类不被混淆------------</span></span><br><span class="line">-keep public <span class="class"><span class="keyword">class</span> <span class="title">com</span>.<span class="title">android</span>.<span class="title">vending</span>.<span class="title">licensing</span>.<span class="title">ILicensingService</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">###-----------保持 native 方法不被混淆------------</span></span><br><span class="line">-keepclasseswithmembernames <span class="class"><span class="keyword">class</span> * {</span></span><br><span class="line"> native <methods>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">###-----------保持自定义控件类不被混淆------------</span></span><br><span class="line">-keepclasseswithmembers <span class="class"><span class="keyword">class</span> * {</span></span><br><span class="line"> public <init>(android.content.Context, android.util.AttributeSet);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">###-----------保持自定义控件类不被混淆------------</span></span><br><span class="line">-keepclasseswithmembers <span class="class"><span class="keyword">class</span> * {</span></span><br><span class="line"> public <init>(android.content.Context, android.util.AttributeSet, int);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">###-----------保持自定义控件类不被混淆------------</span></span><br><span class="line">-keepclassmembers <span class="class"><span class="keyword">class</span> * <span class="title">extends</span> <span class="title">android</span>.<span class="title">app</span>.<span class="title">Activity</span> {</span></span><br><span class="line"> public void *(android.view.View);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">###-----------保持枚举 enum 类不被混淆------------</span></span><br><span class="line">-keepclassmembers <span class="class"><span class="keyword">enum</span> * {</span></span><br><span class="line"> public static **[] values();</span><br><span class="line"> public static ** valueOf(java.lang.String);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">###-----------# 保持 Parcelable 不被混淆------------</span></span><br><span class="line">-keep <span class="class"><span class="keyword">class</span> * <span class="title">implements</span> <span class="title">android</span>.<span class="title">os</span>.<span class="title">Parcelable</span> {</span></span><br><span class="line"> public static final android.os.Parcelable$Creator *;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">###-----------保持 retrofit 不被混淆------------</span></span><br><span class="line">-dontwarn retrofit2.**</span><br><span class="line">-keep <span class="class"><span class="keyword">class</span> <span class="title">retrofit2</span>.** { *;</span> }</span><br><span class="line">-dontwarn javax.<span class="keyword">annotation</span>.**</span><br><span class="line"></span><br><span class="line"><span class="comment">###-----------保持 okhttp 不被混淆------------</span></span><br><span class="line">-dontwarn com.squareup.okhttp3.**</span><br><span class="line">-keep <span class="class"><span class="keyword">class</span> <span class="title">com</span>.<span class="title">squareup</span>.<span class="title">okhttp3</span>.** { *;</span>}</span><br><span class="line">-dontwarn okio.**</span><br><span class="line"></span><br><span class="line"><span class="comment">###-----------保持 GreenDao 不被混淆------------</span></span><br><span class="line">-keepclassmembers <span class="class"><span class="keyword">class</span> * <span class="title">extends</span> <span class="title">org</span>.<span class="title">greenrobot</span>.<span class="title">greendao</span>.<span class="title">AbstractDao</span> {</span></span><br><span class="line">public static java.lang.String TABLENAME;</span><br><span class="line">}</span><br><span class="line">-keep <span class="class"><span class="keyword">class</span> **$<span class="title">Properties</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">###-----------保持 Zbar 不被混淆------------</span></span><br><span class="line">-keep <span class="class"><span class="keyword">class</span> <span class="title">net</span>.<span class="title">sourceforge</span>.<span class="title">zbar</span>.** { *;</span> }</span><br><span class="line">-keep interface net.sourceforge.zbar.** { *; }</span><br><span class="line">-dontwarn net.sourceforge.zbar.**</span><br><span class="line"></span><br><span class="line"><span class="comment">###-----------保持 Glide 不被混淆------------</span></span><br><span class="line">-keep public <span class="class"><span class="keyword">class</span> * <span class="title">implements</span> <span class="title">com</span>.<span class="title">bumptech</span>.<span class="title">glide</span>.<span class="title">module</span>.<span class="title">GlideModule</span></span></span><br><span class="line">-keep public <span class="class"><span class="keyword">class</span> * <span class="title">extends</span> <span class="title">com</span>.<span class="title">bumptech</span>.<span class="title">glide</span>.<span class="title">module</span>.<span class="title">AppGlideModule</span></span></span><br><span class="line">-keep public <span class="class"><span class="keyword">enum</span> <span class="title">com</span>.<span class="title">bumptech</span>.<span class="title">glide</span>.<span class="title">load</span>.<span class="title">resource</span>.<span class="title">bitmap</span>.<span class="title">ImageHeaderParser</span>$** {</span></span><br><span class="line"> **[] $VALUES;</span><br><span class="line"> public *;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">###-----------保持 eventbus 不被混淆------------</span></span><br><span class="line">-keepattributes *Annotation*</span><br><span class="line">-keepclassmembers <span class="class"><span class="keyword">class</span> ** {</span></span><br><span class="line"> @org.greenrobot.eventbus.Subscribe <methods>;</span><br><span class="line">}</span><br><span class="line">-keep <span class="class"><span class="keyword">enum</span> <span class="title">org</span>.<span class="title">greenrobot</span>.<span class="title">eventbus</span>.<span class="title">ThreadMode</span> { *;</span> }</span><br><span class="line"><span class="comment"># Only required if you use AsyncExecutor</span></span><br><span class="line">-keepclassmembers <span class="class"><span class="keyword">class</span> * <span class="title">extends</span> <span class="title">org</span>.<span class="title">greenrobot</span>.<span class="title">eventbus</span>.<span class="title">util</span>.<span class="title">ThrowableFailureEvent</span> {</span></span><br><span class="line"> <init>(java.lang.Throwable);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">###-----------保持 MPAndroidChart 不被混淆------------</span></span><br><span class="line">-keep <span class="class"><span class="keyword">class</span> <span class="title">com</span>.<span class="title">github</span>.<span class="title">mikephil</span>.<span class="title">charting</span>.** { *;</span> }</span><br><span class="line"></span><br><span class="line"><span class="comment">###-----------保持 bugly 不被混淆------------</span></span><br><span class="line">-dontwarn com.tencent.bugly.**</span><br><span class="line">-keep public <span class="class"><span class="keyword">class</span> <span class="title">com</span>.<span class="title">tencent</span>.<span class="title">bugly</span>.**{*;</span>}</span><br><span class="line"></span><br><span class="line"><span class="comment">###-----------保持 gson 不被混淆------------</span></span><br><span class="line">-keep <span class="class"><span class="keyword">class</span> <span class="title">sun</span>.<span class="title">misc</span>.<span class="title">Unsafe</span> { *;</span> }</span><br><span class="line">-keep <span class="class"><span class="keyword">class</span> <span class="title">com</span>.<span class="title">google</span>.<span class="title">gson</span>.<span class="title">stream</span>.** { *;</span> }</span><br><span class="line"></span><br><span class="line"><span class="comment">###-----------保持 Rxjava RxAndroid 不被混淆------------</span></span><br><span class="line">-dontwarn sun.misc.**</span><br><span class="line">-keepclassmembers <span class="class"><span class="keyword">class</span> <span class="title">rx</span>.<span class="title">internal</span>.<span class="title">util</span>.<span class="title">unsafe</span>.*<span class="title">ArrayQueue</span>*<span class="title">Field</span>* {</span></span><br><span class="line"> long producerIndex;</span><br><span class="line"> long consumerIndex;</span><br><span class="line">}</span><br><span class="line">-keepclassmembers <span class="class"><span class="keyword">class</span> <span class="title">rx</span>.<span class="title">internal</span>.<span class="title">util</span>.<span class="title">unsafe</span>.<span class="title">BaseLinkedQueueProducerNodeRef</span> {</span></span><br><span class="line"> rx.internal.util.atomic.LinkedQueueNode producerNode;</span><br><span class="line">}</span><br><span class="line">-keepclassmembers <span class="class"><span class="keyword">class</span> <span class="title">rx</span>.<span class="title">internal</span>.<span class="title">util</span>.<span class="title">unsafe</span>.<span class="title">BaseLinkedQueueConsumerNodeRef</span> {</span></span><br><span class="line"> rx.internal.util.atomic.LinkedQueueNode consumerNode;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">###-----------保持 volley 不被混淆------------</span></span><br><span class="line">-keep <span class="class"><span class="keyword">class</span> <span class="title">com</span>.<span class="title">android</span>.<span class="title">volley</span>.** { *;</span> }</span><br><span class="line">-keep <span class="class"><span class="keyword">class</span> <span class="title">com</span>.<span class="title">android</span>.<span class="title">volley</span>.<span class="title">toolbox</span>.** { *;</span> }</span><br><span class="line"></span><br><span class="line"><span class="comment">###-----------保持 本项目的gson实体类 不被混淆------------</span></span><br><span class="line"><span class="comment">###-----------网络请求解析的实体类一定不要混淆------------</span></span><br><span class="line">-keep <span class="class"><span class="keyword">class</span> <span class="title">your</span>-<span class="title">package</span>.<span class="title">entity</span>.** { *;</span> }</span><br><span class="line"></span><br><span class="line"><span class="comment"># If your project uses WebView with JS, uncomment the following</span></span><br><span class="line"><span class="comment"># and specify the fully qualified class name to the JavaScript interface</span></span><br><span class="line"><span class="comment"># class:</span></span><br><span class="line"><span class="comment">#-keepclassmembers class fqcn.of.javascript.interface.for.webview {</span></span><br><span class="line"><span class="comment"># public *;</span></span><br><span class="line"><span class="comment">#}</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Uncomment this to preserve the line number information for</span></span><br><span class="line"><span class="comment"># debugging stack traces.</span></span><br><span class="line"><span class="comment">#-keepattributes SourceFile,LineNumberTable</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># If you keep the line number information, uncomment this to</span></span><br><span class="line"><span class="comment"># hide the original source file name.</span></span><br><span class="line"><span class="comment">#-renamesourcefileattribute SourceFile</span></span><br></pre></td></tr></tbody></table></figure><h2 id="Proguard常用语法"><a href="#Proguard常用语法" class="headerlink" title="Proguard常用语法"></a><a href="http://www.trinea.cn/android/proguard-grammar/" target="_blank" rel="noopener">Proguard常用语法</a></h2><ul><li>关键字</li></ul><table><thead><tr><th>关键字</th><th>描述</th></tr></thead><tbody><tr><td>keep</td><td>保留类和类中的成员,防止它们被移除或被重命名</td></tr><tr><td>keepnames</td><td>保留类和类中的成员,防止它们被重命名,但当成员没有被引用时会被移除</td></tr><tr><td>keepclassmembers</td><td>只保留类中的成员,防止它们被移除或者被重命名</td></tr><tr><td>keepclassmembernames</td><td>只保留类中的成员,防止它们被重命名,但当成员没有被引用时会被移除</td></tr><tr><td>keepclasseswithmembers</td><td>防止<strong>拥有该成员的</strong>类和成员被移除或被重命名,前提是指明的类中的成员必须存在,如果不存在则还是会混淆</td></tr><tr><td>keepclasseswithmembernames</td><td>防止<strong>拥有该成员的</strong>类和成员被重命名,但当成员没有被引用时会被移除,前提是指明的类中的成员必须存在,如果不存在则还是会混淆</td></tr></tbody></table><ul><li>通配符</li></ul><table><thead><tr><th>通配符</th><th>描述</th></tr></thead><tbody><tr><td><code><field></code></td><td>匹配类中的所有字段</td></tr><tr><td><code><method></code></td><td>匹配类中的所有方法</td></tr><tr><td><code><init></code></td><td>匹配类中的所有构造函数</td></tr><tr><td><code>*</code></td><td><code>匹配任意长度字符,但不含包名分隔符(.);如完整包名:com.example.test.util,使用com.*、com.example.*都是无法匹配的,因为*无法匹配包名中的分隔符,正确的匹配方式是com.example.*.*、com.example.test.*;如果你不写任何其他内容,只有一个*,则表示匹配所有的字符</code></td></tr><tr><td><code>**</code></td><td>匹配任意长度字符,并且包含包名分隔符(.);如混淆文件里的:dontwarn android.support.**就可以匹配android.support包下的所有内容,包括任意长度的子包</td></tr><tr><td><code>***</code></td><td>匹配任意参数类型;如<code>void set(***)</code>匹配任意传入的参数类型,<code>*** get()</code>就能匹配任意返回值的类型</td></tr><tr><td><code>...</code></td><td>匹配任意长度的任意类型参数;如void test(…)匹配<code>void test(String a)</code>或<code>void test(int a, String b)</code>等等</td></tr></tbody></table><ul><li>常见规则</li></ul><p>形如:<br></p><figure class="highlight markdown"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[<span class="string">关键字</span>][<span class="symbol">类</span>]{</span><br><span class="line"><span class="code">[成员]</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p></p><p>包:<code>com.example.test</code><br>类:A</p><ul><li><p>不混淆某个类</p><figure class="highlight actionscript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">-keep <span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">com</span>.<span class="title">example</span>.<span class="title">test</span>.<span class="title">A</span> </span>{ *; }</span><br></pre></td></tr></tbody></table></figure></li><li><p>不混淆某个包下所有的类</p><figure class="highlight haml"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">-<span class="ruby">keep <span class="class"><span class="keyword">class</span> <span class="title">com</span>.<span class="title">example</span>.<span class="title">test</span>.**{ *;</span> }</span></span><br></pre></td></tr></tbody></table></figure></li><li><p>不混淆某个类的子类</p><figure class="highlight scala"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">-keep public <span class="class"><span class="keyword">class</span> <span class="title">*</span> <span class="keyword">extends</span> <span class="title">com</span>.<span class="title">example</span>.<span class="title">test</span>.<span class="title">A</span> </span>{ *; }</span><br></pre></td></tr></tbody></table></figure></li><li><p>不混淆所有类名中包含了”关键字”的类及成员</p><figure class="highlight actionscript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">-keep <span class="keyword">public</span> <span class="class"><span class="keyword">class</span> **.*<span class="title">model</span>*.** </span>{ *; }</span><br></pre></td></tr></tbody></table></figure></li><li><p>不混淆某个接口</p><figure class="highlight actionscript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">-keep <span class="class"><span class="keyword">class</span> * <span class="keyword">implements</span> <span class="title">com</span>.<span class="title">example</span>.<span class="title">test</span>.<span class="title">Interface</span> </span>{ *; }</span><br></pre></td></tr></tbody></table></figure></li><li><p>不混淆某个类的构造方法</p><figure class="highlight kotlin"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">-keepclassmembers <span class="class"><span class="keyword">class</span> <span class="title">com</span>.<span class="title">example</span>.<span class="title">test</span>.<span class="title">A</span> </span>{</span><br><span class="line"><span class="keyword">public</span> <<span class="keyword">init</span>>();</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li><p>不混淆某个类的特定的方法</p><figure class="highlight cpp"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">-keepclassmembers <span class="class"><span class="keyword">class</span> <span class="title">com</span>.<span class="title">example</span>.<span class="title">test</span>.<span class="title">A</span> {</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test</span><span class="params">(java.lang.String)</span></span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li><p>不混淆某个类的内部类</p><figure class="highlight stata"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">-<span class="keyword">keep</span> <span class="keyword">class</span> com.example.<span class="keyword">test</span>.A$* {</span><br><span class="line"><span class="comment">*;</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><h2 id="混淆注意"><a href="#混淆注意" class="headerlink" title="混淆注意"></a>混淆注意</h2><p>代码开启混淆之后,调试app或者遇到app异常时,打印里面显示的则是<code>a b c...</code>这种的异常,定位不到异常的位置,这个时候在目录<code>build\outputs\mapping\release</code>里,使用mapping.txt,结合SDK里的工具<br><code>sdk\tools\proguard\bin\proguardgui.bat</code>->ReTrace,进行定位异常,基本上可以定位,然后解决异常错误。</p><h1 id="加壳"><a href="#加壳" class="headerlink" title="加壳"></a>加壳</h1><p>加壳是在二进制的程序中植入一段代码,在运行的时候优先取得程序的控制权,做一些额外的工作。是应用加固的一种手法对原始二进制原文进行加密/隐藏/混淆。加壳的程序可以有效阻止对程序的反汇编分析,常用来保护软件版权,防止被软件破解。</p><h2 id="Android-Dex文件加壳原理"><a href="#Android-Dex文件加壳原理" class="headerlink" title="Android Dex文件加壳原理"></a>Android Dex文件加壳原理</h2><p>Android Dex文件大量使用引用给加壳带来了一定的难度,但是从理论上讲,Android APK加壳也是可行的。</p><ul><li><p>加壳程序:加密源程序为解壳数据、组装解壳程序和解壳数据</p></li><li><p>解壳程序:解密解壳数据,并运行时通过DexClassLoader动态加载</p></li><li><p>源程序:需要加壳处理的被保护代码</p></li></ul><h2 id="加壳的利与弊"><a href="#加壳的利与弊" class="headerlink" title="加壳的利与弊"></a>加壳的利与弊</h2><h3 id="优势"><a href="#优势" class="headerlink" title="优势"></a>优势</h3><ul><li><p>保护自己核心代码算法,提高破解/盗版/二次打包的难度</p></li><li><p>还可以缓解代码注入/动态调试/内存注入攻击</p></li></ul><h3 id="劣势"><a href="#劣势" class="headerlink" title="劣势"></a>劣势</h3><ul><li><p>影响兼容性</p></li><li><p>影响程序运行效率</p></li></ul><h2 id="常用的加壳方式"><a href="#常用的加壳方式" class="headerlink" title="常用的加壳方式"></a>常用的加壳方式</h2><ul><li><a href="https://cloud.tencent.com/product/ms?idx=2" target="_blank" rel="noopener">腾讯乐固</a></li></ul><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8D%E8%97%8F%E5%9C%A8%E5%A2%99%E4%B8%AD%E7%9A%84android.jpg" alt="Android" title="藏在墙中的android"></p>
<p><strong>反编译并不是为了去破解人家辛辛苦苦开发的app;混淆与加壳也是为了在一定程度上保护自己的开发成果。反编译、混淆、加壳是非常有用的技能。</strong></p>
</summary>
<category term="Android联盟" scheme="https://tiimor.cn/categories/Android%E8%81%94%E7%9B%9F/"/>
<category term="Android" scheme="https://tiimor.cn/tags/Android/"/>
<category term="混淆" scheme="https://tiimor.cn/tags/%E6%B7%B7%E6%B7%86/"/>
<category term="反编译" scheme="https://tiimor.cn/tags/%E5%8F%8D%E7%BC%96%E8%AF%91/"/>
<category term="加壳" scheme="https://tiimor.cn/tags/%E5%8A%A0%E5%A3%B3/"/>
</entry>
<entry>
<title>Android图表库-MPAndroidChart</title>
<link href="https://tiimor.cn/Android%E5%9B%BE%E8%A1%A8%E5%BA%93-MPAndroidChart/"/>
<id>https://tiimor.cn/Android图表库-MPAndroidChart/</id>
<published>2017-12-10T06:29:46.000Z</published>
<updated>2019-03-17T06:47:42.363Z</updated>
<content type="html"><![CDATA[<p><img src="http://cdn.tiimor.cn/images/MPAndroidChart.png" alt="Android" title="MPAndroidChart"></p><p><strong><a href="https://github.com/PhilJay/MPAndroidChart" target="_blank" rel="noopener">MPAndroidChart</a>图表库的使用</strong></p><a id="more"></a><p>MPAndroidChart是一款基于Android的开源图表库,MPAndroidChart不仅可以在Android设备上绘制各种统计图表,而且可以对图表进行拖动和缩放操作,应用起来非常灵活。MPAndroidChart显得更为轻巧和简单,拥有常用的图表类型:线型图、饼图、柱状图和散点图。</p><h1 id="核心功能"><a href="#核心功能" class="headerlink" title="核心功能"></a>核心功能</h1><ul><li>支持x,y轴缩放</li><li>支持拖拽</li><li>支持手指滑动</li><li>支持高亮显示</li><li>支持保存图表到文件中</li><li>支持从文件(txt)中读取数据</li><li>预先定义颜色模板</li><li>自动生成标注</li><li>支持自定义x,y轴的显示标签</li><li>支持x,y轴动画</li><li>支持x,y轴设置最大值和附加信息</li><li>支持自定义字体,颜色,背景,手势,虚线等</li></ul><h1 id="图表类型"><a href="#图表类型" class="headerlink" title="图表类型"></a>图表类型</h1><ul><li><p>LineChart (with legend, simple design)<br><img src="http://cdn.tiimor.cn/images/LineChart1.png" alt="LineChart1"></p></li><li><p>LineChart (with legend, simple design)<br><img src="http://cdn.tiimor.cn/images/LineChart2.png" alt="LineChart2"></p></li><li><p>LineChart (cubic lines)<br><img src="http://cdn.tiimor.cn/images/LineChart3.png" alt="LineChart3"></p></li><li><p>LineChart (gradient fill)<br><img src="http://cdn.tiimor.cn/images/LineChart4.png" alt="LineChart4"></p></li><li><p>Combined-Chart (bar- and linechart in this case)<br><img src="http://cdn.tiimor.cn/images/Combined-Chart.png" alt="Combined-Chart"></p></li><li><p>BarChart (with legend, simple design)<br><img src="http://cdn.tiimor.cn/images/BarChart1.png" alt="BarChart1"></p></li><li><p>BarChart (grouped DataSets)<br><img src="http://cdn.tiimor.cn/images/BarChart2.png" alt="BarChart2"></p></li><li><p>Horizontal-BarChart<br><img src="http://cdn.tiimor.cn/images/Horizontal-BarChart.png" alt="Horizontal-BarChart"></p></li><li><p>PieChart (with selection, …)<br><img src="http://cdn.tiimor.cn/images/PieChart.png" alt="PieChart"></p></li><li><p>ScatterChart (with squares, triangles, circles, … and more)<br><img src="http://cdn.tiimor.cn/images/ScatterChart.png" alt="ScatterChart"></p></li><li><p>CandleStickChart (for financial data)<br><img src="http://cdn.tiimor.cn/images/CandleStickChart.png" alt="CandleStickChart"></p></li><li><p>BubbleChart (area covered by bubbles indicates the yValue)<br><img src="http://cdn.tiimor.cn/images/BubbleChart.png" alt="BubbleChart"></p></li><li><p>RadarChart (spider web chart)<br><img src="http://cdn.tiimor.cn/images/RadarChart.png" alt="RadarChart"></p></li></ul><h1 id="图表绘制"><a href="#图表绘制" class="headerlink" title="图表绘制"></a>图表绘制</h1><h2 id="XY轴绘制"><a href="#XY轴绘制" class="headerlink" title="XY轴绘制"></a>XY轴绘制</h2><ul><li><code>setEnabled(boolean enabled)</code>:设置轴是否被绘制。默认绘制,false不会被绘制。</li><li><code>setDrawLabels(boolean enabled)</code>:设置为true打开绘制轴的标签。</li><li><code>setDrawAxisLine(boolean enabled)</code>:设置为true,绘制轴线</li><li><code>setDrawGridLines(boolean enabled)</code>:设置为true绘制网格线。</li></ul><h2 id="定义轴线样式"><a href="#定义轴线样式" class="headerlink" title="定义轴线样式"></a>定义轴线样式</h2><ul><li><code>setTextColor(int color)</code>:设置轴标签文本颜色。</li><li><code>setTextSize(float size)</code>:设置轴标签的字体大小。</li><li><code>setTypeface(Typeface tf)</code>:设置轴标签的自定义Typeface</li><li><code>setGridColor(int color)</code>:设置网格线颜色。</li><li><code>setGridLineWidth(float width)</code>:设置网格线宽度。</li><li><code>setAxisLineColor(int color)</code>:设置此轴的坐标轴的颜色。</li><li><code>setAxisLineWidth(float width)</code>:设置此轴的坐标轴的宽度。</li><li><code>setVisibleXRangeMaximum(float maxXRange)</code>:设置x轴最多显示数据条数,(要在设置数据源后调用,否则是无效的)</li><li><code>enableGridDashedLine(float lineLength, float spaceLength, float phase)</code>:显示网格线虚线模式,“lineLength”控制短线条的长度,“spaceLength”控制两段线之间的间隔长度,“phase”控制开始的点。</li></ul><h2 id="自定义轴线的值"><a href="#自定义轴线的值" class="headerlink" title="自定义轴线的值"></a>自定义轴线的值</h2><ul><li><code>setAdjustXLabels(boolean enabled)</code>:如果被设置为true,x轴条目将依赖于它自己在进行缩放的时候。如果设置为false,x轴条目将总是保持相同。</li><li><code>setAvoidFirstLastClipping(boolean enabled)</code>:如果设置为true,图表将避免第一个和最后一个标签条目被减掉在图表或屏幕的边缘。</li><li><code>setSpaceBetweenLabels(int characters)</code>:设置x轴标签之间的空间字符数,默认是4个。</li><li><code>setPosition(XAxisPosition pos)</code>:设置XAxis应该出现的位置。可以选择TOP,BOTTOM,BOTH_SIDED,TOP_INSIDE或者BOTTOM_INSIDE。</li><li><code>setStartAtZero(boolean enabled)</code>:如果这个打开,轴线总是有最小值0,无论什么类型的图表被展示。</li><li><code>setAxisMaxValue(float max)</code>:设置一个自定义的最大值为这条轴,如果设置了,这个值将不会依赖于提供的数据自动计算。</li><li><code>resetAxisMaxValue()</code>:调用这个将撤销以前设置的最大值。这意味着,你将再次允许轴自动计算它的最大值。</li><li><code>setAxisMinValue(float min)</code>:设置一个自定义的最小值。如果设置了,这个值将不会依赖于你提供的数据进行自动计算。</li><li><code>resetAxisMinValue()</code>:调用这个方法撤销以前设置的最小值。这意味着,你将再次允许轴自动计算他的最小值。</li><li><code>setInverted(boolean enabled)</code>:如果设置为true,这个轴将被反向,那意味着最高出的将到底部,最低部的到顶端。</li><li><code>setSpaceTop(float percent)</code>:设置在图表上最高处的值相比轴上最高值的顶端空间(总轴范围的百分比)</li><li><code>setSpaceBottom(float percent)</code>:设置在图表上最低处的值相比轴上最低处值的底部空间(总轴范围的百分比)</li><li><code>setShowOnlyMinMax(boolean enabled)</code>:如果打开了,这个轴将展示出它的最小值和最大值。这将忽略或者覆盖定义过的label-count。</li><li><code>setPosition(YAxisLabelPosition pos)</code>:设置轴标签应该被绘制的位置。INSIDE_CHART或者OUTSIDE_CHART中的一个。 自定义影响轴的数值范围应该在图表被设置数据之前应用。</li></ul><h2 id="图表样式"><a href="#图表样式" class="headerlink" title="图表样式"></a>图表样式</h2><p>一些样式相关方法,可以直接使用<br>有关更详尽单独图表类型的样式和设置,请看看具体的图表设置的wiki页面<a href="https://github.com/PhilJay/MPAndroidChart/wiki/Specific-chart-settings" target="_blank" rel="noopener">Specific-chart-settings</a></p><ul><li><code>setBackgroundColor(int color)</code>:设置整个图表视图的背景</li><li><code>setDescription(String desc)</code>:右下角对图表的描述信息</li><li><code>setDescriptionColor(int color)</code>:描述信息的颜色</li><li><code>setDescriptionPosition(float x, float y)</code>:自定义描述信息位置.</li><li><code>setDescriptionTypeface(Typeface t)</code>:自定义描述信息字体</li><li><code>setDescriptionTextSize(float size)</code>:自定义描述信息字体大小, 最小值6f, 最大值16f.</li><li><code>setNoDataTextDescription(String desc)</code>:设置空表的描述信息</li><li><code>setDrawGridBackground(boolean enabled)</code>:是否绘制网格背景</li><li><code>setGridBackgroundColor(int color)</code>:设置网格背景颜色</li><li><code>setDrawBorders(boolean enabled)</code>:是否绘制边线</li><li><code>setBorderColor(int color)</code>:边线颜色</li><li><code>setBorderWidth(float width)</code>:边线宽度,单位dp</li><li><code>setMaxVisibleValueCount(int count)</code>:设置图表绘制可见标签数量最大值. 仅在setDrawValues() 启用时生效</li></ul><h2 id="打印日志"><a href="#打印日志" class="headerlink" title="打印日志"></a>打印日志</h2><ul><li><code>setLogEnabled(boolean enabled)</code>:设置为true会激活log输出。使用这种log会对性能造成影响,没有必要用的话关掉它。</li></ul><h2 id="刷新"><a href="#刷新" class="headerlink" title="刷新"></a>刷新</h2><ul><li><code>invalidate()</code>:这个方法能使图表重绘.要使图表更改生效,这个方法是必要的。</li><li><code>notifyDataSetChanged()</code>:让图表知道它的基础数据发生更改,并执行所有必要的重新计算(offsets, legend, maxima, minima, …)。 动态添加数据时,这是必须调用的。</li></ul><h1 id="图表的交互"><a href="#图表的交互" class="headerlink" title="图表的交互"></a>图表的交互</h1><h2 id="启用-禁用交互"><a href="#启用-禁用交互" class="headerlink" title="启用/禁用交互"></a>启用/禁用交互</h2><ul><li><code>setTouchEnabled(boolean enabled)</code>:启用图表触摸事件</li><li><code>setDragEnabled(boolean enabled)</code>:启用图表拖拽事件</li><li><code>setScaleEnabled(boolean enabled)</code>:启用图表缩放事件</li><li><code>setScaleXEnabled(boolean enabled)</code>:启用X轴上的缩放</li><li><code>setScaleYEnabled(boolean enabled)</code>:启用Y轴上的缩放</li><li><code>setPinchZoom(boolean enabled)</code>:XY同时缩放</li><li><code>setDoubleTapToZoomEnabled(boolean enabled)</code>:启用双击缩放</li><li><code>setHighlightPerDragEnabled(boolean enabled)</code>:拖拽超过图标绘制画布时高亮显示</li><li><code>setHighlightPerTapEnabled(boolean enabled)</code>:双击高亮显示</li></ul><h2 id="图表的减速器"><a href="#图表的减速器" class="headerlink" title="图表的减速器"></a>图表的减速器</h2><ul><li><code>setDragDecelerationEnabled(boolean enabled)</code>:抬起之后继续滚动</li><li><code>setDragDecelerationFrictionCoef(float coef)</code>: 减速插值,取值范围[0,1)。0表示立停止。值越大速度下降越缓慢</li></ul><h2 id="高亮方式"><a href="#高亮方式" class="headerlink" title="高亮方式"></a>高亮方式</h2><ul><li><code>highlightValues(Highlight[] highs)</code>:高亮点的集合,如果为空,全部不高亮</li><li><code>highlightValue(int xIndex, int dataSetIndex)</code>:x轴上的数据集合高亮。如果为-1,全部不高两</li><li><code>getHighlighted()</code>:获取高亮点的集合,高亮显示使用OnChartValueSelectedListener不会生成一个回调。可以通过ChartData或DataSet对象启用和禁用高亮显示。</li></ul><h2 id="自定义高亮符号"><a href="#自定义高亮符号" class="headerlink" title="自定义高亮符号"></a>自定义高亮符号</h2><p>所有的用户输入在内部被默认ChartHighlighter类处理。它可以用下面的方法自定义实现替换默认highligher:</p><ul><li><code>setHighlighter(ChartHighlighter highlighter)</code>:通过继承ChartHighlighter类实现自定义高亮符号。通过setHighlighter设置点击等操作高亮显示的符号</li></ul><h2 id="选择回调"><a href="#选择回调" class="headerlink" title="选择回调"></a>选择回调</h2><ul><li><code>OnChartValueSelectedListener</code>:触摸高亮值时回调</li></ul><h2 id="手势回调"><a href="#手势回调" class="headerlink" title="手势回调"></a>手势回调</h2><ul><li><code>OnChartGestureListener</code>:这个回调可以定制手势操作相关回调。注意该手势的缩放是对图表的Matrix缩放,并不改变X轴的值,如果想实现X轴数值变化缩放,如天月年的切换,需要借助手势类实现。</li></ul><h1 id="主要图表类"><a href="#主要图表类" class="headerlink" title="主要图表类"></a>主要图表类</h1><h2 id="轴AxisBase"><a href="#轴AxisBase" class="headerlink" title="轴AxisBase"></a>轴AxisBase</h2><p>AxisBase 这个类,他是XAxis 和YAxis的基类</p><h2 id="X轴XAxis"><a href="#X轴XAxis" class="headerlink" title="X轴XAxis"></a>X轴XAxis</h2><p>XAxis 是AxisBase的子类。<br>XAxis 类是所有的数据和信息的容器与水平轴有关。。XAxis显示什么是交给ChartData对象作为一个ArrayList<string> 或者String[]。<br>XAxis类允许自定义样式和以下部分:<br>水平对齐标签绘制,其中包含轴描述值,为图表X轴提供的数据对象设置。<br>在标签旁边与标签平行绘制了一个“axis-line”。<br>每个在垂直方向坐标轴标签的网格线。</string></p><h2 id="Y轴YAxis"><a href="#Y轴YAxis" class="headerlink" title="Y轴YAxis"></a>Y轴YAxis</h2><p>YAxis 是AxisBase的子类。<br>YAxis 类是与垂直轴相关的所有数据和信息容器,与左边右边垂直的轴相关。RadarChart 只有一个Y轴,默认情况下,图标的两个轴都启用绘制。</p><h2 id="zeroline"><a href="#zeroline" class="headerlink" title="zeroline"></a>zeroline</h2><p>除了网格线,在水平方向Y轴的每个值,有所谓的zeroline,这是在0位置轴线上值绘制的,是类似于网格线,但可以单独配置。</p><h2 id="LimitLine-类"><a href="#LimitLine-类" class="headerlink" title="LimitLine 类"></a>LimitLine 类</h2><p>两轴支持,所谓LimitLines允许显示特殊信息,如边界或限制。LimitLine在水平方向时添加到YAxis,而在垂直方向时添加到XAxis。这是如何从轴添加和删除LimitLines</p><ul><li><code>addLimitLine(LimitLine l)</code>:在轴上添加新的 LimitLine</li><li><code>removeLimitLine(LimitLine l)</code>:从轴上移除 LimitLine</li><li><code>setDrawLimitLinesBehindData(boolean enabled)</code>:允许控制LimitLines之间的z轴上的实际的数据顺序。如果设置为true,LimitLines在真实数据后边绘制,,否则在上面。默认false</li></ul><h1 id="常见问题"><a href="#常见问题" class="headerlink" title="常见问题"></a>常见问题</h1><h2 id="显示隐藏Y轴线及自定义轴线的显示样式"><a href="#显示隐藏Y轴线及自定义轴线的显示样式" class="headerlink" title="显示隐藏Y轴线及自定义轴线的显示样式"></a>显示隐藏Y轴线及自定义轴线的显示样式</h2><ul><li><code>mChart.getAxisLeft().setEnabled(false)</code>:隐藏Y轴左边轴线,此时标签数字也隐藏</li><li><code>mChart.getAxisRight().setEnabled(false)</code>:隐藏Y轴右边轴线,此时标签数字也隐藏 </li><li><code>mChart.getAxisRight().setDrawAxisLine(false)</code>:如果想隐藏轴线但是想显示数字标签</li></ul><h2 id="Y轴线数据标签怎么自己控制显示个数"><a href="#Y轴线数据标签怎么自己控制显示个数" class="headerlink" title="Y轴线数据标签怎么自己控制显示个数"></a>Y轴线数据标签怎么自己控制显示个数</h2><ul><li><code>mChart.getAxisLeft().setLabelCount(8, false)</code>:此时设置了分8个,可根据自己喜好设置</li></ul><h2 id="设置轴线颜色,宽度等信息"><a href="#设置轴线颜色,宽度等信息" class="headerlink" title="设置轴线颜色,宽度等信息"></a>设置轴线颜色,宽度等信息</h2><figure class="highlight jboss-cli"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">YAxis leftAxis = mChart.getAxisLeft<span class="params">()</span>;</span><br><span class="line"><span class="string">//</span> 显示轴线内部INSIDE_CHART </span><br><span class="line">leftAxis.<span class="keyword">set</span>Position<span class="params">(YAxisLabelPosition.OUTSIDE_CHART)</span>;</span><br><span class="line"><span class="string">//</span> 设置轴线颜色</span><br><span class="line">leftAxis.<span class="keyword">set</span>AxisLineColor<span class="params">(Color.parseColor(“#ff0000”)</span>);</span><br><span class="line"><span class="string">//</span> 设置轴线宽度</span><br><span class="line">leftAxis.<span class="keyword">set</span>AxisLineWidth<span class="params">(1)</span>; </span><br><span class="line"><span class="string">//</span> 设置y轴标签字体大小 </span><br><span class="line">leftAxis.<span class="keyword">set</span>TextSize<span class="params">(20)</span>;</span><br><span class="line"><span class="string">//</span> 设置自定义字体</span><br><span class="line">leftAxis.<span class="keyword">set</span>Typeface<span class="params">()</span>; </span><br><span class="line"><span class="string">//</span> 设置是否显示网格线</span><br><span class="line">leftAxis.<span class="keyword">set</span>DrawGridLines<span class="params">(Boolean)</span>;</span><br></pre></td></tr></tbody></table></figure><h2 id="自定义Y轴方向上的值"><a href="#自定义Y轴方向上的值" class="headerlink" title="自定义Y轴方向上的值"></a>自定义Y轴方向上的值</h2><p>重写ValueFormatter,使用DataSet.setValueFormatter进行设置<br></p><figure class="highlight aspectj"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CustomerValueFormatter</span> <span class="keyword">implements</span> <span class="title">ValueFormatter</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> DecimalFormat mFormat;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">CustomerValueFormatter</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="comment">// 此处是显示数据的方式,显示整型或者小数后面小数位数自己随意确定</span></span><br><span class="line"> mFormat = <span class="keyword">new</span> DecimalFormat(<span class="string">"###,###,###,##0"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="function">String <span class="title">getFormattedValue</span><span class="params">(<span class="keyword">float</span> value, Entry entry, <span class="keyword">int</span> dataSetIndex, ViewPortHandler viewPortHandler)</span> </span>{</span><br><span class="line"><span class="comment">// 数据前或者后可根据自己想要显示的方式添加</span></span><br><span class="line"> <span class="function"><span class="keyword">return</span> mFormat.<span class="title">format</span><span class="params">(value)</span></span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p></p><h2 id="自定义Y轴坐标显示的值"><a href="#自定义Y轴坐标显示的值" class="headerlink" title="自定义Y轴坐标显示的值"></a>自定义Y轴坐标显示的值</h2><p>重写YAxisValueFormatter,使用YAxis.setValueFormatter设置<br></p><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CustomerValueFormatter</span> <span class="keyword">implements</span> <span class="title">YAxisValueFormatter</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> DecimalFormat mFormat;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">CustomerValueFormatter</span><span class="params">()</span> </span>{</span><br><span class="line"> mFormat = <span class="keyword">new</span> DecimalFormat(<span class="string">"###,###,###,##0"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">getFormattedValue</span><span class="params">(<span class="keyword">float</span> value, YAxis yAxis)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"¥"</span>+mFormat.format(value);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p></p><h2 id="自定义X轴坐标显示的值"><a href="#自定义X轴坐标显示的值" class="headerlink" title="自定义X轴坐标显示的值"></a>自定义X轴坐标显示的值</h2><p>重写XAxisValueFormatter,使用XAxis.setValueFormatter设置</p><h2 id="将x轴标签倾斜显示"><a href="#将x轴标签倾斜显示" class="headerlink" title="将x轴标签倾斜显示"></a>将x轴标签倾斜显示</h2><figure class="highlight jboss-cli"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">XAxis xl = mChart.getXAxis<span class="params">()</span>;</span><br><span class="line"><span class="string">//</span> 设置x轴字体显示角度</span><br><span class="line">xl.<span class="keyword">set</span>LabelRotationAngle<span class="params">(-20)</span>;</span><br><span class="line"><span class="string">//</span> 设置X轴的位置TOP, BOTTOM, BOTH_SIDED, TOP_INSIDE, BOTTOM_INSIDE</span><br><span class="line">xl.<span class="keyword">set</span>Position<span class="params">(XAxisPosition.BOTTOM)</span>;</span><br><span class="line"><span class="string">//</span> 设置Lable之间的距离(字符),小于距离将不显示,需要放大图标才能看到</span><br><span class="line">xl.<span class="keyword">set</span>SpaceBetweenLabels<span class="params">(int spaceCharacters)</span></span><br></pre></td></tr></tbody></table></figure><h2 id="设置一页数据点数"><a href="#设置一页数据点数" class="headerlink" title="设置一页数据点数"></a>设置一页数据点数</h2><p><code>setVisibleXRange(float minXRange, float maxXRange)</code></p><h1 id="补充"><a href="#补充" class="headerlink" title="补充"></a>补充</h1><p>更多详细介绍可参考</p><blockquote><ul><li><a href="https://github.com/Android-View/JNChartDemo" target="_blank" rel="noopener">JNChartDemo</a></li><li><a href="https://github.com/Android-View/ChartLib-Demo-Android" target="_blank" rel="noopener">ChartLib-Demo-Android</a></li><li><a href="https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.0.0-beta1/javadoc/" target="_blank" rel="noopener">API</a></li></ul></blockquote><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
<p><img src="http://cdn.tiimor.cn/images/MPAndroidChart.png" alt="Android" title="MPAndroidChart"></p>
<p><strong><a href="https://github.com/PhilJay/MPAndroidChart" target="_blank" rel="noopener">MPAndroidChart</a>图表库的使用</strong></p>
</summary>
<category term="Android联盟" scheme="https://tiimor.cn/categories/Android%E8%81%94%E7%9B%9F/"/>
<category term="Android图表" scheme="https://tiimor.cn/tags/Android%E5%9B%BE%E8%A1%A8/"/>
<category term="MPAndroidChart" scheme="https://tiimor.cn/tags/MPAndroidChart/"/>
</entry>
<entry>
<title>Android数据库加密</title>
<link href="https://tiimor.cn/Android%E6%95%B0%E6%8D%AE%E5%BA%93%E5%8A%A0%E5%AF%86/"/>
<id>https://tiimor.cn/Android数据库加密/</id>
<published>2017-11-05T07:11:51.000Z</published>
<updated>2019-03-17T06:47:42.365Z</updated>
<content type="html"><![CDATA[<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8DTalk%20is%20cheap%EF%BC%8Cshow%20me%20the%20code.jpg" alt="Android" title="Talk is cheap,show me the code"></p><p><strong>Android数据库加密,加密应用里重要的信息,避免干坏事,坏人!</strong></p><a id="more"></a><h1 id="加密方案"><a href="#加密方案" class="headerlink" title="加密方案"></a>加密方案</h1><p>SQLite不支持加密,应用中重要的数据账号密码等容易被泄露。</p><h2 id="加密数据库内容"><a href="#加密数据库内容" class="headerlink" title="加密数据库内容"></a>加密数据库内容</h2><p>在存储数据时加密内容,在查询时进行解密。但是这种方式不能彻底加密,数据库的表结构等信息还是能被查看到,另外检索数据也是一个问题。</p><h2 id="加密数据库文件"><a href="#加密数据库文件" class="headerlink" title="加密数据库文件"></a>加密数据库文件</h2><p>借助<a href="https://github.com/sqlcipher/android-database-sqlcipher" target="_blank" rel="noopener">SQLCipher</a>。<a href="https://github.com/sqlcipher/android-database-sqlcipher" target="_blank" rel="noopener">SQLCipher</a>是一个在SQLite基础之上进行扩展的开源数据库,它主要是在SQLite的基础之上增加了数据加密功能。</p><ul><li>加密性能高、开销小,只要5-15%的开销用于加密</li><li>完全做到数据库100%加密</li><li>采用良好的加密方式(CBC加密模式)</li><li>使用方便,做到应用级别加密</li><li>采用OpenSSL加密库提供的算法</li></ul><h1 id="加密内容"><a href="#加密内容" class="headerlink" title="加密内容"></a>加密内容</h1><p>介绍一些常用的加密数据的方式,可以通过这些方式加密存储的数据库内容。</p><table><thead><tr><th>加密算法</th><th>描述</th><th>优点</th><th>缺点</th></tr></thead><tbody><tr><td>DES,3DES</td><td>对称加密算法</td><td>算法公开、计算量小、加密速度快、加密效率高</td><td>双方都使用同样密钥,安全性得不到保证</td></tr><tr><td>AES</td><td>对称加密算法</td><td>算法公开、计算量小、加密速度快、加密效率高</td><td>双方都使用同样密钥,安全性得不到保证</td></tr><tr><td>XOR</td><td>异或加密</td><td>两个变量的互换(不借助第三个变量),简单的数据加密</td><td>加密方式简单</td></tr><tr><td>Base64</td><td>算不上什么加密算法,只是对数据进行编码传输</td><td></td><td></td></tr><tr><td>SHA</td><td>非对称加密算法。安全散列算法,数字签名工具。著名的图片加载框架Glide在缓存key时就采用的此加密</td><td>破解难度高,不可逆</td><td>可以通过穷举法进行破解</td></tr><tr><td>RSA</td><td>非对称加密算法,最流行的公钥密码算法,使用长度可变的秘钥</td><td>不可逆,既能用于数据加密,也可以应用于数字签名</td><td>RSA非对称加密内容长度有限制,1024位key的最多只能加密127位数据</td></tr><tr><td>MD5</td><td>非对称加密算法。全程:Message-Digest Algorithm,翻译为消息摘要算法</td><td>不可逆,压缩性,不容易修改,容易计算</td><td>穷举法可以破解</td></tr></tbody></table><h1 id="加密文件"><a href="#加密文件" class="headerlink" title="加密文件"></a>加密文件</h1><h2 id="集成"><a href="#集成" class="headerlink" title="集成"></a>集成</h2><p>AndroidStudio的Module中build.gradle添加<a href="https://github.com/sqlcipher/android-database-sqlcipher" target="_blank" rel="noopener">SQLCipher</a>的依赖<br></p><figure class="highlight gradle"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">compile</span> <span class="string">'net.zetetic:android-database-sqlcipher:3.5.7'</span></span><br></pre></td></tr></tbody></table></figure><p></p><h2 id="GreenDao加密"><a href="#GreenDao加密" class="headerlink" title="GreenDao加密"></a>GreenDao加密</h2><p>GreenDao有加密的接口,使用非常方便,在GreenDao初始化的时候启动加密模式。</p><ul><li><p>非加密模式</p><figure class="highlight stylus"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">DaoMaster<span class="selector-class">.OpenHelper</span><span class="selector-class">.getWritableDatabase</span>()</span><br></pre></td></tr></tbody></table></figure></li><li><p>加密模式</p><figure class="highlight stylus"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">DaoMaster<span class="selector-class">.OpenHelper</span><span class="selector-class">.getEncryptedWritableDb</span>(key)</span><br></pre></td></tr></tbody></table></figure> <figure class="highlight haxe"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">DaoMaster.OpenHelper encryptedHelper = <span class="keyword">new</span> <span class="type">DaoMaster</span>.DevOpenHelper(<span class="built_in">this</span>, VOD_DB, <span class="literal">null</span>);</span><br><span class="line"><span class="comment">// 选用设备唯一码作为加密的key</span></span><br><span class="line">DaoSession encryptedDaoSession = <span class="keyword">new</span> <span class="type">DaoMaster</span>(encryptedHelper.getEncryptedWritableDb(getUniquePseudoID())).<span class="keyword">new</span><span class="type">Session</span>();</span><br></pre></td></tr></tbody></table></figure></li></ul><p><strong>注意</strong></p><ul><li>读取数据库时,key需要保存一致,否则,读出的数据为空</li><li>增删改查的数据库操作加密非加密模式下都是一样的。</li></ul><h2 id="SQLiteOpenHelper加密"><a href="#SQLiteOpenHelper加密" class="headerlink" title="SQLiteOpenHelper加密"></a>SQLiteOpenHelper加密</h2><p>使用SQLiteOpenHelper自己定义的接口</p><ul><li><p>初始化SQLiteOpenHelper时,加载so库</p><figure class="highlight scala"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">public <span class="class"><span class="keyword">class</span> <span class="title">DBCipherHelper</span> <span class="keyword">extends</span> <span class="title">SQLiteOpenHelper</span> </span>{</span><br><span class="line"></span><br><span class="line">public <span class="type">DBCipherHelper</span>(<span class="type">Context</span> context, <span class="type">String</span> name, <span class="type">SQLiteDatabase</span>.<span class="type">CursorFactory</span> factory, int version) {</span><br><span class="line"> <span class="keyword">super</span>(context, name, factory, version);</span><br><span class="line"> <span class="comment">//不可忽略的 进行so库加载</span></span><br><span class="line"> <span class="type">SQLiteDatabase</span>.loadLibs(context);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li><p>使用</p><ul><li><p>传统模式</p><figure class="highlight gcode"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//获取可写数据库</span></span><br><span class="line">SQLiteDatabase db = dbHelper.getWritableDatabase<span class="comment">()</span>;</span><br><span class="line"><span class="comment">//获取可读数据库</span></span><br><span class="line">SQLiteDatabase db = dbHelper.getReadableDatabase<span class="comment">()</span>;</span><br></pre></td></tr></tbody></table></figure></li><li><p>加密模式</p><figure class="highlight gauss"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//获取写数据库</span></span><br><span class="line">SQLiteDatabase db = dbHelper.getWritableDatabase(<span class="built_in">key</span>);</span><br><span class="line"><span class="comment">//获取可读数据库</span></span><br><span class="line">SQLiteDatabase db = dbHelper.getReadableDatabase(<span class="built_in">key</span>);</span><br></pre></td></tr></tbody></table></figure></li></ul></li></ul><h1 id="加密方案总结"><a href="#加密方案总结" class="headerlink" title="加密方案总结"></a>加密方案总结</h1><ul><li><strong>加密内容</strong>后,将数据库打开,查询,可以看到加密后的内容,即密文。</li><li><strong>加密文件</strong>,集成<a href="https://github.com/sqlcipher/android-database-sqlcipher" target="_blank" rel="noopener">SQLCipher</a>后,<strong>数据库文件.db是完全加密的</strong>,因此,通过命令查询等操作数据库,会被提示数据库加密,操作失败!</li><li>根据不同需求,使用加密文件或者加密内容的方式进行数据的安全保护,<a href="https://github.com/sqlcipher/android-database-sqlcipher" target="_blank" rel="noopener">SQLCipher</a>会增加apk的大小。</li></ul><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8DTalk%20is%20cheap%EF%BC%8Cshow%20me%20the%20code.jpg" alt="Android" title="Talk is cheap,show me the code"></p>
<p><strong>Android数据库加密,加密应用里重要的信息,避免干坏事,坏人!</strong></p>
</summary>
<category term="Android联盟" scheme="https://tiimor.cn/categories/Android%E8%81%94%E7%9B%9F/"/>
<category term="Android" scheme="https://tiimor.cn/tags/Android/"/>
<category term="加密" scheme="https://tiimor.cn/tags/%E5%8A%A0%E5%AF%86/"/>
<category term="SQLCipher" scheme="https://tiimor.cn/tags/SQLCipher/"/>
<category term="SQLite" scheme="https://tiimor.cn/tags/SQLite/"/>
<category term="GreenDao" scheme="https://tiimor.cn/tags/GreenDao/"/>
</entry>
<entry>
<title>Android白银篇-GreenDao3</title>
<link href="https://tiimor.cn/Android%E7%99%BD%E9%93%B6%E7%AF%87-GreenDao3/"/>
<id>https://tiimor.cn/Android白银篇-GreenDao3/</id>
<published>2017-11-04T08:34:52.000Z</published>
<updated>2019-03-17T06:47:42.367Z</updated>
<content type="html"><![CDATA[<p><img src="http://cdn.tiimor.cn/images/GreenDao.png" alt="GreenDao"></p><p>GreenDao 是一个将对象映射到 SQLite 数据库中的轻量且快速的 ORM 解决方案。它的本质就是提供一个面向对象的接口,使得开发者更加方便地将数据存储到数据库SQLite之中。我们只需要定义数据模型,GreenDao就会为我们生成实体类以及DAOs(data access objects),(在3.0之后不需要我们编写generator而是编写实体类并添加注解,GreenDao会为我们生成schema,以及DAOs)从而避免了开发者编写较多枯燥的数据存储和加载的代码。</p><a id="more"></a><hr><blockquote><ul><li><a href="http://tiimor.cn/Android%E9%BB%84%E9%87%91%E7%AF%87-SQLite%E6%95%B0%E6%8D%AE%E5%BA%93/">Android黄金篇-SQLite数据库</a></li><li><a href="http://tiimor.cn/Android%E6%95%B0%E6%8D%AE%E5%BA%93%E5%8A%A0%E5%AF%86/">Android数据库加密</a></li></ul></blockquote><hr><font size="4"><strong><a href="https://github.com/greenrobot/greenDAO" target="_blank" rel="noopener">GreenDao传送门</a></strong></font><p><br></p><h1 id="GreenDao介绍"><a href="#GreenDao介绍" class="headerlink" title="GreenDao介绍"></a>GreenDao介绍</h1><p>在GreenDao中,默认会为每一个实体类建立一张数据表,实体类的每一个属性对应数据表中的一列。</p><p><strong>优点</strong></p><ul><li>Android精简的依赖库,方便集成</li><li>性能最大化</li><li>内存开销最小化</li><li>易于使用的APIs</li><li>对Android进行高度优化</li></ul><p><img src="http://cdn.tiimor.cn/images/GreenDao-%E5%8D%95%E4%BD%8D%E6%97%B6%E9%97%B4%E5%86%85%E7%9A%84%E6%93%8D%E4%BD%9C%E6%95%B0%E9%87%8F.png" alt="性能比较" title="单位时间内的操作数量"></p><h1 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h1><p>GreenDao3 采用注解方式来定义实体类,通过gradle插件生成相应的代码。</p><h2 id="配置插件"><a href="#配置插件" class="headerlink" title="配置插件"></a>配置插件</h2><p>在工程<strong>根目录下的build.gradle文件里</strong>,添加代码:<br></p><figure class="highlight gradle"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">dependencies</span> {</span><br><span class="line"><span class="keyword">classpath</span> <span class="string">'com.android.tools.build:gradle:2.3.3'</span></span><br><span class="line"><span class="comment">// 添加GreenDao插件</span></span><br><span class="line"><span class="keyword">classpath</span> <span class="string">'org.greenrobot:greendao-gradle-plugin:3.2.0'</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p></p><h2 id="配置依赖"><a href="#配置依赖" class="headerlink" title="配置依赖"></a>配置依赖</h2><p>在<strong>Module下的build.gradle</strong>文件里,添加代码:<br></p><figure class="highlight sqf"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">apply</span> plugin: <span class="string">'com.android.application'</span></span><br><span class="line"><span class="comment">// 添加</span></span><br><span class="line"><span class="built_in">apply</span> plugin: <span class="string">'org.greenrobot.greendao'</span></span><br><span class="line"></span><br><span class="line">dependencies {</span><br><span class="line"><span class="comment">// 添加</span></span><br><span class="line"><span class="built_in">compile</span> <span class="string">'org.greenrobot:greendao:3.2.0'</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 添加</span></span><br><span class="line">greendao {</span><br><span class="line"> schemaVersion <span class="number">1</span></span><br><span class="line"> daoPackage <span class="string">'com.excellence.medical.greendao'</span></span><br><span class="line"> targetGenDir <span class="string">'src/main/java'</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p></p><ul><li>schemaVersion: 数据库版本号,默认为1</li><li>daoPackage: 生成的DAOs、DaoMaster、DaoSession包名;默认为entities(数据库实体类)所在的包名</li><li>targetGenDir: 生成的DAOs、DaoMaster、DaoSession的目录,默认为build/generated/source/greendao</li><li>generateTests: 设置true自动生成单元测试。</li><li>targetGenDirTests: 设置生成单元测试目录。默认为src/androidTest/java</li></ul><h1 id="基本用法"><a href="#基本用法" class="headerlink" title="基本用法"></a>基本用法</h1><h2 id="实体"><a href="#实体" class="headerlink" title="实体"></a>实体</h2><p>创建带注解@Entity的实体类,实体类即数据表;通常(除开带@Transient注解的成员)实体类中成员就是数据库中对应的字段。然后<code>make project</code>编译项目,实体类会自动生成get、set方法,并且在<code>targetGenDir</code>目录下的<code>daoPackage</code>包里,如<code>src/main/java/com/excellence/medical/greendao</code>,生成DaoMaster、DaoSession、以及AccountDao。</p><p>如果想增加或减少数据库字段,删除实体类中自动生成的代码,然后进行增加或减少实体类中的成员。</p><figure class="highlight kotlin"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Entity(nameInDb = <span class="meta-string">"account"</span>)</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Account</span> </span>{</span><br><span class="line"></span><br><span class="line"><span class="meta">@Id(autoincrement = true)</span></span><br><span class="line"><span class="keyword">private</span> <span class="built_in">Long</span>id;</span><br><span class="line"><span class="meta">@Unique</span></span><br><span class="line"><span class="keyword">private</span> StringaccountId;</span><br><span class="line"><span class="keyword">private</span> StringaccountName;</span><br><span class="line"><span class="keyword">private</span> StringaccountPwd;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="注解"><a href="#注解" class="headerlink" title="注解"></a>注解</h2><ul><li><p>@Entity<br>修饰实体类名</p><figure class="highlight nix"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">@Entity(</span><br><span class="line"> // schema 名,多个 schema 时设置关联实体。插件产生不支持,需使用产生器</span><br><span class="line"> // <span class="attr">schema</span> = <span class="string">"myschema"</span>,</span><br><span class="line"></span><br><span class="line"> // 标记一个实体是否处于活动状态,活动实体有 update、delete、refresh 方法。默认为 <span class="literal">false</span></span><br><span class="line"> <span class="attr">active</span> = <span class="literal">false</span>,</span><br><span class="line"></span><br><span class="line"> // 表名,默认为类名</span><br><span class="line"> <span class="attr">nameInDb</span> = <span class="string">"Account"</span>,</span><br><span class="line"></span><br><span class="line"> // 定义多列索引</span><br><span class="line"> <span class="attr">indexes</span> = {</span><br><span class="line"> @Index(<span class="attr">value</span> = <span class="string">"name DESC"</span>, <span class="attr">unique</span> = <span class="literal">true</span>)</span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> // 标记是否创建表,默认 <span class="literal">true</span>。多实体对应一个表或者表已创建,不需要 greenDAO 创建时设置 <span class="literal">false</span></span><br><span class="line"> <span class="attr">createInDb</span> = <span class="literal">true</span>,</span><br><span class="line"></span><br><span class="line"> // 是否产生所有参数构造器。默认为 <span class="literal">true</span>。无参构造器必定产生</span><br><span class="line"> <span class="attr">generateConstructors</span> = <span class="literal">true</span>,</span><br><span class="line"></span><br><span class="line"> // 如果没有 get/set 方法,是否生成。默认为 <span class="literal">true</span></span><br><span class="line"> <span class="attr">generateGettersSetters</span> = <span class="literal">true</span></span><br><span class="line">)</span><br></pre></td></tr></tbody></table></figure></li><li><p>修饰实体类成员</p><figure class="highlight less"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 主键,autoincrement设置自增,注意类型是Long,而不是long</span></span><br><span class="line"><span class="variable">@Id</span>(autoincrement = true)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 唯一,默认索引</span></span><br><span class="line"><span class="variable">@Unique</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 列名-字段名,默认使用变量名。变化:customName --> CUSTOM_NAME</span></span><br><span class="line"><span class="variable">@Property</span>(nameInDb = <span class="string">"USERNAME"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 索引,unique设置唯一,name设置索引别名</span></span><br><span class="line"><span class="variable">@Index</span>(unique = true)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 非空</span></span><br><span class="line"><span class="variable">@NotNull</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 忽略,不持久化,即数据表不创建该字段,可用关键字transient替代</span></span><br><span class="line"><span class="variable">@Transient</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 对一,实体属性 joinProperty 对应外联实体ID</span></span><br><span class="line"><span class="variable">@ToOne</span>(joinProperty = <span class="string">"fk_dogId"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 对多。实体ID对应外联实体属性 referencedJoinProperty</span></span><br><span class="line"><span class="variable">@ToMany</span>(referencedJoinProperty = <span class="string">"fk_userId"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 对多。@JoinProperty:name 实体属性对应外联实体属性 referencedName</span></span><br><span class="line"><span class="variable">@ToMany</span>(joinProperties = {<span class="variable">@JoinProperty</span>(name = <span class="string">"horseName"</span>, referencedName = <span class="string">"name"</span>)})</span><br><span class="line"></span><br><span class="line"><span class="comment">// 对多。@JoinEntity:entity 中间表;中间表属性 sourceProperty 对应实体ID;中间表属性 targetProperty 对应外联实体ID</span></span><br><span class="line"><span class="variable">@ToMany</span></span><br><span class="line"><span class="variable">@JoinEntity</span>(entity = JoinUserWithSheep.class, sourceProperty = <span class="string">"uId"</span>, targetProperty = <span class="string">"sId"</span>)</span><br></pre></td></tr></tbody></table></figure></li></ul><h2 id="初始化"><a href="#初始化" class="headerlink" title="初始化"></a>初始化</h2><p>在Application中进行初始化,GreenDao打开数据库两种方式:①打开内部(<code>/data/data/xxxpackageNamexxx/</code>)数据库,②打开外部(其他目录下)数据库</p><ul><li><p>内部数据库</p><figure class="highlight haxe"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 注意</span></span><br><span class="line">DaoMaster.OpenHelper helper = <span class="keyword">new</span> <span class="type">DaoMaster</span>.DevOpenHelper(<span class="built_in">this</span>, VOD_DB, <span class="literal">null</span>);</span><br><span class="line">mDaoMaster = <span class="keyword">new</span> <span class="type">DaoMaster</span>(helper.getWritableDatabase());</span><br><span class="line"></span><br><span class="line"><span class="comment">// 如果想使用Dao,直接用mDaoSession获取对应的Dao来操作</span></span><br><span class="line">mDaoSession = mDaoMaster.<span class="keyword">new</span><span class="type">Session</span>();</span><br><span class="line"><span class="comment">// Dao,执行增删改查操作</span></span><br><span class="line">AccountDao dao = mDaoSession.getAccountDao();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 如果想使用Sql语句,就使用mDaoDatabase操作</span></span><br><span class="line">mDaoDatabase = mDaoSession.getDatabase();</span><br><span class="line"></span><br><span class="line"><span class="comment">// GreenDao有特殊的线程来处理数据库的耗时操作</span></span><br><span class="line">mAsyncSession = mDaoSession.startAsyncSession();</span><br></pre></td></tr></tbody></table></figure></li><li><p>外部数据库</p><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DatabaseContext</span> <span class="keyword">extends</span> <span class="title">ContextWrapper</span> </span>{</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> String mDBPath = <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="title">DatabaseContext</span><span class="params">(Context base)</span> </span>{</span><br><span class="line"><span class="keyword">super</span>(base);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">DatabaseContext</span><span class="params">(Context base, String dbPath)</span> </span>{</span><br><span class="line"><span class="keyword">super</span>(base);</span><br><span class="line">mDBPath = dbPath;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> File <span class="title">getDatabasePath</span><span class="params">(String name)</span> </span>{</span><br><span class="line">String dbPath = mDBPath + name;</span><br><span class="line"><span class="keyword">if</span> (FileUtils.isFileExists(dbPath)) {</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">new</span> File(dbPath);</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> SQLiteDatabase <span class="title">openOrCreateDatabase</span><span class="params">(String name, <span class="keyword">int</span> mode, SQLiteDatabase.CursorFactory factory)</span> </span>{</span><br><span class="line"><span class="keyword">int</span> flags = SQLiteDatabase.CREATE_IF_NECESSARY | SQLiteDatabase.NO_LOCALIZED_COLLATORS;</span><br><span class="line"><span class="keyword">return</span> SQLiteDatabase.openDatabase(getDatabasePath(name).getAbsolutePath(), factory, flags, <span class="keyword">null</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> SQLiteDatabase <span class="title">openOrCreateDatabase</span><span class="params">(String name, <span class="keyword">int</span> mode, SQLiteDatabase.CursorFactory factory, DatabaseErrorHandler errorHandler)</span> </span>{</span><br><span class="line"><span class="keyword">int</span> flags = SQLiteDatabase.CREATE_IF_NECESSARY | SQLiteDatabase.NO_LOCALIZED_COLLATORS;</span><br><span class="line"><span class="keyword">return</span> SQLiteDatabase.openDatabase(getDatabasePath(name).getAbsolutePath(), factory, flags, errorHandler);</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><figure class="highlight haxe"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 重写传入DaoMaster.DevOpenHelper的Context,即改变数据库的路径</span></span><br><span class="line">DatabaseContext databaseContext = <span class="keyword">new</span> <span class="type">DatabaseContext</span>(<span class="built_in">this</span>, DB_PATH);</span><br><span class="line">DaoMaster.OpenHelper helper = <span class="keyword">new</span> <span class="type">DaoMaster</span>.DevOpenHelper(databaseContext, VOD_DB, <span class="literal">null</span>);</span><br><span class="line">其他类似打开内部数据库</span><br></pre></td></tr></tbody></table></figure><ul><li><p><strong>注意:</strong></p><ul><li>DaoMaster.DevOpenHelper在数据库升级的时候,会删除所有的表,只能用于Debug调试,正式项目需要封装处理,GreenDao升级请参考:<a href="https://github.com/yuweiguocn/GreenDaoUpgradeHelper" target="_blank" rel="noopener">GreenDaoUpgradeHelper</a></li><li>如果GreenDao想使用<strong>打开多个数据库</strong>,可以创建多个DaoMaster.OpenHelper和DaoSession;同时重写onCreate方法,否则每个数据库的表是一样的。例如:<figure class="highlight haxe"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">DaoMaster.OpenHelper helper = <span class="keyword">new</span> <span class="type">DaoMaster</span>.OpenHelper(<span class="built_in">this</span>, DB_NAME) {</span><br><span class="line">@Override</span><br><span class="line"><span class="keyword">public</span> void onCreate(Database db) {</span><br><span class="line">ConfigDao.createTable(db, <span class="literal">false</span>);</span><br><span class="line">MemberDao.createTable(db, <span class="literal">false</span>);</span><br><span class="line">}</span><br><span class="line">};</span><br><span class="line">mDaoSession = <span class="keyword">new</span> <span class="type">DaoMaster</span>(helper.getWritableDatabase()).<span class="keyword">new</span><span class="type">Session</span>();</span><br><span class="line"></span><br><span class="line">DaoMaster.OpenHelper encryptedHelper = <span class="keyword">new</span> <span class="type">DaoMaster</span>.OpenHelper(<span class="built_in">this</span>, ENCRYPTED_DB_NAME) {</span><br><span class="line">@Override</span><br><span class="line"><span class="keyword">public</span> void onCreate(Database db) {</span><br><span class="line">AccountDao.createTable(db, <span class="literal">false</span>);</span><br><span class="line">}</span><br><span class="line">};</span><br><span class="line">mEncryptedDaoSession = <span class="keyword">new</span> <span class="type">DaoMaster</span>(encryptedHelper.getEncryptedWritableDb(getUniquePseudoID())).<span class="keyword">new</span><span class="type">Session</span>();</span><br></pre></td></tr></tbody></table></figure></li></ul></li></ul><h2 id="增删改查"><a href="#增删改查" class="headerlink" title="增删改查"></a>增删改查</h2><ul><li><p>使用Dao操作时,数据库里必须有<strong>主键</strong>,操作才会成功;否则操作无效或达不到预期的结果</p></li><li><p>GreenDao有一个缓存机制,即把用户插入,更改或查找的实体保存在内存中,当用户下一次查找时先从内存中查找,如果不存在再从数据库中查找,清除缓存使用:<code>DaoSession.clear()</code></p></li><li><p>如果没有主键,则只能使用Sql语句操作数据库,可以参考:<a href="http://tiimor.cn/Android%E9%BB%84%E9%87%91%E7%AF%87-SQLite%E6%95%B0%E6%8D%AE%E5%BA%93/">Android黄金篇-SQLite数据库</a></p></li></ul><h3 id="Dao增加"><a href="#Dao增加" class="headerlink" title="Dao增加"></a>Dao增加</h3><figure class="highlight processing"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">long</span> insert(T entity) <span class="comment">// 插入指定实体</span></span><br><span class="line"><span class="keyword">void</span> insertInTx(T... entities)</span><br><span class="line"><span class="keyword">void</span> insertInTx(java.lang.Iterable<T> entities)</span><br><span class="line"><span class="keyword">void</span> insertInTx(java.lang.Iterable<T> entities, <span class="built_in">boolean</span> setPrimaryKey)</span><br><span class="line"><span class="keyword">long</span> insertWithoutSettingPk(T entity) <span class="comment">// 插入指定实体,无主键</span></span><br><span class="line"><span class="keyword">long</span> insertOrReplace(T entity) <span class="comment">// 插入或替换指定实体</span></span><br><span class="line"><span class="keyword">void</span> insertOrReplaceInTx(T... entities)</span><br><span class="line"><span class="keyword">void</span> insertOrReplaceInTx(java.lang.Iterable<T> entities)</span><br><span class="line"><span class="keyword">void</span> insertOrReplaceInTx(java.lang.Iterable<T> entities, <span class="built_in">boolean</span> setPrimaryKey)</span><br><span class="line"><span class="keyword">void</span> <span class="built_in">save</span>(T entity) <span class="comment">// 依赖指定的主键插入或修改实体</span></span><br><span class="line"><span class="keyword">void</span> saveInTx(T... entities)</span><br><span class="line"><span class="keyword">void</span> saveInTx(java.lang.Iterable<T> entities)</span><br></pre></td></tr></tbody></table></figure><h3 id="Dao删除"><a href="#Dao删除" class="headerlink" title="Dao删除"></a>Dao删除</h3><figure class="highlight dart"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> deleteAll() <span class="comment">// 删除所有</span></span><br><span class="line"><span class="keyword">void</span> delete(T entity) <span class="comment">// 删除指定的实体</span></span><br><span class="line"><span class="keyword">void</span> deleteInTx(T... entities)</span><br><span class="line"><span class="keyword">void</span> deleteInTx(java.lang.<span class="built_in">Iterable</span><T> entities)</span><br><span class="line"><span class="keyword">void</span> deleteByKey(K key) <span class="comment">// 删除指定主键对应的实体</span></span><br><span class="line"><span class="keyword">void</span> deleteByKeyInTx(K... keys)</span><br><span class="line"><span class="keyword">void</span> deleteByKeyInTx(java.lang.<span class="built_in">Iterable</span><K> keys)</span><br></pre></td></tr></tbody></table></figure><h3 id="Dao修改"><a href="#Dao修改" class="headerlink" title="Dao修改"></a>Dao修改</h3><figure class="highlight lisp"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">void update(<span class="name">T</span> entity)</span><br><span class="line">void updateInTx(<span class="name">T</span>... entities)</span><br><span class="line">void updateInTx(<span class="name">java</span>.lang.Iterable<T> entities)</span><br></pre></td></tr></tbody></table></figure><h3 id="Dao查询"><a href="#Dao查询" class="headerlink" title="Dao查询"></a>Dao查询</h3><figure class="highlight pf"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">java.util.List<span class="variable"><T></span> <span class="built_in">load</span>All()</span><br><span class="line">T <span class="built_in">load</span>(K key)</span><br><span class="line">T <span class="built_in">load</span>ByRowId(long rowId)</span><br></pre></td></tr></tbody></table></figure><h3 id="QueryBuilder查询"><a href="#QueryBuilder查询" class="headerlink" title="QueryBuilder查询"></a>QueryBuilder查询</h3><figure class="highlight dts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">QueryBuilder<span class="params"><T></span> queryBuilder() <span class="comment">// Dao</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// QueryBuilder</span></span><br><span class="line">QueryBuilder<span class="params"><T></span> where(WhereCondition cond, WhereCondition... condMore) <span class="comment">// 条件,AND 连接</span></span><br><span class="line">QueryBuilder<span class="params"><T></span> whereOr(WhereCondition cond1, WhereCondition cond2, WhereCondition... condMore) <span class="comment">// 条件,OR 连接</span></span><br><span class="line">QueryBuilder<span class="params"><T></span> distinct() <span class="comment">// 去重,例如使用联合查询时</span></span><br><span class="line">QueryBuilder<span class="params"><T></span> limit(int limit) <span class="comment">// 限制返回数</span></span><br><span class="line">QueryBuilder<span class="params"><T></span> offset(int offset) <span class="comment">// 偏移结果起始位,配合limit(int)使用</span></span><br><span class="line">QueryBuilder<span class="params"><T></span> orderAsc(Property... properties) <span class="comment">// 排序,升序</span></span><br><span class="line">QueryBuilder<span class="params"><T></span> orderDesc(Property... properties) <span class="comment">// 排序,降序</span></span><br><span class="line">QueryBuilder<span class="params"><T></span> orderCustom(Property property, java.lang.String customOrderForProperty) <span class="comment">// 排序,自定义</span></span><br><span class="line">QueryBuilder<span class="params"><T></span> orderRaw(java.lang.String rawOrder) <span class="comment">// 排序,SQL 语句</span></span><br><span class="line">QueryBuilder<span class="params"><T></span> preferLocalizedStringOrder() <span class="comment">// 本地化字符串排序,用于加密数据库无效</span></span><br><span class="line">QueryBuilder<span class="params"><T></span> stringOrderCollation(java.lang.String stringOrderCollation) <span class="comment">// 自定义字符串排序,默认不区分大小写</span></span><br><span class="line"></span><br><span class="line">WhereCondition and(WhereCondition cond1, WhereCondition cond2, WhereCondition... condMore) <span class="comment">// 条件,AND 连接</span></span><br><span class="line">WhereCondition or(WhereCondition cond1, WhereCondition cond2, WhereCondition... condMore) <span class="comment">// 条件,OR 连接</span></span><br></pre></td></tr></tbody></table></figure><p>示例<br></p><figure class="highlight css"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">mAccountDao</span><span class="selector-class">.queryBuilder</span>()<span class="selector-class">.orderDesc</span>(<span class="selector-tag">Properties</span><span class="selector-class">.Date</span>)<span class="selector-class">.limit</span>(1)<span class="selector-class">.unique</span>()</span><br></pre></td></tr></tbody></table></figure><p></p><h3 id="DaoSession异步操作"><a href="#DaoSession异步操作" class="headerlink" title="DaoSession异步操作"></a>DaoSession异步操作</h3><figure class="highlight less"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">DaoSession</span>()<span class="selector-class">.startAsyncSession</span>()<span class="selector-class">.runInTx</span>(new Runnable() {</span><br><span class="line"></span><br><span class="line"> <span class="variable">@Override</span></span><br><span class="line"> public void run() {</span><br><span class="line"> <span class="comment">// insert</span></span><br><span class="line"> <span class="comment">// delete</span></span><br><span class="line"> <span class="comment">// update</span></span><br><span class="line"><span class="comment">// query</span></span><br><span class="line"> }</span><br><span class="line">});</span><br><span class="line"><span class="selector-tag">DaoSession</span><span class="selector-class">.startAsyncSession</span>()<span class="selector-class">.insertInTx</span></span><br><span class="line"><span class="selector-tag">DaoSession</span><span class="selector-class">.startAsyncSession</span>()<span class="selector-class">.deleteInTx</span></span><br><span class="line"><span class="selector-tag">DaoSession</span><span class="selector-class">.startAsyncSession</span>()<span class="selector-class">.updateInTx</span></span><br><span class="line"><span class="selector-tag">DaoSession</span><span class="selector-class">.startAsyncSession</span>()<span class="selector-class">.insertInTx</span></span><br><span class="line"><span class="selector-tag">DaoSession</span><span class="selector-class">.startAsyncSession</span>()<span class="selector-class">.insertOrReplaceInTx</span></span><br></pre></td></tr></tbody></table></figure><h3 id="DaoSession增删改查"><a href="#DaoSession增删改查" class="headerlink" title="DaoSession增删改查"></a>DaoSession增删改查</h3><figure class="highlight stylus"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// DaoSession 的方法转换成 Dao 的对应方法执行</span></span><br><span class="line"><T> long insert(T entity)</span><br><span class="line"><T> long insertOrReplace(T entity)</span><br><span class="line"><T> void delete(T entity)</span><br><span class="line"><T> void deleteAll(java<span class="selector-class">.lang</span><span class="selector-class">.Class</span><T> entityClass)</span><br><span class="line"><T> void update(T entity)</span><br><span class="line"><T,K> T load(java<span class="selector-class">.lang</span><span class="selector-class">.Class</span><T> entityClass, K key)</span><br><span class="line"><T,K> java<span class="selector-class">.util</span><span class="selector-class">.List</span><T> loadAll(java<span class="selector-class">.lang</span><span class="selector-class">.Class</span><T> entityClass)</span><br><span class="line"><T> QueryBuilder<T> queryBuilder(java<span class="selector-class">.lang</span><span class="selector-class">.Class</span><T> entityClass)</span><br><span class="line"><T,K> java<span class="selector-class">.util</span><span class="selector-class">.List</span><T> queryRaw(java<span class="selector-class">.lang</span><span class="selector-class">.Class</span><T> entityClass, java<span class="selector-class">.lang</span><span class="selector-class">.String</span> where, java<span class="selector-class">.lang</span><span class="selector-class">.String</span>... selectionArgs)</span><br><span class="line"><T> void refresh(T entity)</span><br></pre></td></tr></tbody></table></figure><h3 id="Query重复查询"><a href="#Query重复查询" class="headerlink" title="Query重复查询"></a>Query重复查询</h3><figure class="highlight glsl"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// QueryBuilder</span></span><br><span class="line">Query<T> build()</span><br><span class="line">CursorQuery buildCursor()</span><br><span class="line">CountQuery<T> buildCount()</span><br><span class="line">DeleteQuery<T> buildDelete()</span><br><span class="line"></span><br><span class="line"><span class="comment">// Query</span></span><br><span class="line"><span class="comment">// 设置查询参数,从 0 开始</span></span><br><span class="line">Query<T> setParameter(<span class="type">int</span> <span class="keyword">index</span>, java.lang.Object parameter)</span><br><span class="line">Query<T> setParameter(<span class="type">int</span> <span class="keyword">index</span>, java.lang.Boolean parameter)</span><br><span class="line">Query<T> setParameter(<span class="type">int</span> <span class="keyword">index</span>, java.util.Date parameter)</span><br><span class="line"><span class="type">void</span> setLimit(<span class="type">int</span> limit) <span class="comment">// 限制返回数</span></span><br><span class="line"><span class="type">void</span> setOffset(<span class="type">int</span> <span class="keyword">offset</span>) <span class="comment">// 偏移结果起始位,配合limit(int)使用</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Query 绑定线程,执行非本线程的 Query 抛异常,调用获取本线程 Query</span></span><br><span class="line">Query<T> forCurrentThread() <span class="comment">// 获取本线程 Query</span></span><br></pre></td></tr></tbody></table></figure><h3 id="查询结果"><a href="#查询结果" class="headerlink" title="查询结果"></a>查询结果</h3><figure class="highlight dts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// QueryBuilder、Query</span></span><br><span class="line">T unique() <span class="comment">// 返回唯一结果或者 null</span></span><br><span class="line">T uniqueOrThrow() <span class="comment">// 返回唯一非空结果,如果 null 则抛异常</span></span><br><span class="line">java.util.List<span class="params"><T></span> list() <span class="comment">// 返回结果集进内存</span></span><br><span class="line"><span class="comment">// 懒加载,须在 try/finally 代码中关闭。</span></span><br><span class="line">LazyList<span class="params"><T></span> listLazy() <span class="comment">// 第一次使用返回结果集,所有数据使用后会自动关闭</span></span><br><span class="line">LazyList<span class="params"><T></span> listLazyUncached() <span class="comment">// 返回虚拟结果集,数据库读取不缓存</span></span><br><span class="line">CloseableListIterator<span class="params"><T></span> listIterator() <span class="comment">// 懒加载数据迭代器,不缓存,所有数据使用后会自动关闭</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// QueryBuilder、CountQuery</span></span><br><span class="line">long count() <span class="comment">// 获取结果数量</span></span><br></pre></td></tr></tbody></table></figure><h1 id="混淆"><a href="#混淆" class="headerlink" title="混淆"></a>混淆</h1><p>在混淆文件proguard-rules.pro中添加</p><figure class="highlight scala"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">### greenDAO <span class="number">3</span></span><br><span class="line">-keepclassmembers <span class="class"><span class="keyword">class</span> <span class="title">*</span> <span class="keyword">extends</span> <span class="title">org</span>.<span class="title">greenrobot</span>.<span class="title">greendao</span>.<span class="title">AbstractDao</span> </span>{</span><br><span class="line">public static java.lang.<span class="type">String</span> <span class="type">TABLENAME</span>;</span><br><span class="line">}</span><br><span class="line">-keep <span class="class"><span class="keyword">class</span> <span class="title">**$Properties</span></span></span><br><span class="line"><span class="class"></span></span><br><span class="line"><span class="class"><span class="title">#</span> <span class="title">If</span> <span class="title">you</span> <span class="title">do</span> <span class="title">not</span> <span class="title">use</span> <span class="title">SQLCipher</span></span>:</span><br><span class="line">-dontwarn org.greenrobot.greendao.database.**</span><br><span class="line"># <span class="type">If</span> you do not use <span class="type">RxJava</span>:</span><br><span class="line">-dontwarn rx.**</span><br></pre></td></tr></tbody></table></figure><p><strong>@ToOne、@ToMany,1:1、1:n、n:m等多张表关联待续^_^</strong></p><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
<p><img src="http://cdn.tiimor.cn/images/GreenDao.png" alt="GreenDao"></p>
<p>GreenDao 是一个将对象映射到 SQLite 数据库中的轻量且快速的 ORM 解决方案。它的本质就是提供一个面向对象的接口,使得开发者更加方便地将数据存储到数据库SQLite之中。我们只需要定义数据模型,GreenDao就会为我们生成实体类以及DAOs(data access objects),(在3.0之后不需要我们编写generator而是编写实体类并添加注解,GreenDao会为我们生成schema,以及DAOs)从而避免了开发者编写较多枯燥的数据存储和加载的代码。</p>
</summary>
<category term="Android联盟" scheme="https://tiimor.cn/categories/Android%E8%81%94%E7%9B%9F/"/>
<category term="Android" scheme="https://tiimor.cn/tags/Android/"/>
<category term="SQLite" scheme="https://tiimor.cn/tags/SQLite/"/>
<category term="GreenDao" scheme="https://tiimor.cn/tags/GreenDao/"/>
</entry>
<entry>
<title>Android适配器终结者?</title>
<link href="https://tiimor.cn/Android%E9%80%82%E9%85%8D%E5%99%A8%E7%BB%88%E7%BB%93%E8%80%85%EF%BC%9F/"/>
<id>https://tiimor.cn/Android适配器终结者?/</id>
<published>2017-10-22T06:39:56.000Z</published>
<updated>2019-03-17T06:47:42.368Z</updated>
<content type="html"><![CDATA[<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8D%E7%94%A8%E4%BD%A0%E7%9A%84%E9%92%A5%E5%8C%99%EF%BC%8C%E6%89%93%E5%BC%80%E6%88%91%E7%9A%84%E5%BF%83%E6%89%89.jpg" alt="Android" title="用你的钥匙,打开我的心扉"></p><p><strong>终结Adapter、ViewHolder,封装通用适配器</strong></p><a id="more"></a><font size="4"><strong><a href="https://github.com/VeiZhang/BaseToolsLibrary" target="_blank" rel="noopener">项目传送门</a></strong></font><figure class="highlight gradle"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">compile</span> <span class="string">'com.excellence:basetools:1.2.4'</span></span><br></pre></td></tr></tbody></table></figure><h1 id="Android适配器"><a href="#Android适配器" class="headerlink" title="Android适配器"></a>Android适配器</h1><p>在Android开发中,经常使用Adapter和ViewHolder,总是写着千篇一律的适配器代码,所以进行通用型、万能型的Adapter封装。</p><ul><li><p>MVVM模式Adapter封装<br>最近学习MVVM,发现一系列优点,适配器封装也非常简单,并且可以让ViewHolder去死吧,只留下可爱的Adapter;开启DataBinding,进行封装。</p></li><li><p>普通Adapter封装<br>其他环境下:不开启DataBinding,封装Adapter和ViewHolder,使用时需要实现Adapter的抽象接口。</p></li></ul><p><strong>以上两种方式都可以实现单布局类型、多布局类型的适配器。</strong></p><h1 id="Adapter示例"><a href="#Adapter示例" class="headerlink" title="Adapter示例"></a>Adapter示例</h1><p><img src="https://github.com/VeiZhang/BaseToolsLibrary/raw/master/images/common_adapter.png?raw=true" alt="单布局类型" title="单布局类型"></p><p><img src="https://github.com/VeiZhang/BaseToolsLibrary/raw/master/images/multi_adapter.png?raw=true" alt="多布局类型" title="多布局类型"></p><h2 id="MVVM模式Adapter示例"><a href="#MVVM模式Adapter示例" class="headerlink" title="MVVM模式Adapter示例"></a>MVVM模式Adapter示例</h2><h3 id="DataBinding,ListVew、GridView适配器"><a href="#DataBinding,ListVew、GridView适配器" class="headerlink" title="DataBinding,ListVew、GridView适配器"></a>DataBinding,ListVew、GridView适配器</h3><ul><li><p>单布局类型<br><strong>简单到爆炸有木有!!!</strong></p><figure class="highlight jboss-cli"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">//</span> 直接创建CommonBindingAdapter</span><br><span class="line">CommonBindingAdapter<Flower> adapter = new CommonBindingAdapter<><span class="params">(mFlowers, R.layout.item_flower, BR.flower)</span>;</span><br><span class="line"><span class="string">//</span> 设置适配器,等同于ListView.<span class="keyword">set</span>Adapter<span class="params">()</span>、GridView.<span class="keyword">set</span>Adapter<span class="params">()</span></span><br><span class="line">mBinding.<span class="keyword">set</span>Adapter<span class="params">(adapter)</span>;</span><br></pre></td></tr></tbody></table></figure></li><li><p>多布局类型<br>主要是实现多布局类型的<code>ViewDelegate</code>的接口</p><figure class="highlight haxe"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 直接创建MultiItemTypeBindingAdapter</span></span><br><span class="line">MultiItemTypeBindingAdapter<Flower> adapter = <span class="keyword">new</span> <span class="type">MultiItemTypeBindingAdapter</span><>(mFlowers);</span><br><span class="line"><span class="comment">// 添加多布局类型</span></span><br><span class="line">adapter.addItemViewDelegate(<span class="keyword">new</span> <span class="type">RoseViewDelegate</span>());</span><br><span class="line">adapter.addItemViewDelegate(<span class="keyword">new</span> <span class="type">TulipViewDelegate</span>());</span><br><span class="line"><span class="comment">// 设置适配器</span></span><br><span class="line">mBinding.setAdapter(adapter);</span><br></pre></td></tr></tbody></table></figure><p> 实现<code>ItemViewDelegate</code>的接口</p><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">RoseViewDelegate</span> <span class="keyword">implements</span> <span class="title">ItemViewDelegate</span><<span class="title">Flower</span>></span></span><br><span class="line"><span class="class"></span>{</span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getItemViewLayoutId</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">return</span> R.layout.item_rose;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getItemVariable</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">return</span> BR.rose;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isForViewType</span><span class="params">(Flower item, <span class="keyword">int</span> position)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">return</span> item <span class="keyword">instanceof</span> Rose;</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><h3 id="DataBinding,RecyclerView适配器"><a href="#DataBinding,RecyclerView适配器" class="headerlink" title="DataBinding,RecyclerView适配器"></a>DataBinding,RecyclerView适配器</h3><p>同上</p><ul><li><p>单布局类型</p><figure class="highlight haxe"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 直接创建BaseRecyclerBindingAdapter</span></span><br><span class="line">BaseRecyclerBindingAdapter<Flower> adapter = <span class="keyword">new</span> <span class="type">BaseRecyclerBindingAdapter</span><>(mFlowers, R.layout.item_flower, BR.flower);</span><br><span class="line"><span class="comment">// 设置适配器,等同于RecyclerView.setAdapter()</span></span><br><span class="line">mBinding.setAdapter(adapter);</span><br><span class="line"><span class="comment">// 注意设置LayoutManager,等同于RecyclerView.setLayoutManager()</span></span><br><span class="line">mBinding.setLayoutManager(<span class="keyword">new</span> <span class="type">LinearLayoutManager</span>(<span class="built_in">this</span>));</span><br></pre></td></tr></tbody></table></figure></li><li><p>多布局类型</p><figure class="highlight haxe"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">MultiItemTypeBindingRecyclerAdapter<Flower> adapter = <span class="keyword">new</span> <span class="type">MultiItemTypeBindingRecyclerAdapter</span><>(mFlowers);</span><br><span class="line">adapter.addItemViewDelegate(<span class="keyword">new</span> <span class="type">RoseViewDelegate</span>());</span><br><span class="line">adapter.addItemViewDelegate(<span class="keyword">new</span> <span class="type">TulipViewDelegate</span>());</span><br><span class="line">mBinding.setAdapter(adapter);</span><br><span class="line">mBinding.setLayoutManager(<span class="keyword">new</span> <span class="type">LinearLayoutManager</span>(<span class="built_in">this</span>));</span><br></pre></td></tr></tbody></table></figure></li></ul><h2 id="普通Adapter示例"><a href="#普通Adapter示例" class="headerlink" title="普通Adapter示例"></a>普通Adapter示例</h2><h3 id="ListVew、GridView适配器"><a href="#ListVew、GridView适配器" class="headerlink" title="ListVew、GridView适配器"></a>ListVew、GridView适配器</h3><ul><li><p>单布局类型<br>实现<code>CommonAdapter</code>的接口</p><figure class="highlight scala"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 创建adapter类继承CommonAdapter,然后设置适配器即可</span></span><br><span class="line"><span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">AppGridAdapter</span> <span class="keyword">extends</span> <span class="title">CommonAdapter<ResolveInfo></span></span></span><br><span class="line"><span class="class"></span>{</span><br><span class="line"> public <span class="type">AppGridAdapter</span>(<span class="type">Context</span> context, <span class="type">List</span><<span class="type">ResolveInfo</span>> datas, int layoutId)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">super</span>(context, datas, layoutId);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> public void convert(<span class="type">ViewHolder</span> viewHolder, <span class="type">ResolveInfo</span> item, int position)</span><br><span class="line"> {</span><br><span class="line"><span class="comment">// ViewHolder封装了一些辅助方法,方便View的各种设置</span></span><br><span class="line"> <span class="type">ImageView</span> iconView = viewHolder.getView(android.<span class="type">R</span>.id.icon);</span><br><span class="line"> iconView.setImageDrawable(item.loadIcon(mPackageManager));</span><br><span class="line"> viewHolder.setText(android.<span class="type">R</span>.id.text1, item.loadLabel(mPackageManager).toString());</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li><p>多布局类型<br>实现<code>ItemViewDelegate</code>接口</p><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 多布局适配器</span></span><br><span class="line"><span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">ChatAdapter</span> <span class="keyword">extends</span> <span class="title">MultiItemTypeAdapter</span><<span class="title">People</span>></span></span><br><span class="line"><span class="class"></span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">ChatAdapter</span><span class="params">(Context context, List<People> messages)</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> <span class="keyword">super</span>(context, messages);</span><br><span class="line"> addItemViewDelegate(<span class="keyword">new</span> ComputerDelegate());</span><br><span class="line"> addItemViewDelegate(<span class="keyword">new</span> BlueDelegate());</span><br><span class="line"> addItemViewDelegate(<span class="keyword">new</span> PurpleDelegate());</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 不同的布局视图</span></span><br><span class="line"><span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">ComputerDelegate</span> <span class="keyword">implements</span> <span class="title">ItemViewDelegate</span><<span class="title">People</span>></span></span><br><span class="line"><span class="class"></span>{</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getItemViewLayoutId</span><span class="params">()</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> <span class="keyword">return</span> R.layout.item_computer;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isForViewType</span><span class="params">(People item, <span class="keyword">int</span> position)</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> <span class="keyword">return</span> item <span class="keyword">instanceof</span> ComputerData;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">convert</span><span class="params">(ViewHolder viewHolder, People item, <span class="keyword">int</span> position)</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> viewHolder.setText(R.id.computer_text, item.getMsg());</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><h3 id="RecyclerView适配器"><a href="#RecyclerView适配器" class="headerlink" title="RecyclerView适配器"></a>RecyclerView适配器</h3><ul><li><p>单布局类型<br>实现<code>BaseRecyclerAdapter</code>的接口</p><figure class="highlight scala"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 创建adapter类继承BaseRecyclerAdapter</span></span><br><span class="line"><span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">AppRecyclerAdapter</span> <span class="keyword">extends</span> <span class="title">BaseRecyclerAdapter<ResolveInfo></span></span></span><br><span class="line"><span class="class"></span>{</span><br><span class="line"> <span class="keyword">private</span> <span class="type">PackageManager</span> mPackageManager = <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line"> public <span class="type">AppRecyclerAdapter</span>(<span class="type">Context</span> context, <span class="type">List</span><<span class="type">ResolveInfo</span>> datas, int layoutId)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">super</span>(context, datas, layoutId);</span><br><span class="line"> mPackageManager = context.getPackageManager();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> public void convert(<span class="type">RecyclerViewHolder</span> viewHolder, <span class="type">ResolveInfo</span> item, int position)</span><br><span class="line"> {</span><br><span class="line"><span class="comment">// ViewHolder封装了一些辅助方法,方便View的各种设置</span></span><br><span class="line"> viewHolder.setText(android.<span class="type">R</span>.id.text1, item.loadLabel(mPackageManager));</span><br><span class="line"> viewHolder.setImageDrawable(android.<span class="type">R</span>.id.icon, item.loadIcon(mPackageManager));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li><p>多布局类型<br>实现<code>ItemViewDelegate</code>的接口</p><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 多布局适配器</span></span><br><span class="line"><span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">WarAdapter</span> <span class="keyword">extends</span> <span class="title">MultiItemTypeRecyclerAdapter</span><<span class="title">People</span>></span></span><br><span class="line"><span class="class"></span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">WarAdapter</span><span class="params">(Context context, List<People> datas)</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> <span class="keyword">super</span>(context, datas);</span><br><span class="line"> addItemViewDelegate(<span class="keyword">new</span> ComputerRecyclerDelegate());</span><br><span class="line"> addItemViewDelegate(<span class="keyword">new</span> BlueRecyclerDelegate());</span><br><span class="line"> addItemViewDelegate(<span class="keyword">new</span> PurpleRecyclerDelegate());</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 不同的布局视图</span></span><br><span class="line"><span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">ComputerRecyclerDelegate</span> <span class="keyword">implements</span> <span class="title">ItemViewDelegate</span><<span class="title">People</span>></span></span><br><span class="line"><span class="class"></span>{</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getItemViewLayoutId</span><span class="params">()</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> <span class="keyword">return</span> R.layout.item_computer;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isForViewType</span><span class="params">(People item, <span class="keyword">int</span> position)</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> <span class="keyword">return</span> item <span class="keyword">instanceof</span> ComputerData;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">convert</span><span class="params">(RecyclerViewHolder viewHolder, People item, <span class="keyword">int</span> position)</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> viewHolder.setText(R.id.computer_text, item.getMsg());</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><font size="4"><strong>欢迎star!!!</strong></font><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8D%E7%94%A8%E4%BD%A0%E7%9A%84%E9%92%A5%E5%8C%99%EF%BC%8C%E6%89%93%E5%BC%80%E6%88%91%E7%9A%84%E5%BF%83%E6%89%89.jpg" alt="Android" title="用你的钥匙,打开我的心扉"></p>
<p><strong>终结Adapter、ViewHolder,封装通用适配器</strong></p>
</summary>
<category term="Android联盟" scheme="https://tiimor.cn/categories/Android%E8%81%94%E7%9B%9F/"/>
<category term="Android-Adapter" scheme="https://tiimor.cn/tags/Android-Adapter/"/>
<category term="适配器终结者" scheme="https://tiimor.cn/tags/%E9%80%82%E9%85%8D%E5%99%A8%E7%BB%88%E7%BB%93%E8%80%85/"/>
</entry>
<entry>
<title>Android6.0+动态权限管理</title>
<link href="https://tiimor.cn/Android6-0-%E5%8A%A8%E6%80%81%E6%9D%83%E9%99%90%E7%AE%A1%E7%90%86/"/>
<id>https://tiimor.cn/Android6-0-动态权限管理/</id>
<published>2017-10-21T11:52:28.000Z</published>
<updated>2019-03-17T06:47:42.354Z</updated>
<content type="html"><![CDATA[<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8Dlistener%20your%20heart.jpg" alt="Android" title="listener your heart"></p><p><strong>封装Android依赖库,方便Android6.0+环境下动态申请权限。</strong></p><a id="more"></a><p>Android6.0以后,一些权限不只是通过AndroidManifest申明,还需要在代码申请,系统弹出权限申请弹框,让用户确认授权,才能使用这些权限,如:WRITE_EXTERNAL_STORAGE。</p><h1 id="Android权限"><a href="#Android权限" class="headerlink" title="Android权限"></a>Android权限</h1><ul><li><p>需要申请权限<br>需要申请的权限分为9组,每组只要有一个权限申请成功,就默认整组权限都可以使用。</p> <figure class="highlight stylus"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line">group:android<span class="selector-class">.permission-group</span><span class="selector-class">.CONTACTS</span></span><br><span class="line"> permission:android<span class="selector-class">.permission</span><span class="selector-class">.WRITE_CONTACTS</span></span><br><span class="line"> permission:android<span class="selector-class">.permission</span><span class="selector-class">.GET_ACCOUNTS</span> </span><br><span class="line"> permission:android<span class="selector-class">.permission</span><span class="selector-class">.READ_CONTACTS</span></span><br><span class="line"></span><br><span class="line">group:android<span class="selector-class">.permission-group</span><span class="selector-class">.PHONE</span></span><br><span class="line"> permission:android<span class="selector-class">.permission</span><span class="selector-class">.READ_CALL_LOG</span></span><br><span class="line"> permission:android<span class="selector-class">.permission</span><span class="selector-class">.READ_PHONE_STATE</span> </span><br><span class="line"> permission:android<span class="selector-class">.permission</span><span class="selector-class">.CALL_PHONE</span></span><br><span class="line"> permission:android<span class="selector-class">.permission</span><span class="selector-class">.WRITE_CALL_LOG</span></span><br><span class="line"> permission:android<span class="selector-class">.permission</span><span class="selector-class">.USE_SIP</span></span><br><span class="line"> permission:android<span class="selector-class">.permission</span><span class="selector-class">.PROCESS_OUTGOING_CALLS</span></span><br><span class="line"> permission:com<span class="selector-class">.android</span><span class="selector-class">.voicemail</span><span class="selector-class">.permission</span><span class="selector-class">.ADD_VOICEMAIL</span></span><br><span class="line"></span><br><span class="line">group:android<span class="selector-class">.permission-group</span><span class="selector-class">.CALENDAR</span></span><br><span class="line"> permission:android<span class="selector-class">.permission</span><span class="selector-class">.READ_CALENDAR</span></span><br><span class="line"> permission:android<span class="selector-class">.permission</span><span class="selector-class">.WRITE_CALENDAR</span></span><br><span class="line"></span><br><span class="line">group:android<span class="selector-class">.permission-group</span><span class="selector-class">.CAMERA</span></span><br><span class="line"> permission:android<span class="selector-class">.permission</span><span class="selector-class">.CAMERA</span></span><br><span class="line"></span><br><span class="line">group:android<span class="selector-class">.permission-group</span><span class="selector-class">.SENSORS</span></span><br><span class="line"> permission:android<span class="selector-class">.permission</span><span class="selector-class">.BODY_SENSORS</span></span><br><span class="line"></span><br><span class="line">group:android<span class="selector-class">.permission-group</span><span class="selector-class">.LOCATION</span></span><br><span class="line"> permission:android<span class="selector-class">.permission</span><span class="selector-class">.ACCESS_FINE_LOCATION</span></span><br><span class="line"> permission:android<span class="selector-class">.permission</span><span class="selector-class">.ACCESS_COARSE_LOCATION</span></span><br><span class="line"></span><br><span class="line">group:android<span class="selector-class">.permission-group</span><span class="selector-class">.STORAGE</span></span><br><span class="line"> permission:android<span class="selector-class">.permission</span><span class="selector-class">.READ_EXTERNAL_STORAGE</span></span><br><span class="line"> permission:android<span class="selector-class">.permission</span><span class="selector-class">.WRITE_EXTERNAL_STORAGE</span></span><br><span class="line"></span><br><span class="line">group:android<span class="selector-class">.permission-group</span><span class="selector-class">.MICROPHONE</span></span><br><span class="line"> permission:android<span class="selector-class">.permission</span><span class="selector-class">.RECORD_AUDIO</span></span><br><span class="line"></span><br><span class="line">group:android<span class="selector-class">.permission-group</span><span class="selector-class">.SMS</span></span><br><span class="line"> permission:android<span class="selector-class">.permission</span><span class="selector-class">.READ_SMS</span></span><br><span class="line"> permission:android<span class="selector-class">.permission</span><span class="selector-class">.RECEIVE_WAP_PUSH</span></span><br><span class="line"> permission:android<span class="selector-class">.permission</span><span class="selector-class">.RECEIVE_MMS</span></span><br><span class="line"> permission:android<span class="selector-class">.permission</span><span class="selector-class">.RECEIVE_SMS</span></span><br><span class="line"> permission:android<span class="selector-class">.permission</span><span class="selector-class">.SEND_SMS</span></span><br><span class="line"> permission:android<span class="selector-class">.permission</span><span class="selector-class">.READ_CELL_BROADCASTS</span></span><br></pre></td></tr></tbody></table></figure></li><li><p>普通权限,在AndroidManifest.xml中申明即可</p> <figure class="highlight stylus"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.ACCESS_LOCATION_EXTRA_COMMANDS</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.ACCESS_NETWORK_STATE</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.ACCESS_NOTIFICATION_POLICY</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.ACCESS_WIFI_STATE</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.ACCESS_WIMAX_STATE</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.BLUETOOTH</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.BLUETOOTH_ADMIN</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.BROADCAST_STICKY</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.CHANGE_NETWORK_STATE</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.CHANGE_WIFI_MULTICAST_STATE</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.CHANGE_WIFI_STATE</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.CHANGE_WIMAX_STATE</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.DISABLE_KEYGUARD</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.EXPAND_STATUS_BAR</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.FLASHLIGHT</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.GET_ACCOUNTS</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.GET_PACKAGE_SIZE</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.INTERNET</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.KILL_BACKGROUND_PROCESSES</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.MODIFY_AUDIO_SETTINGS</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.NFC</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.READ_SYNC_SETTINGS</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.READ_SYNC_STATS</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.RECEIVE_BOOT_COMPLETED</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.REORDER_TASKS</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.REQUEST_INSTALL_PACKAGES</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.SET_TIME_ZONE</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.SET_WALLPAPER</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.SET_WALLPAPER_HINTS</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.SUBSCRIBED_FEEDS_READ</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.TRANSMIT_IR</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.USE_FINGERPRINT</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.VIBRATE</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.WAKE_LOCK</span></span><br><span class="line">android<span class="selector-class">.permission</span><span class="selector-class">.WRITE_SYNC_SETTINGS</span></span><br><span class="line">com<span class="selector-class">.android</span><span class="selector-class">.alarm</span><span class="selector-class">.permission</span><span class="selector-class">.SET_ALARM</span></span><br><span class="line">com<span class="selector-class">.android</span><span class="selector-class">.launcher</span><span class="selector-class">.permission</span><span class="selector-class">.INSTALL_SHORTCUT</span></span><br><span class="line">com<span class="selector-class">.android</span><span class="selector-class">.launcher</span><span class="selector-class">.permission</span><span class="selector-class">.UNINSTALL_SHORTCUT</span></span><br></pre></td></tr></tbody></table></figure></li></ul><h1 id="动态申请"><a href="#动态申请" class="headerlink" title="动态申请"></a>动态申请</h1><ul><li><p>设置targetSdkVersion为23以上</p></li><li><p>在AndroidManifest.xml中申明需要的权限,包括普通权限和需要申请的权限</p></li><li><p>对于申请的权限,需要在代码中申请</p><ol><li><p>检测权限,使用ContextCompat可以在任意地方使用该方式</p><figure class="highlight gauss"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 检测权限是否被授权</span></span><br><span class="line"><span class="keyword">if</span> (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED)</span><br></pre></td></tr></tbody></table></figure></li><li><p>如果未授权,则<strong>必须在Activity中申请</strong></p><figure class="highlight css"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">Activity</span><span class="selector-class">.requestPermissions</span>()</span><br></pre></td></tr></tbody></table></figure><p><img src="https://github.com/VeiZhang/Permission/blob/master/imags/%E7%94%B3%E8%AF%B7%E6%9D%83%E9%99%90-%E7%B3%BB%E7%BB%9F%E5%BC%B9%E6%A1%86.png?raw=true" alt="icon_permission_request" title="申请权限-系统弹框"></p></li><li><p>如果用户拒绝,并且点击了“不再询问”,当再次使用步骤2申请时,界面上不会有任何反应,因此需要判断用户是否点击“不再询问”,<strong>在Activity中</strong>使用<code>shouldShowRequestPermissionRationale</code>方法判断。然后自定义弹框给用户,让用户进入Setting应用去开启权限。<br><img src="https://github.com/VeiZhang/Permission/blob/master/imags/%E6%8B%92%E7%BB%9D-%E4%B8%8D%E5%86%8D%E8%AF%A2%E9%97%AE.png?raw=true" alt="icon_permission_denied" title="拒绝-不再询问"></p><p><img src="https://github.com/VeiZhang/Permission/blob/master/imags/%E4%B8%8D%E5%9C%A8%E8%AF%A2%E9%97%AE%E6%97%B6%EF%BC%8C%E8%87%AA%E5%AE%9A%E4%B9%89%E5%BC%B9%E6%A1%86%E8%BF%9B%E5%85%A5Setting%E5%BA%94%E7%94%A8.png?raw=true" alt="icon_permission_no_remind" title="不在询问时,自定义弹框进入Setting应用"></p><p><img src="https://github.com/VeiZhang/Permission/blob/master/imags/Setting%E5%BA%94%E7%94%A8%E6%8E%88%E6%9D%83%E6%9D%83%E9%99%90.png?raw=true" alt="icon_permission_setting" title="Setting应用授权权限"></p><p>注意:对于“不再询问”的理解:</p><figure class="highlight yaml"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">在6.0时代,需要在程序运行时获取相关权限,展开一个对话框询问是否授予该程序相应权限。</span></span><br><span class="line"><span class="string">从第二次开始运行的时候,会增加一个选项框,“以后不再询问”,如果选择了这个选项,那么以后程序不会再询问是否授予权限了。</span></span><br><span class="line"><span class="string">这时候选择了确认倒还好,之后倒方便了。</span></span><br><span class="line"><span class="string">如果选择了拒绝,那之后也不会显示对话框,但是权限一直是拒绝的。这样是非常不好的体验,不知道的还以为程序崩溃了。</span></span><br><span class="line"><span class="string">所以,我们需要在这个时候也显示相应对话框[自定义的对话框]来告诉用户</span></span><br><span class="line"></span><br><span class="line"><span class="string">第一次请求时,返回false</span></span><br><span class="line"><span class="string">如果应用之前请求过此权限但用户拒绝了请求,此方法将返回</span> <span class="literal">true</span><span class="string">。</span></span><br><span class="line"><span class="string">注:如果用户在过去拒绝了权限请求,并在权限请求系统对话框中选择了</span> <span class="string">Don’t</span> <span class="string">ask</span> <span class="string">again</span> <span class="string">选项,此方法将返回</span> <span class="literal">false</span><span class="string">。</span></span><br><span class="line"><span class="string">如果设备规范禁止应用具有该权限,此方法也会返回</span> <span class="literal">false</span><span class="string">。</span></span><br><span class="line"></span><br><span class="line"><span class="string">如果想判断是否拒绝权限,需要在请求一次之后的Failure回调里,再次执行shouldShowRequestPermissionRationale方法,返回</span> <span class="literal">false</span><span class="string">为拒绝</span></span><br></pre></td></tr></tbody></table></figure></li></ol></li></ul><h1 id="权限申请封装依赖库"><a href="#权限申请封装依赖库" class="headerlink" title="权限申请封装依赖库"></a>权限申请封装依赖库</h1><p><strong><a href="https://github.com/VeiZhang/Permission" target="_blank" rel="noopener">Permission</a>,欢迎start!!!</strong></p><p>将动态申请权限的步骤封装起来,方便使用:</p><ul><li>链式申请和回调</li><li>可在任何地方调用,不限于Activity</li><li>自定义进入Setting应用的提示</li></ul><h2 id="引入"><a href="#引入" class="headerlink" title="引入"></a>引入</h2><figure class="highlight gradle"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">compile</span> <span class="string">'com.excellence:permission:1.0.0'</span></span><br></pre></td></tr></tbody></table></figure><h2 id="使用示例"><a href="#使用示例" class="headerlink" title="使用示例"></a>使用示例</h2><figure class="highlight aspectj"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 申请单个权限</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> singleRequest()</span><br><span class="line">{</span><br><span class="line"> PermissionRequest.with(<span class="keyword">this</span>).permission(WRITE_EXTERNAL_STORAGE).request(<span class="keyword">new</span> IPermissionListener()</span><br><span class="line"> {</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> onPermissionsGranted()</span><br><span class="line"> {</span><br><span class="line"> Toast.makeText(MainActivity.<span class="keyword">this</span>, <span class="string">"申请单个权限成功"</span>, Toast.LENGTH_SHORT).show();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> onPermissionsDenied()</span><br><span class="line"> {</span><br><span class="line"> Toast.makeText(MainActivity.<span class="keyword">this</span>, <span class="string">"申请单个权限失败"</span>, Toast.LENGTH_SHORT).show();</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 选中“不在询问”情况下,拒绝时默认情况显示SettingDialog的弹框;</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * 可以自定义弹框,提示用户进入Setting应用,在rationale监听中提示</span></span><br><span class="line"><span class="comment"> * 注意:请使用PermissionActivity的引用,因为我将回调全部在PermissionActivity处理了</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> singleRequest()</span><br><span class="line">{</span><br><span class="line">PermissionRequest.with(<span class="keyword">this</span>).rationale(<span class="keyword">new</span> IRationaleListener()</span><br><span class="line">{</span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> OnRationale(<span class="keyword">final</span> PermissionActivity activity)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">new</span> SettingDialog(activity).setTitle(<span class="string">"Warning"</span>).setOnCancelListener(<span class="keyword">new</span> SettingDialog.OnCancelListener()</span><br><span class="line">{</span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> onCancel()</span><br><span class="line">{</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 点击取消时,认为请求失败</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">activity.permissionsDenied();</span><br><span class="line">}</span><br><span class="line">}).show();</span><br><span class="line">}</span><br><span class="line">}).permission(WRITE_EXTERNAL_STORAGE).request(<span class="keyword">new</span> IPermissionListener()</span><br><span class="line">{</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> onPermissionsGranted()</span><br><span class="line">{</span><br><span class="line">Toast.makeText(MainActivity.<span class="keyword">this</span>, <span class="string">"申请单个权限成功"</span>, Toast.LENGTH_SHORT).show();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> onPermissionsDenied()</span><br><span class="line">{</span><br><span class="line">Toast.makeText(MainActivity.<span class="keyword">this</span>, <span class="string">"申请单个权限失败"</span>, Toast.LENGTH_SHORT).show();</span><br><span class="line">}</span><br><span class="line">});</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 申请多个权限</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> multiRequest()</span><br><span class="line">{</span><br><span class="line"> PermissionRequest.with(<span class="keyword">this</span>).permission(READ_CONTACTS, CAMERA).request(<span class="keyword">new</span> IPermissionListener()</span><br><span class="line"> {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> onPermissionsGranted()</span><br><span class="line"> {</span><br><span class="line"> Toast.makeText(MainActivity.<span class="keyword">this</span>, <span class="string">"申请多个权限成功"</span>, Toast.LENGTH_SHORT).show();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> onPermissionsDenied()</span><br><span class="line"> {</span><br><span class="line"> Toast.makeText(MainActivity.<span class="keyword">this</span>, <span class="string">"申请多个权限失败"</span>, Toast.LENGTH_SHORT).show();</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h1 id="感谢"><a href="#感谢" class="headerlink" title="感谢"></a>感谢</h1><ul><li><a href="https://github.com/yanzhenjie/AndPermission" target="_blank" rel="noopener">yanzhenjie</a></li><li><a href="https://github.com/tbruyelle/RxPermissions" target="_blank" rel="noopener">tbruyelle</a></li><li><a href="https://github.com/googlesamples/easypermissions" target="_blank" rel="noopener">googlesamples</a></li></ul><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8Dlistener%20your%20heart.jpg" alt="Android" title="listener your heart"></p>
<p><strong>封装Android依赖库,方便Android6.0+环境下动态申请权限。</strong></p>
</summary>
<category term="Android联盟" scheme="https://tiimor.cn/categories/Android%E8%81%94%E7%9B%9F/"/>
<category term="Android6.0+" scheme="https://tiimor.cn/tags/Android6-0/"/>
<category term="动态权限" scheme="https://tiimor.cn/tags/%E5%8A%A8%E6%80%81%E6%9D%83%E9%99%90/"/>
</entry>
<entry>
<title>Android-DIY-ShimmerTextView</title>
<link href="https://tiimor.cn/Android-DIY-ShimmerTextView/"/>
<id>https://tiimor.cn/Android-DIY-ShimmerTextView/</id>
<published>2017-09-24T05:46:44.000Z</published>
<updated>2019-03-17T06:47:42.353Z</updated>
<content type="html"><![CDATA[<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8Dandroid%E6%98%9F%E7%B3%BB.jpg" alt="Android" title="android星系"></p><p><strong>Android自定义控件:闪烁文字效果</strong></p><a id="more"></a><hr><p><a href="https://github.com/VeiZhang/DIY-TextView/blob/master/ShimmerTextView/src/main/java/com/excellence/shimmer/Widget/GradientTextView.java" target="_blank" rel="noopener">源码传送门</a></p><hr><h2 id="效果展示"><a href="#效果展示" class="headerlink" title="效果展示"></a>效果展示</h2><p><img src="https://github.com/VeiZhang/MyTextView/blob/master/images/ShimmerTextView.gif?raw=true" alt="ShimmerTextView"></p><h2 id="讲解"><a href="#讲解" class="headerlink" title="讲解"></a>讲解</h2><p>使用线性渲染LinearGradient设置画笔的着色器Shader,Matrix用于对图像的图形处理,然后不停绘制文本。</p><figure class="highlight aspectj"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">protected</span> <span class="keyword">void</span> onSizeChanged(<span class="keyword">int</span> w, <span class="keyword">int</span> h, <span class="keyword">int</span> oldw, <span class="keyword">int</span> oldh)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">super</span>.onSizeChanged(w, h, oldw, oldh);</span><br><span class="line"><span class="keyword">if</span> (mViewWidth == <span class="number">0</span>)</span><br><span class="line">{</span><br><span class="line">mViewWidth = getMeasuredWidth();</span><br><span class="line"><span class="keyword">if</span> (mViewWidth > <span class="number">0</span>)</span><br><span class="line">{</span><br><span class="line">mPaint = getPaint();</span><br><span class="line"><span class="comment">// 设置线性渲染</span></span><br><span class="line">mLinearGradient = <span class="keyword">new</span> LinearGradient(-mViewWidth, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="keyword">new</span> <span class="keyword">int</span>[] { <span class="number">0x59ffffff</span>, <span class="number">0xffffffff</span>, <span class="number">0x59ffffff</span> }, <span class="keyword">new</span> <span class="keyword">float</span>[] { <span class="number">0</span>, <span class="number">0.5</span>f, <span class="number">1</span> }, Shader.TileMode.CLAMP);</span><br><span class="line"><span class="comment">// 设置Paint着色器</span></span><br><span class="line">mPaint.setShader(mLinearGradient);</span><br><span class="line">mGradientMatrix = <span class="keyword">new</span> Matrix();</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">protected</span> <span class="keyword">void</span> onDraw(Canvas canvas)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">super</span>.onDraw(canvas);</span><br><span class="line"><span class="keyword">if</span> (mGradientMatrix != <span class="keyword">null</span>)</span><br><span class="line">{</span><br><span class="line">mTranslate += mViewWidth / <span class="number">10</span>;</span><br><span class="line"><span class="keyword">if</span> (mTranslate > <span class="number">2</span> * mViewWidth)</span><br><span class="line">{</span><br><span class="line">mTranslate = -mViewWidth;</span><br><span class="line">}</span><br><span class="line">mGradientMatrix.setTranslate(mTranslate, <span class="number">0</span>);</span><br><span class="line">mLinearGradient.setLocalMatrix(mGradientMatrix);</span><br><span class="line">postInvalidateDelayed(<span class="number">50</span>);</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8Dandroid%E6%98%9F%E7%B3%BB.jpg" alt="Android" title="android星系"></p>
<p><strong>Android自定义控件:闪烁文字效果</strong></p>
</summary>
<category term="DIY" scheme="https://tiimor.cn/categories/DIY/"/>
<category term="Android" scheme="https://tiimor.cn/tags/Android/"/>
<category term="自定义控件" scheme="https://tiimor.cn/tags/%E8%87%AA%E5%AE%9A%E4%B9%89%E6%8E%A7%E4%BB%B6/"/>
</entry>
<entry>
<title>Android热修复-Sophix</title>
<link href="https://tiimor.cn/Android%E7%83%AD%E4%BF%AE%E5%A4%8D-Sophix/"/>
<id>https://tiimor.cn/Android热修复-Sophix/</id>
<published>2017-09-17T03:52:10.000Z</published>
<updated>2019-03-17T06:47:42.366Z</updated>
<content type="html"><![CDATA[<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8D%E7%94%BB%E4%B8%AD%E7%9A%84android.jpg" alt="Android" title="画中的android"></p><p>以往当Android App出现bug的时候,甚至仅仅是修改一行代码,都要重新发布新版本对bug进行修复,这样带来的缺点是明显的,需要用户重新升级app,覆盖率太慢,成本太高。所以就出现了热修复技术,通过打补丁的方式,通过从服务器下载补丁包,然后对有问题的类中出问题的方法,进行替换,优点是用户无感知修复,无需下载新的应用,代价小。对比其他的热修复方案,<strong>来耍一耍阿里-Sophix</strong> 。</p><a id="more"></a><h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><h3 id="“冷热”"><a href="#“冷热”" class="headerlink" title="“冷热”"></a>“冷热”</h3><ul><li><strong>插件化</strong> - apk 分为宿主和插件部分,插件在需要的时候才加载进来</li><li><strong>热修复</strong> – 更新的类或者插件粒度较小的时候,我们会称之为热修复,一般用于修复bug</li><li><strong>热更新</strong> – 2016 Google 的 Android Studio 推出了Instant Run 功能 同时提出了3个名词</li><li><strong>热部署</strong> – 方法内的简单修改,无需重启app和Activity。 </li><li><strong>暖部署</strong> – app无需重启,但是activity需要重启,比如资源的修改。 </li><li><strong>冷部署</strong> – app需要重启,比如继承关系的改变或方法的签名变化等。</li></ul><h3 id="热修复特点"><a href="#热修复特点" class="headerlink" title="热修复特点"></a>热修复特点</h3><ul><li>无需重新发版,实时高效热修复</li><li>用户无感知修复,无需下载新的应用,无需重装App,代价小</li><li>修复成功率高,把损失降到最低</li></ul><h3 id="阿里热修复方案对比"><a href="#阿里热修复方案对比" class="headerlink" title="阿里热修复方案对比"></a>阿里热修复方案对比</h3><table><thead><tr><th>方案对比</th><th><a href="https://github.com/alibaba/AndFix" target="_blank" rel="noopener">Andfix开源版本</a></th><th>阿里Hotfix 1.X</th><th><a href="https://github.com/aliyun/alicloud-android-demo/tree/master/hotfix_android_demo" target="_blank" rel="noopener">阿里Hotfix最新版 (Sophix)</a></th></tr></thead><tbody><tr><td>方法替换</td><td>支持,除部分情况[0]</td><td>支持,除部分情况</td><td>全部支持</td></tr><tr><td>方法增加减少</td><td>不支持</td><td>不支持</td><td>以冷启动方式支持[1]</td></tr><tr><td>方法反射调用</td><td>只支持静态方法</td><td>只支持静态方法</td><td>以冷启动方式支持</td></tr><tr><td>即时生效</td><td>支持</td><td>支持</td><td>视情况支持[2]</td></tr><tr><td>多DEX</td><td>不支持</td><td>支持</td><td>支持</td></tr><tr><td>资源更新</td><td>不支持</td><td>不支持</td><td>支持</td></tr><tr><td>so库更新</td><td>不支持</td><td>不支持</td><td>支持</td></tr><tr><td>Android版本</td><td>支持2.3~7.0</td><td>支持2.3~6.0</td><td>全部支持包含7.0以上</td></tr><tr><td>已有机型</td><td>大部分支持[3]</td><td>大部分支持</td><td>全部支持</td></tr><tr><td>安全机制</td><td>无</td><td>加密传输及签名校验</td><td>加密传输及签名校验</td></tr><tr><td>性能损耗</td><td>低,几乎无损耗</td><td>低,几乎无损耗</td><td>低,仅冷启动情况下有些损耗</td></tr><tr><td>生成补丁</td><td>繁琐,命令行操作</td><td>繁琐,命令行操作</td><td>便捷,图形化界面</td></tr><tr><td>补丁大小</td><td>不大,仅变动的类</td><td>小,仅变动的方法</td><td>不大,仅变动的资源和代码[4]</td></tr><tr><td>服务端支持</td><td>无</td><td>支持服务端控制[5]</td><td>支持服务端控制</td></tr></tbody></table><p>说明:</p><ul><li>[0] 部分情况指的是构造方法、参数数目大于8或者参数包括long,double,float基本类型的方法。</li><li>[1] 冷启动方式,指的是需要重启app在下次启动时才能生效。 </li><li>[2] 对于Andfix及Hotfix 1.X能够支持的代码变动情况,都能做到即时生效。而对于其他代码变动较大的情况,会走冷启动方式,此时就无法做到即时生效。 </li><li>[3] Hotfix 1.X已经支持绝大部分主流手机,只是在X86设备以及修改了虚拟机底层结构的ROM上不支持。 </li><li>[4] 由于支持了资源和库,如果有这些方面的更新,就会导致的补丁变大一些,这个是很正常的。并且由于只包含差异的部分,所以补丁已经是最大程度的小了。 </li><li>[5] 提供服务端的补丁发布和停发、版本控制和灰度功能,存储开发者上传的补丁包。</li></ul><h3 id="其他热修复方案"><a href="#其他热修复方案" class="headerlink" title="其他热修复方案"></a>其他热修复方案</h3><table><thead><tr><th>方案</th><th>作者</th></tr></thead><tbody><tr><td><a href="https://github.com/Tencent/tinker" target="_blank" rel="noopener">Tinker</a></td><td>微信(apk补丁)</td></tr><tr><td><a href="https://github.com/Meituan-Dianping/Robust" target="_blank" rel="noopener">Robust</a></td><td>美团</td></tr><tr><td><a href="https://github.com/eleme/Amigo" target="_blank" rel="noopener">Amigo</a></td><td>饿了么(apk补丁)</td></tr><tr><td><a href="https://github.com/jasonross/Nuwa" target="_blank" rel="noopener">Nuwa</a></td><td>个人开发者</td></tr><tr><td><a href="https://github.com/alibaba/dexposed" target="_blank" rel="noopener">Dexposed</a></td><td></td></tr><tr><td><a href="https://github.com/dodola/RocooFix" target="_blank" rel="noopener">RocooFix</a></td><td>个人开发者</td></tr></tbody></table><h2 id="集成Sophix"><a href="#集成Sophix" class="headerlink" title="集成Sophix"></a>集成Sophix</h2><h3 id="注册阿里云账号"><a href="#注册阿里云账号" class="headerlink" title="注册阿里云账号"></a>注册<a href="https://www.aliyun.com" target="_blank" rel="noopener">阿里云</a>账号</h3><p><strong><a href="https://help.aliyun.com/document_detail/53238.html?spm=5176.doc53240.3.2.xk9U86" target="_blank" rel="noopener">创建App文档传送门</a></strong></p><ul><li>注册完开发者账号,成功登录后进入控制台,添加移动热修复服务。<br><img src="http://cdn.tiimor.cn/images/%E6%B7%BB%E5%8A%A0%E7%A7%BB%E5%8A%A8%E7%83%AD%E4%BF%AE%E5%A4%8D.png" alt="添加移动热修复"></li></ul><ul><li><p>开通热修复服务后,跳转到热修复产品界面-<strong>App管理</strong>,创建App “Sophix测试”,创建完成后会出现两个平台的App列表:iOS和Android<br><img src="http://cdn.tiimor.cn/images/Sophix%E6%B5%8B%E8%AF%95.png" alt="Sophix测试"></p></li><li><p>点击管理进入Android平台,在客户端里,需要使用到AppId、APPSecret、RSA密钥<br><img src="http://cdn.tiimor.cn/images/Sophix%E5%BF%85%E8%A6%81%E4%BF%A1%E6%81%AF.png" alt="Sophix必要信息"></p></li></ul><h3 id="客户端集成"><a href="#客户端集成" class="headerlink" title="客户端集成"></a>客户端集成</h3><p><strong><a href="https://help.aliyun.com/document_detail/53240.html?spm=5176.doc53287.2.1.xLNLZz#1.1%20Android%20SDK%E5%8F%8A%E5%B7%A5%E5%85%B7%E4%B8%8B%E8%BD%BD" target="_blank" rel="noopener">Sophix文档传送门</a></strong></p><ul><li><p>引入maven依赖仓库<br>在项目app下的build.gradle中添加maven仓库地址和版本依赖</p><ul><li><p>添加maven仓库地址:</p><figure class="highlight nginx"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">repositories</span> {</span><br><span class="line"> <span class="section">maven</span> {</span><br><span class="line"> <span class="attribute">url</span> <span class="string">"http://maven.aliyun.com/nexus/content/repositories/releases"</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li><p>添加依赖:</p><figure class="highlight gradle"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">compile</span> <span class="string">'com.aliyun.ams:alicloud-android-hotfix:3.1.2'</span></span><br></pre></td></tr></tbody></table></figure></li></ul></li></ul><p><img src="http://cdn.tiimor.cn/images/%E5%BC%95%E5%85%A5Sophix%E4%BB%93%E5%BA%93.png" alt="引入Sophix依赖"></p><ul><li><p>添加使用权限</p><figure class="highlight applescript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><! <span class="comment">-- 网络权限 --></span></span><br><span class="line"><uses-permission android:<span class="built_in">name</span>=<span class="string">"android.permission.INTERNET"</span> /></span><br><span class="line"><uses-permission android:<span class="built_in">name</span>=<span class="string">"android.permission.ACCESS_NETWORK_STATE"</span> /></span><br><span class="line"><uses-permission android:<span class="built_in">name</span>=<span class="string">"android.permission.ACCESS_WIFI_STATE"</span> /></span><br><span class="line"><! <span class="comment">-- 外部存储读权限,调试工具加载本地补丁需要 --></span></span><br><span class="line"><uses-permission android:<span class="built_in">name</span>=<span class="string">"android.permission.READ_EXTERNAL_STORAGE"</span>/></span><br></pre></td></tr></tbody></table></figure></li><li><p>配置AndroidManifest文件<br>在application节点里添加配置,用之前在阿里云上创建的App的配置信息AppId、APPSecret、RSA密钥替换value的值:</p><figure class="highlight haskell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><application></span><br><span class="line"><meta-<span class="class"><span class="keyword">data</span></span></span><br><span class="line">android:name=<span class="string">"com.taobao.android.hotfix.IDSECRET"</span></span><br><span class="line">android:value=<span class="string">"App ID"</span> /></span><br><span class="line"><meta-<span class="class"><span class="keyword">data</span></span></span><br><span class="line">android:name=<span class="string">"com.taobao.android.hotfix.APPSECRET"</span></span><br><span class="line">android:value=<span class="string">"App Secret"</span> /></span><br><span class="line"><meta-<span class="class"><span class="keyword">data</span></span></span><br><span class="line">android:name=<span class="string">"com.taobao.android.hotfix.RSASECRET"</span></span><br><span class="line">android:value=<span class="string">"RSA密钥"</span> /></span><br><span class="line">···</span><br><span class="line"></application></span><br></pre></td></tr></tbody></table></figure></li></ul><p>可参考官方的动图示例:<br><img src="http://cdn.tiimor.cn/images/gif-Sophix.gif" alt="gif-Sophix"></p><ul><li>Application接入SDK<br>其他接口使用请查看SDK文档<figure class="highlight processing"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 初始化Sophix</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> initSophix()</span><br><span class="line">{</span><br><span class="line"><span class="keyword">String</span> appVersion;</span><br><span class="line"><span class="keyword">try</span></span><br><span class="line">{</span><br><span class="line">appVersion = getPackageManager().getPackageInfo(getPackageName(), <span class="number">0</span>).versionName;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">catch</span> (Exception e)</span><br><span class="line">{</span><br><span class="line">e.printStackTrace();</span><br><span class="line">appVersion = <span class="string">"1.0.0"</span>;</span><br><span class="line">}</span><br><span class="line">SophixManager.getInstance().setContext(<span class="keyword">this</span>).setAppVersion(appVersion).setAesKey(<span class="keyword">null</span>).setEnableDebug(<span class="keyword">true</span>).setPatchLoadStatusStub(<span class="keyword">new</span> PatchLoadStatusListener()</span><br><span class="line">{</span><br><span class="line">@Override</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> onLoad(<span class="keyword">final</span> <span class="built_in">int</span> mode, <span class="keyword">final</span> <span class="built_in">int</span> code, <span class="keyword">final</span> <span class="keyword">String</span> info, <span class="keyword">final</span> <span class="built_in">int</span> handlePatchVersion)</span><br><span class="line">{</span><br><span class="line"><span class="comment">// 补丁加载回调信息</span></span><br><span class="line">StringBuilder msg = <span class="keyword">new</span> StringBuilder();</span><br><span class="line">msg.<span class="built_in">append</span>(<span class="string">"Mode:"</span>).<span class="built_in">append</span>(mode).<span class="built_in">append</span>(<span class="string">"\n"</span>);</span><br><span class="line">msg.<span class="built_in">append</span>(<span class="string">"Code:"</span>).<span class="built_in">append</span>(code).<span class="built_in">append</span>(<span class="string">"\n"</span>);</span><br><span class="line">msg.<span class="built_in">append</span>(<span class="string">"Info:"</span>).<span class="built_in">append</span>(info).<span class="built_in">append</span>(<span class="string">"\n"</span>);</span><br><span class="line">msg.<span class="built_in">append</span>(<span class="string">"HandlePatchVersion:"</span>).<span class="built_in">append</span>(handlePatchVersion).<span class="built_in">append</span>(<span class="string">"\n"</span>);</span><br><span class="line"><span class="keyword">if</span> (mDisplayListener != <span class="keyword">null</span>)</span><br><span class="line">mDisplayListener.handle(msg.toString());</span><br><span class="line"></span><br><span class="line"><span class="comment">// 补丁加载回调通知</span></span><br><span class="line"><span class="keyword">switch</span> (code)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">case</span> PatchStatus.CODE_LOAD_SUCCESS:</span><br><span class="line"><span class="comment">// 表明补丁加载成功</span></span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">case</span> PatchStatus.CODE_LOAD_RELAUNCH:</span><br><span class="line"><span class="comment">// 表明新补丁生效需要重启. 开发者可提示用户或者强制重启;</span></span><br><span class="line"><span class="comment">// 建议: 用户可以监听进入后台事件,然后调用killProcessSafely自杀</span></span><br><span class="line"><span class="comment">// 注意:不可以直接Process.killProcess(Process.myPid())来杀进程,这样会扰乱Sophix的内部状态。</span></span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">case</span> PatchStatus.CODE_LOAD_FAIL:</span><br><span class="line"><span class="comment">// 内部引擎异常,推荐此时清空本地补丁,防止失败补丁重复加载</span></span><br><span class="line">SophixManager.getInstance().cleanPatches();</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">default</span>:</span><br><span class="line"><span class="comment">// 其它错误信息,查看PatchStatus类说明</span></span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">}).initialize();</span><br><span class="line"><span class="comment">// 加载新的补丁包</span></span><br><span class="line">SophixManager.getInstance().queryAndLoadNewPatch();</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><p>至此,Sophix配置完成。</p><h3 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h3><ul><li>客户端第一版本,如图,打包成Sophix_V1.apk<br><img src="http://cdn.tiimor.cn/images/Sophix_V1.png" alt="Sophix_V1"></li></ul><ul><li><p>客户端补丁版本,如图,打包成Sophix_V2.apk<br><img src="http://cdn.tiimor.cn/images/Sophix_V2.png" alt="Sophix_V2"></p></li><li><p>生成补丁,<a href="https://help.aliyun.com/document_detail/53247.html?spm=5176.2020520107.0.0.719a83830VCANh" target="_blank" rel="noopener">生成补丁文档传送门</a><br>Windows下载阿里补丁工具<a href="http://ams-hotfix-repo.oss-cn-shanghai.aliyuncs.com/SophixPatchTool_windows.zip?spm=5176.doc53247.2.2.lz640u&file=SophixPatchTool_windows.zip" target="_blank" rel="noopener">SophixPatchTool</a>,运行SophixPatchTool.exe,添加包,如果有签名等设置,则点击设置,配置相应的签名等,然后点击“Go”,生成补丁,即<strong>sophix-patch.jar</strong>:<br><img src="http://cdn.tiimor.cn/images/Sophix%E8%A1%A5%E4%B8%81%E7%94%9F%E6%88%90.png" alt="Sophix补丁生成" title="生成补丁"></p></li><li><p>上传补丁<br>进入阿里云热修复App管理的Android平台里,即查看AppId、APPSecret、RSA密钥那个页面,添加新版本,成功添加版本后,点击查看详情进入,上传刚刚生成的sophix-patch.jar补丁。<br><img src="http://cdn.tiimor.cn/images/Sophix%E6%B7%BB%E5%8A%A0%E8%A1%A5%E4%B8%81%E7%89%88%E6%9C%AC.png" alt="Sophix添加补丁版本" title="添加补丁版本"><br><img src="http://cdn.tiimor.cn/images/Sophix%E4%B8%8A%E4%BC%A0%E8%A1%A5%E4%B8%81.png" alt="Sophix上传补丁" title="上传补丁"></p></li><li><p>本地测试<br>安装Sophix_V1.apk,同时将补丁sophix-patch.jar放到Android设备的目录里:<code>/sdcard/sophix-patch.jar</code>,下载<a href="http://ams-hotfix-repo.oss-cn-shanghai.aliyuncs.com/hotfix_debug_tool-release.apk?spm=5176.2020520107.0.0.719a8383MbMrkQ&file=hotfix_debug_tool-release.apk" target="_blank" rel="noopener">hotfixdebug</a>工具,安装后,打开进入调试apk,配置如下:<br><img src="http://cdn.tiimor.cn/images/Sophix%E6%9C%AC%E5%9C%B0%E6%B5%8B%E8%AF%95.png" alt="Sophix本地测试"></p></li></ul><p>测试成功<br><img src="http://cdn.tiimor.cn/images/Sophix%E6%9C%AC%E5%9C%B0%E6%B5%8B%E8%AF%95%E6%88%90%E5%8A%9F.png" alt="Sophix本地测试成功"></p><ul><li>发布<br>进入阿里云的补丁详情页面,点击发布。再次进行调试,这次不用Sophix调试工具;先卸载已安装的Sophix_V1,重新安装,打开后再次等待检测补丁更新,再次出现提示ok。<br><img src="http://cdn.tiimor.cn/images/Sophix%E5%8F%91%E5%B8%83.png" alt="Sophix发布"></li></ul><h2 id="推广"><a href="#推广" class="headerlink" title="推广"></a>推广</h2><p>有兴趣的童鞋可以看看阿里出品的<strong><a href="http://note.youdao.com/noteshare?id=1c2b949de5e966ecaf37b363500e6491&sub=3CAAE8571EC74E4B99C841DD208C23CC" target="_blank" rel="noopener">热修复原理宝典</a></strong></p><script src="//cdn.bootcss.com/jquery.lazyload/1.9.1/jquery.lazyload.min.js"></script><div class="hexo-img-readStream"><style type="text/css">.hexo-image-stream-lazy {display:block;}.hexo-img-readStream{width:100%;max-width:1100px;margin:3% auto}div.hexo-img-readStream readItems{ background: #fefefe;box-shadow: 0 1px 2px rgba(34, 25, 25, 0.2);margin: 0 1% 3%;padding: 3%;padding-bottom: 9px;display: inline-block;max-width: 25%;}div.hexo-img-readStream readItems img{padding-bottom:10px;margin-top: 0.7em;}div.hexo-img-readStream readItems figcaption{font-size:.8rem;color:#999;line-height:1.5;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align: center;}div.hexo-img-readStream small{font-size:1rem;float:right;text-transform:uppercase;color:#aaa}div.hexo-img-readStream small a{color:#666;text-decoration:none;transition:.4s color}@media screen and (max-width:750px){.hexo-img-readStream{column-gap:0}}</style><readitems><a href="http://cdn.tiimor.cn/images/%E9%98%BF%E9%87%8C%E7%83%AD%E4%BF%AE%E5%A4%8D%E5%8E%9F%E7%90%86%E5%AE%9D%E5%85%B8.png" target="_blank" rel="external"><img class="hexo-image-stream-lazy nofancy" src="//ww4.sinaimg.cn/large/e724cbefgw1etyppy7bgwg2001001017.gif" data-original="http://cdn.tiimor.cn/images/%E9%98%BF%E9%87%8C%E7%83%AD%E4%BF%AE%E5%A4%8D%E5%8E%9F%E7%90%86%E5%AE%9D%E5%85%B8.png"><noscript><img src="http://cdn.tiimor.cn/images/%E9%98%BF%E9%87%8C%E7%83%AD%E4%BF%AE%E5%A4%8D%E5%8E%9F%E7%90%86%E5%AE%9D%E5%85%B8.png"></noscript></a><figcaption><a href="http://note.youdao.com/noteshare?id=1c2b949de5e966ecaf37b363500e6491&sub=3CAAE8571EC74E4B99C841DD208C23CC" target="_blank" rel="external" id="favorite">阿里热修复原理宝典</a></figcaption></readitems> </div><script type="text/javascript">$('img.hexo-image-stream-lazy').lazyload({ effect:'fadeIn' });</script><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
<p><img src="http://cdn.tiimor.cn/images/%E5%94%AF%E7%BE%8E%E7%AF%87%EF%BC%8D%E7%94%BB%E4%B8%AD%E7%9A%84android.jpg" alt="Android" title="画中的android"></p>
<p>以往当Android App出现bug的时候,甚至仅仅是修改一行代码,都要重新发布新版本对bug进行修复,这样带来的缺点是明显的,需要用户重新升级app,覆盖率太慢,成本太高。所以就出现了热修复技术,通过打补丁的方式,通过从服务器下载补丁包,然后对有问题的类中出问题的方法,进行替换,优点是用户无感知修复,无需下载新的应用,代价小。对比其他的热修复方案,<strong>来耍一耍阿里-Sophix</strong> 。</p>
</summary>
<category term="好好学习" scheme="https://tiimor.cn/categories/%E5%A5%BD%E5%A5%BD%E5%AD%A6%E4%B9%A0/"/>
<category term="Android" scheme="https://tiimor.cn/tags/Android/"/>
<category term="热修复" scheme="https://tiimor.cn/tags/%E7%83%AD%E4%BF%AE%E5%A4%8D/"/>
<category term="Sophix" scheme="https://tiimor.cn/tags/Sophix/"/>
</entry>
</feed>