-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
511 lines (288 loc) · 307 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
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Cyning</title>
<link href="/atom.xml" rel="self"/>
<link href="http://ownwell.github.io/"/>
<updated>2019-07-22T11:41:09.301Z</updated>
<id>http://ownwell.github.io/</id>
<author>
<name>cyning</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>插件化之Hook Activity</title>
<link href="http://ownwell.github.io/2019/06/27/%E6%8F%92%E4%BB%B6%E5%8C%96%E4%B9%8BHookActivity/"/>
<id>http://ownwell.github.io/2019/06/27/插件化之HookActivity/</id>
<published>2019-06-27T14:11:25.000Z</published>
<updated>2019-07-22T11:41:09.301Z</updated>
<content type="html"><![CDATA[<p>插件化是最近几年的比较流行的技术,最近腾讯出了以<a href="https://github.com/Tencent/Shadow" target="_blank" rel="noopener">Shadow</a>,和其他框架比,官方介绍还是很多的。</p><a id="more"></a><p><img src="https://raw.githubusercontent.com/ownwell/image-bed/master/img/2019-07-02-22-42-40.png" alt></p><p>这个后面会探索下,一直觉得国内的开发者在我们自己的范围内玩的真的玩出花了(Google play是不允许这些动态化和热修的东西的,否则等着警告吧)。</p><p>为了看Shadow我又把之前看动态的文章给找出来,也算是复习了。<br>对于插件化,就要理解什么是插件化</p><h2><span id="插件化">插件化</span></h2><p>插件化技术最初源于免安装运行apk的想法,这个免安装的apk或者dex可以理解为插件。支持插件化的app(宿主App host)可以在运行时加载和运行插件,这样便可以将app中一些不常用的功能模块做成插件,一方面减小了安装包的大小,另一方面可以实现app功能的动态扩展(AB<br>Test 、换肤等)。</p><p>对于Activity的hook其实网上有很多文章,讲述怎么切入,今天我们就以滴滴的<code>VirtualAPK</code>为蓝本分析下怎么hook一个Activity.</p><h1><span id="如何hook-acitvity">如何Hook Acitvity</span></h1><p>对于一个Activity,我们如何Hook,就需要了解整个Activity的start过程。<br>为了更好理解Activity的启动过程,我们需要一步步分析不同的启动过程。</p><p>首先我们可以从Activity的启动过程讲起。</p><h2><span id="startactivity过程">startActivity过程</span></h2><p>我就以目前我常用的android27为基础来一步步跟踪如何启动Activity吧</p><ol><li><code>android.app.Activity</code> 入口是<code>startActivity</code><figure class="highlight java"><table><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Activity</span> </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">void</span> <span class="title">startActivity</span><span class="params">(Intent intent)</span> </span>{</span><br><span class="line"> <span class="keyword">this</span>.startActivity(intent, <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> <span class="keyword">void</span> <span class="title">startActivity</span><span class="params">(Intent intent, @Nullable Bundle options)</span> </span>{</span><br><span class="line"> <span class="comment">// 有options参数</span></span><br><span class="line"> <span class="keyword">if</span> (options != <span class="keyword">null</span>) {</span><br><span class="line"> startActivityForResult(intent, -<span class="number">1</span>, options);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// 无options参数</span></span><br><span class="line"> <span class="comment">// Note we want to go through this call for compatibility with</span></span><br><span class="line"> <span class="comment">// applications that may have overridden the method.</span></span><br><span class="line"> startActivityForResult(intent, -<span class="number">1</span>);</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><br><span class="line"> <span class="comment">// 无options参数最后也是默认传个空值</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">startActivityForResult</span><span class="params">(@RequiresPermission Intent intent, <span class="keyword">int</span> requestCode)</span> </span>{</span><br><span class="line"> startActivityForResult(intent, requestCode, <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="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">startActivityForResult</span><span class="params">(@RequiresPermission Intent intent, <span class="keyword">int</span> requestCode,</span></span></span><br><span class="line"><span class="function"><span class="params"> @Nullable Bundle options)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (mParent == <span class="keyword">null</span>) {</span><br><span class="line"> options = transferSpringboardActivityOptions(options);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 通过mInstrumentation来进行下一步</span></span><br><span class="line"> Instrumentation.ActivityResult ar =</span><br><span class="line"> mInstrumentation.execStartActivity(</span><br><span class="line"> <span class="keyword">this</span>, mMainThread.getApplicationThread(), mToken, <span class="keyword">this</span>,</span><br><span class="line"> intent, requestCode, options);</span><br><span class="line"> <span class="keyword">if</span> (ar != <span class="keyword">null</span>) {</span><br><span class="line"> mMainThread.sendActivityResult(</span><br><span class="line"> mToken, mEmbeddedID, requestCode, ar.getResultCode(),</span><br><span class="line"> ar.getResultData());</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (requestCode >= <span class="number">0</span>) {</span><br><span class="line"> <span class="comment">// If this start is requesting a result, we can avoid making</span></span><br><span class="line"> <span class="comment">// the activity visible until the result is received. Setting</span></span><br><span class="line"> <span class="comment">// this code during onCreate(Bundle savedInstanceState) or onResume() will keep the</span></span><br><span class="line"> <span class="comment">// activity hidden during this time, to avoid flickering.</span></span><br><span class="line"> <span class="comment">// This can only be done when a result is requested because</span></span><br><span class="line"> <span class="comment">// that guarantees we will get information back when the</span></span><br><span class="line"> <span class="comment">// activity is finished, no matter what happens to it.</span></span><br><span class="line"> mStartedActivity = <span class="keyword">true</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> cancelInputsAndStartExitTransition(options);</span><br><span class="line"> <span class="comment">// TODO Consider clearing/flushing other event sources and events for child windows.</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">if</span> (options != <span class="keyword">null</span>) {</span><br><span class="line"> mParent.startActivityFromChild(<span class="keyword">this</span>, intent, requestCode, options);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// Note we want to go through this method for compatibility with</span></span><br><span class="line"> <span class="comment">// existing applications that may have overridden it.</span></span><br><span class="line"> mParent.startActivityFromChild(<span class="keyword">this</span>, intent, requestCode);</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><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ol><p>而后若是parent为空,就不在分析了,大同小异。</p><ol start="2"><li>android.app.Instrumentationde</li></ol><figure class="highlight java"><table><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></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">Instrumentation</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> ActivityResult <span class="title">execStartActivity</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params"> Context who, IBinder contextThread, IBinder token, String target,</span></span></span><br><span class="line"><span class="function"><span class="params"> Intent intent, <span class="keyword">int</span> requestCode, Bundle options)</span> </span>{</span><br><span class="line"> IApplicationThread whoThread = (IApplicationThread) contextThread;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// ......省略非关键代码</span></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> intent.migrateExtraStreamToClipData();</span><br><span class="line"> intent.prepareToLeaveProcess(who);</span><br><span class="line"> <span class="comment">// 这个才是关键</span></span><br><span class="line"> <span class="keyword">int</span> result = ActivityManager.getService()</span><br><span class="line"> .startActivity(whoThread, who.getBasePackageName(), intent,</span><br><span class="line"> intent.resolveTypeIfNeeded(who.getContentResolver()),</span><br><span class="line"> token, target, requestCode, <span class="number">0</span>, <span class="keyword">null</span>, options);</span><br><span class="line"> checkStartActivityResult(result, intent);</span><br><span class="line"> } <span class="keyword">catch</span> (RemoteException e) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(<span class="string">"Failure from system"</span>, e);</span><br><span class="line"> }</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></pre></td></tr></table></figure><ol start="3"><li>最后还是要到<code>ActivityManager</code>走一遭</li></ol><p>通过ServiceManager获取远程服务AMS的Binder,然后通过这个Binder创建一个ActivityManagerProxy,将这个ActivityManagerProxy 对象返回</p><figure class="highlight java"><table><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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ActivityManager</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> IActivityManager <span class="title">getService</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> IActivityManagerSingleton.get();</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="keyword">static</span> <span class="keyword">final</span> Singleton<IActivityManager> IActivityManagerSingleton =</span><br><span class="line"> <span class="keyword">new</span> Singleton<IActivityManager>() {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">protected</span> IActivityManager <span class="title">create</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">final</span> IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);</span><br><span class="line"> <span class="keyword">final</span> IActivityManager am = IActivityManager.Stub.asInterface(b);</span><br><span class="line"> <span class="keyword">return</span> am;</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p> <code>ServiceManager.getService(Context.ACTIVITY_SERVICE)</code>这个我比较好奇,这个IBinder究竟是什么,我们最终定位到了<code>ActivityManagerService</code></p><ol start="4"><li><p>com.android.server.am.ActivityManagerService的.ActivityManagerService<br>入口是<code>startActivity</code></p><figure class="highlight java"><table><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 继承IActivityManager.Stub</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ActivityManagerService</span> <span class="keyword">extends</span> <span class="title">IActivityManager</span>.<span class="title">Stub</span></span></span><br><span class="line"><span class="class"> <span class="keyword">implements</span> <span class="title">Watchdog</span>.<span class="title">Monitor</span>, <span class="title">BatteryStatsImpl</span>.<span class="title">BatteryCallback</span> </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">final</span> <span class="keyword">int</span> <span class="title">startActivity</span><span class="params">(IApplicationThread caller, String callingPackage,</span></span></span><br><span class="line"><span class="function"><span class="params"> Intent intent, String resolvedType, IBinder resultTo, String resultWho, <span class="keyword">int</span> requestCode,</span></span></span><br><span class="line"><span class="function"><span class="params"> <span class="keyword">int</span> startFlags, ProfilerInfo profilerInfo, Bundle bOptions)</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,</span><br><span class="line"> resultWho, requestCode, startFlags, profilerInfo, bOptions,</span><br><span class="line"> UserHandle.getCallingUserId());</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> <span class="keyword">final</span> <span class="keyword">int</span> <span class="title">startActivityAsUser</span><span class="params">(IApplicationThread caller, String callingPackage,</span></span></span><br><span class="line"><span class="function"><span class="params"> Intent intent, String resolvedType, IBinder resultTo, String resultWho, <span class="keyword">int</span> requestCode,</span></span></span><br><span class="line"><span class="function"><span class="params"> <span class="keyword">int</span> startFlags, ProfilerInfo profilerInfo, Bundle bOptions, <span class="keyword">int</span> userId)</span> </span>{</span><br><span class="line"> enforceNotIsolatedCaller(<span class="string">"startActivity"</span>);</span><br><span class="line"> userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),</span><br><span class="line"> userId, <span class="keyword">false</span>, ALLOW_FULL_ONLY, <span class="string">"startActivity"</span>, <span class="keyword">null</span>);</span><br><span class="line"> <span class="comment">// <span class="doctag">TODO:</span> Switch to user app stacks here.</span></span><br><span class="line"> <span class="keyword">return</span> mActivityStarter.startActivityMayWait(caller, -<span class="number">1</span>, callingPackage, intent,</span><br><span class="line"> resolvedType, <span class="keyword">null</span>, <span class="keyword">null</span>, resultTo, resultWho, requestCode, startFlags,</span><br><span class="line"> profilerInfo, <span class="keyword">null</span>, <span class="keyword">null</span>, bOptions, <span class="keyword">false</span>, userId, <span class="keyword">null</span>, <span class="string">"startActivityAsUser"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>com.android.server.am.AcitivityStarter.java</p><figure class="highlight java"><table><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></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">AcitivityStarter</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">final</span> <span class="keyword">int</span> <span class="title">startActivityMayWait</span><span class="params">(IApplicationThread caller, <span class="keyword">int</span> callingUid,</span></span></span><br><span class="line"><span class="function"><span class="params"> String callingPackage, Intent intent, String resolvedType,</span></span></span><br><span class="line"><span class="function"><span class="params"> IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,</span></span></span><br><span class="line"><span class="function"><span class="params"> IBinder resultTo, String resultWho, <span class="keyword">int</span> requestCode, <span class="keyword">int</span> startFlags,</span></span></span><br><span class="line"><span class="function"><span class="params"> ProfilerInfo profilerInfo, WaitResult outResult,</span></span></span><br><span class="line"><span class="function"><span class="params"> Configuration globalConfig, Bundle bOptions, <span class="keyword">boolean</span> ignoreTargetSecurity, <span class="keyword">int</span> userId,</span></span></span><br><span class="line"><span class="function"><span class="params"> TaskRecord inTask, String reason)</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="comment">//.....</span></span><br><span class="line"> ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">final</span> ActivityRecord[] outRecord = <span class="keyword">new</span> ActivityRecord[<span class="number">1</span>];</span><br><span class="line"> <span class="keyword">int</span> res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType,</span><br><span class="line"> aInfo, rInfo, voiceSession, voiceInteractor,</span><br><span class="line"> resultTo, resultWho, requestCode, callingPid,</span><br><span class="line"> callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,</span><br><span class="line"> options, ignoreTargetSecurity, componentSpecified, outRecord, inTask,</span><br><span class="line"> reason);</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">int</span> <span class="title">startActivityLocked</span><span class="params">(IApplicationThread caller, Intent intent, Intent ephemeralIntent,</span></span></span><br><span class="line"><span class="function"><span class="params"> String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,</span></span></span><br><span class="line"><span class="function"><span class="params"> IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,</span></span></span><br><span class="line"><span class="function"><span class="params"> IBinder resultTo, String resultWho, <span class="keyword">int</span> requestCode, <span class="keyword">int</span> callingPid, <span class="keyword">int</span> callingUid,</span></span></span><br><span class="line"><span class="function"><span class="params"> String callingPackage, <span class="keyword">int</span> realCallingPid, <span class="keyword">int</span> realCallingUid, <span class="keyword">int</span> startFlags,</span></span></span><br><span class="line"><span class="function"><span class="params"> ActivityOptions options, <span class="keyword">boolean</span> ignoreTargetSecurity, <span class="keyword">boolean</span> componentSpecified,</span></span></span><br><span class="line"><span class="function"><span class="params"> ActivityRecord[] outActivity, TaskRecord inTask, String reason)</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="comment">//....</span></span><br><span class="line"></span><br><span class="line"> mLastStartActivityResult = startActivity(caller, intent, ephemeralIntent, resolvedType,</span><br><span class="line"> aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode,</span><br><span class="line"> callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,</span><br><span class="line"> options, ignoreTargetSecurity, componentSpecified, mLastStartActivityRecord,</span><br><span class="line"> inTask);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//....</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">int</span> <span class="title">startActivity</span><span class="params">(<span class="keyword">final</span> ActivityRecord r, ActivityRecord sourceRecord,</span></span></span><br><span class="line"><span class="function"><span class="params"> IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,</span></span></span><br><span class="line"><span class="function"><span class="params"> <span class="keyword">int</span> startFlags, <span class="keyword">boolean</span> doResume, ActivityOptions options, TaskRecord inTask,</span></span></span><br><span class="line"><span class="function"><span class="params"> ActivityRecord[] outActivity)</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> result = START_CANCELED;</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> mService.mWindowManager.deferSurfaceLayout();</span><br><span class="line"> <span class="comment">// 继续跟踪</span></span><br><span class="line"> result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,</span><br><span class="line"> startFlags, doResume, options, inTask, outActivity);</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> postStartActivityProcessing(r, result, mSupervisor.getLastStack().mStackId, mSourceRecord,</span><br><span class="line"> mTargetStack);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Note: This method should only be called from {@link startActivity}.</span></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">int</span> <span class="title">startActivityUnchecked</span><span class="params">(<span class="keyword">final</span> ActivityRecord r, ActivityRecord sourceRecord,</span></span></span><br><span class="line"><span class="function"><span class="params"> IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,</span></span></span><br><span class="line"><span class="function"><span class="params"> <span class="keyword">int</span> startFlags, <span class="keyword">boolean</span> doResume, ActivityOptions options, TaskRecord inTask,</span></span></span><br><span class="line"><span class="function"><span class="params"> ActivityRecord[] outActivity)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (mDoResume) {</span><br><span class="line"> <span class="keyword">final</span> ActivityRecord topTaskActivity =</span><br><span class="line"> mStartActivity.getTask().topRunningActivityLocked();</span><br><span class="line"> <span class="keyword">if</span> (!mTargetStack.isFocusable()</span><br><span class="line"> || (topTaskActivity != <span class="keyword">null</span> && topTaskActivity.mTaskOverlay</span><br><span class="line"> && mStartActivity != topTaskActivity)) {</span><br><span class="line"> </span><br><span class="line"> mTargetStack.ensureActivitiesVisibleLocked(<span class="keyword">null</span>, <span class="number">0</span>, !PRESERVE_WINDOWS);</span><br><span class="line"> </span><br><span class="line"> mWindowManager.executeAppTransition();</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (mTargetStack.isFocusable() && !mSupervisor.isFocusedStack(mTargetStack)) {</span><br><span class="line"> mTargetStack.moveToFront(<span class="string">"startActivityUnchecked"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 这一步不太好定位啊</span></span><br><span class="line"> mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,</span><br><span class="line"> mOptions);</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> mTargetStack.addRecentActivityLocked(mStartActivity);</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><br></pre></td></tr></table></figure></li><li><p><code>com.android.server.am.ActivityStackSupervisor</code>resumeFocusedStackTopActivityLocked</p><figure class="highlight java"><table><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></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">ActivityStackSupervisor</span> <span class="keyword">extends</span> <span class="title">ConfigurationContainer</span> <span class="keyword">implements</span> <span class="title">DisplayListener</span> </span>{</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">boolean</span> <span class="title">resumeFocusedStackTopActivityLocked</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params"> ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions)</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!readyToResume()) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (targetStack != <span class="keyword">null</span> && isFocusedStack(targetStack)) {</span><br><span class="line"> <span class="keyword">return</span> targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">final</span> ActivityRecord r = mFocusedStack.topRunningActivityLocked();</span><br><span class="line"> <span class="keyword">if</span> (r == <span class="keyword">null</span> || r.state != RESUMED) {</span><br><span class="line"> <span class="comment">// 栈顶元素</span></span><br><span class="line"> mFocusedStack.resumeTopActivityUncheckedLocked(<span class="keyword">null</span>, <span class="keyword">null</span>);</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (r.state == RESUMED) {</span><br><span class="line"> <span class="comment">// Kick off any lingering app transitions form the MoveTaskToFront operation.</span></span><br><span class="line"> mFocusedStack.executeAppTransition(targetOptions);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure></li><li><p><code>com.android.server.am.ActivityStack</code>的resumeTopActivityUncheckedLocked</p></li></ol><figure class="highlight java"><table><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="function"><span class="keyword">boolean</span> <span class="title">resumeTopActivityUncheckedLocked</span><span class="params">(ActivityRecord prev, ActivityOptions options)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (mStackSupervisor.inResumeTopActivity) {</span><br><span class="line"> <span class="comment">// Don't even start recursing.</span></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">false</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"> </span><br><span class="line"> mStackSupervisor.startSpecificActivityLocked(next, <span class="keyword">true</span>, <span class="keyword">true</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>在ActivityStack.resumeTopActivityInnerLocked方法中会去判断是否有Activity处于Resume状态,如果有的话会先让这个Activity执行Pausing过程,然后再执行startSpecificActivityLocked方法启动要启动Activity。</p><ol start="9"><li><code>com.android.server.am.ActivityStackSupervisor</code></li></ol><figure class="highlight java"><table><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></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">ActivityStackSupervisor</span> <span class="keyword">extends</span> <span class="title">ConfigurationContainer</span> <span class="keyword">implements</span> <span class="title">DisplayListener</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">startSpecificActivityLocked</span><span class="params">(ActivityRecord r,</span></span></span><br><span class="line"><span class="function"><span class="params"> <span class="keyword">boolean</span> andResume, <span class="keyword">boolean</span> checkConfig)</span> </span>{</span><br><span class="line"> <span class="comment">// .....</span></span><br><span class="line"> <span class="comment">// 庐山真面目</span></span><br><span class="line"> realStartActivityLocked(r, app, andResume, checkConfig);</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">realStartActivityLocked</span><span class="params">(ActivityRecord r, ProcessRecord app,</span></span></span><br><span class="line"><span class="function"><span class="params"> <span class="keyword">boolean</span> andResume, <span class="keyword">boolean</span> checkConfig)</span> <span class="keyword">throws</span> RemoteException </span>{</span><br><span class="line"></span><br><span class="line"> app..thread.scheduleLaunchActivity(<span class="keyword">new</span> Intent(r.intent), r.appToken,</span><br><span class="line"> System.identityHashCode(r), r.info,</span><br><span class="line"> <span class="comment">// <span class="doctag">TODO:</span> Have this take the merged configuration instead of separate global</span></span><br><span class="line"> <span class="comment">// and override configs.</span></span><br><span class="line"> mergedConfiguration.getGlobalConfiguration(),</span><br><span class="line"> mergedConfiguration.getOverrideConfiguration(), r.compat,</span><br><span class="line"> r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,</span><br><span class="line"> r.persistentState, results, newIntents, !andResume,</span><br><span class="line"> mService.isNextTransitionForward(), profilerInfo);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>app(ProcessRecord的对象)的thread实际上也是一个 IApplicationThread对象,实际上会调用的是应用进程的ApplicationThread。</p><ol start="10"><li><code>ApplicationThread</code></li></ol><figure class="highlight java"><table><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">ApplicationThread</span> <span class="keyword">extends</span> <span class="title">IApplicationThread</span>.<span class="title">Stub</span> </span>{</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">scheduleLaunchActivity</span><span class="params">(Intent intent, IBinder token, <span class="keyword">int</span> ident,</span></span></span><br><span class="line"><span class="function"><span class="params"> ActivityInfo info, Configuration curConfig, Configuration overrideConfig,</span></span></span><br><span class="line"><span class="function"><span class="params"> CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,</span></span></span><br><span class="line"><span class="function"><span class="params"> <span class="keyword">int</span> procState, Bundle state, PersistableBundle persistentState,</span></span></span><br><span class="line"><span class="function"><span class="params"> List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,</span></span></span><br><span class="line"><span class="function"><span class="params"> <span class="keyword">boolean</span> notResumed, <span class="keyword">boolean</span> isForward, ProfilerInfo profilerInfo)</span> </span>{</span><br><span class="line"></span><br><span class="line"> updateProcessState(procState, <span class="keyword">false</span>);</span><br><span class="line"></span><br><span class="line"> ActivityClientRecord r = <span class="keyword">new</span> ActivityClientRecord();</span><br><span class="line"></span><br><span class="line"> r.token = token;</span><br><span class="line"> r.ident = ident;</span><br><span class="line"> ...</span><br><span class="line"> updatePendingConfiguration(curConfig);</span><br><span class="line"></span><br><span class="line"> sendMessage(H.LAUNCH_ACTIVITY, r);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">sendMessage</span><span class="params">(<span class="keyword">int</span> what, Object obj)</span> </span>{</span><br><span class="line"> sendMessage(what, obj, <span class="number">0</span>, <span class="number">0</span>, <span class="keyword">false</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">sendMessage</span><span class="params">(<span class="keyword">int</span> what, Object obj, <span class="keyword">int</span> arg1, <span class="keyword">int</span> arg2, <span class="keyword">int</span> seq)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (DEBUG_MESSAGES) Slog.v(</span><br><span class="line"> TAG, <span class="string">"SCHEDULE "</span> + mH.codeToString(what) + <span class="string">" arg1="</span> + arg1 + <span class="string">" arg2="</span> + arg2 +</span><br><span class="line"> <span class="string">"seq= "</span> + seq);</span><br><span class="line"> Message msg = Message.obtain();</span><br><span class="line"> msg.what = what;</span><br><span class="line"> SomeArgs args = SomeArgs.obtain();</span><br><span class="line"> args.arg1 = obj;</span><br><span class="line"> args.argi1 = arg1;</span><br><span class="line"> args.argi2 = arg2;</span><br><span class="line"> args.argi3 = seq;</span><br><span class="line"> msg.obj = args;</span><br><span class="line"> mH.sendMessage(msg);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>mH实际上是个Handler,用来将处理binder信息的消息同步到主线程,可以在ActivityThread里查看H这个内部类。</p><ol start="11"><li><code>ActivityThread$H</code><figure class="highlight java"><table><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="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">H</span> <span class="keyword">extends</span> <span class="title">Handler</span> </span>{</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">handleMessage</span><span class="params">(Message msg)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (DEBUG_MESSAGES) Slog.v(TAG, <span class="string">">>> handling: "</span> + codeToString(msg.what));</span><br><span class="line"> <span class="keyword">switch</span> (msg.what) {</span><br><span class="line"> <span class="keyword">case</span> LAUNCH_ACTIVITY: {</span><br><span class="line"> Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, <span class="string">"activityStart"</span>);</span><br><span class="line"> <span class="keyword">final</span> ActivityClientRecord r = (ActivityClientRecord) msg.obj;</span><br><span class="line"></span><br><span class="line"> r.packageInfo = getPackageInfoNoCheck(</span><br><span class="line"> r.activityInfo.applicationInfo, r.compatInfo);</span><br><span class="line"> handleLaunchActivity(r, <span class="keyword">null</span>, <span class="string">"LAUNCH_ACTIVITY"</span>);</span><br><span class="line"> Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);</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">}</span><br></pre></td></tr></table></figure></li></ol><p>可以画出时序图如下:<br><img src="https://raw.githubusercontent.com/ownwell/image-bed/master/img/2019-07-06-20-04-19.png" alt></p><h2><span id="思考的问题">思考的问题</span></h2><p>看了刚才的Activity的启动过程,看着很复杂,不过很多地方我们开发中用到的地方很少,对于插件化,我们必须要思考几个问题:</p><ol><li>如何在宿主中加载额外的插件</li><li>如何做到宿主和插件的通信(eg:在宿主中,如何启动一个插件的?)</li><li>如何对不同的启动模式的区分(是栈顶复用 还是栈内复用,还是标准启动模式等)</li></ol><p>针对这写问题,下面会一一解决:</p><h3><span id="如何在宿主中加载额外的插件">如何在宿主中加载额外的插件</span></h3><p>这个问题后面会有介绍,暂时不考虑单独加载的问题,本文是入门篇,只考虑加载java文件下有,当时没有在Manifest文件中注册的Activity.</p><h3><span id="如何启动没在manifest文件中注册的activity">如何启动没在Manifest文件中注册的Activity</span></h3><p>若是你在项目中,写了一个Acitivy,当时没有在Manifest文件中注册注册,若是你直接启动会报一个类似这样的异常:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Caused by: android.content.ActivityNotFoundException: Unable to find explicit activity <span class="class"><span class="keyword">class</span> </span>{cc.cyning.hookactivity/cc.cyning.hookactivity.SecondActivity}; have you declared <span class="keyword">this</span> activity in your AndroidManifest.xml?</span><br></pre></td></tr></table></figure><p>其实我们可以从Activity的启动过程触发,搞一个偷天换日:</p><p>在启动时,把需要的启动的Activity(但没有在Manifest文件中注册的Activity,我们为了方便讲述,把它称为插件Activity)在启动前拦截住,把<code>插件Activity</code>替换成在<code>AndroidManifest.xml</code>已经注册的Activity(我们称之为占坑Activity).这样在AMS中能正常的校验。否则会报一个bad token的异常。</p><p>当SystemServer启动Activity时,需要去拦截判断下,若是之前用<code>占坑Activity</code>替换的,需要重新把<code>插件Activity</code>再替换回来。<br>这个就是代考的抢手,考试时枪手上,等考试结束了,被替考者再接着表演。</p><p>我们其实可以在启动之前,也就是<code>Instrumentation</code>的<code>execStartActivity</code>步骤中,去hook替换参数中的Intent:</p><figure class="highlight plain"><table><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">Intent intent1 = new Intent(who, ProxyActivity.class);</span><br><span class="line">intent1.setData(intent.getData());</span><br><span class="line">intent1.putExtra("oldIntent", intent);</span><br></pre></td></tr></table></figure><p><code>ProxyActivity</code>就是我们的<code>占坑Activity</code>,因为它已经在Manifest文件中注册了,AMS校验能通过。</p><p>在ApplicationThread处理完消息时,需要再传到AcitityThread的的Handler(也就是上文的H),我们能监听到它的消息类型,若是<code>startActivity</code>,再将<code>占坑Activity</code>替换成<code>插件Activity</code>。</p><figure class="highlight java"><table><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></pre></td><td class="code"><pre><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">handleMessage</span><span class="params">(Message msg)</span> </span>{</span><br><span class="line"> Log.i(<span class="string">"HookAmsUtil"</span>, <span class="string">"handleMessage"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//替换之前的Intent</span></span><br><span class="line"> <span class="keyword">if</span> (msg.what == LAUNCH_ACTIVITY) {</span><br><span class="line"> Log.i(<span class="string">"HookAmsUtil"</span>, <span class="string">"lauchActivity"</span>);</span><br><span class="line"> handleLauchActivity(msg);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> handler.handleMessage(msg);</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">handleLauchActivity</span><span class="params">(Message msg)</span> </span>{</span><br><span class="line"> Object obj = msg.obj;<span class="comment">//ActivityClientRecord</span></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> Field intentField = obj.getClass().getDeclaredField(<span class="string">"intent"</span>);</span><br><span class="line"> intentField.setAccessible(<span class="keyword">true</span>);</span><br><span class="line"> Intent proxyInent = (Intent) intentField.get(obj);</span><br><span class="line"> Intent realIntent = proxyInent.getParcelableExtra(<span class="string">"oldIntent"</span>);</span><br><span class="line"> <span class="comment">// 是占坑Activity替换的</span></span><br><span class="line"> <span class="keyword">if</span> (realIntent != <span class="keyword">null</span>) {</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 将插件的intent恢复</span></span><br><span class="line"> proxyInent.setComponent(realIntent.getComponent());</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> Log.i(<span class="string">"HookAmsUtil"</span>, <span class="string">"lauchActivity falied"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3><span id="如何对不同的启动模式的区分">如何对不同的启动模式的区分</span></h3><p>若是你给<code>插件Activity</code>设置了非标准启动方式,无论再hook<code>Instrumentation</code>或者<code>ActivityThread$H</code>都无法完美解决,那么怎么能解决不同的启动模式问题呢?<br>VirtualAPK的解决思路是在Manifest中多加几个启动方式不同的<code>占坑Activity</code>,<br><img src="https://raw.githubusercontent.com/ownwell/image-bed/master/img/2019-07-06-22-37-44.png" alt></p><h2><span id="总结">总结</span></h2><p>本文是从低配版的 <code>VirtualAPK</code>只是hook Activity的启动过程,首先是从startActivity,这个步骤如下:</p><ol><li>context的startActiivty</li><li>Instrumentation的execStartActivity</li></ol>]]></content>
<summary type="html">
<p>插件化是最近几年的比较流行的技术,最近腾讯出了以<a href="https://github.com/Tencent/Shadow" target="_blank" rel="noopener">Shadow</a>,和其他框架比,官方介绍还是很多的。</p>
</summary>
<category term="Android" scheme="http://ownwell.github.io/categories/Android/"/>
<category term="插件化" scheme="http://ownwell.github.io/tags/%E6%8F%92%E4%BB%B6%E5%8C%96/"/>
</entry>
<entry>
<title>alfred插件开发-将github作为图床</title>
<link href="http://ownwell.github.io/2019/06/20/2019-06-20-alfred%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91/"/>
<id>http://ownwell.github.io/2019/06/20/2019-06-20-alfred插件开发/</id>
<published>2019-06-20T15:27:11.000Z</published>
<updated>2019-07-06T14:52:57.915Z</updated>
<content type="html"><![CDATA[<p>使用mac也有些年头了,关于工具,可能常见的基本上都尝试过,比如使用<code>mac air</code>传递文件,用<code>brew</code>安装软件,<code>iterm</code>上搞很炫酷的主题,给 <code>VS Code</code>搞爆炸或者毛玻璃的效果,这些可能不是每天都用,但有一个软件是我每天必用,那就是<code>Alfred</code>,这个mac上的神器,我也是看了池建强老师的mac talk才开始使用,不过真的我就把原生的<code>Spotlight</code>直接给废弃到一边了,若是没有体会过的,或许你不知道<code>Alfred</code>是个啥玩意。</p><a id="more"></a><p>说不明白的,就直接给个gif吧。</p><p><img src="https://raw.githubusercontent.com/ownwell/image-bed/master/img/alfred_image.gif" alt></p><p>上图中用了两个<code>Alfred</code>插件:</p><ol><li>一个插件是将图片做了宽高的压缩的处理 </li><li>另一个图片主动推到了github上并将生成的链接复制到粘贴板。<br>有了这两个插件,图片的缩放和图片的上传问题都可以很好的解决,快捷高效,最主要都是免费的。想想是不是都觉得很美啊。</li></ol><p>由上面我们可以知道:<code>Alfred</code>不简单搜索,他更是我们很多工具的入口。</p><p>闲话不多说,我直接说下第二个刚才实例中的第二个插件,这个插件是我自己开发,目前相对来说只能满足我们将github作为图床的功能,还有很多bug,不过在写这篇博客时,我已经通过这个上传了多个图片了。<br>先说下插件的我们的我们需要的几个步骤:</p><ol><li>下载新版本的<code>Alfred</code>。</li><li>新建一个插件的入口</li></ol><p><img src="https://raw.githubusercontent.com/ownwell/image-bed/master/img/2019-06-22-23-12-48.png" alt></p><blockquote><p>其中图中的第二步需要选一个<code>blank workFlower</code></p></blockquote><ol start="3"><li><p>新建一个hotKey,并在后面插入一个脚本。<br><img src="https://raw.githubusercontent.com/ownwell/image-bed/master/img/alfred_image_3.gif" alt></p><blockquote><p>这个hotKey可以触发Alfred的Workflow,Workflow是一个任务(上图中的是个action脚本,主要输入的快捷键是hotKey,就会自动触发这个脚本),我们可以通过python脚本,shell等其他脚本来开发。</p></blockquote></li><li><p>下面需要我们去编写自己的image_bed.py<br><img src="https://raw.githubusercontent.com/ownwell/image-bed/master/img/2019-06-23-22-58-16.png" alt></p></li></ol><p>进入我们的脚本编辑文件下,目前只有一个<code>info.plist</code></p><p>需要先将<a href="http://www.deanishe.net/alfred-workflow/index.html" target="_blank" rel="noopener">Alfred-Workflow</a>对python支持的库放到跟路径下。</p><p>在咱们自己创建的alfred的workFlower,最后创建一个空的文件,命名为<code>image_bed.py</code>,这样文件结构就就是:</p><figure class="highlight python"><table><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><br><span class="line">|___workflow </span><br><span class="line">| |______init__.py </span><br><span class="line">| |______background.py </span><br><span class="line">|</span><br><span class="line">|__image_bed.py </span><br><span class="line">|</span><br><span class="line">|___info。plist</span><br><span class="line"></span><br><span class="line">`</span><br></pre></td></tr></table></figure><p>我就简单把代码贴出来吧</p><figure class="highlight java"><table><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></pre></td><td class="code"><pre><span class="line"># -*- coding:utf-8 -*-</span><br><span class="line"><span class="keyword">import</span> json,sys</span><br><span class="line"><span class="keyword">import</span> img_config as config</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">import</span> subprocess</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line">from workflow <span class="keyword">import</span> Workflow</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> clipboard</span><br><span class="line"></span><br><span class="line">img_dir = config.img_cfg[<span class="string">'img_dir'</span>] </span><br><span class="line"></span><br><span class="line">CLIPBOARD_EXCEPTIONS = (</span><br><span class="line"> clipboard.WriteFileError,</span><br><span class="line"> clipboard.FileTypeUnsupportedError,</span><br><span class="line"> clipboard.NotImageError</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">imgUrl = <span class="string">""</span></span><br><span class="line"></span><br><span class="line"><span class="function">def <span class="title">timestamp_to_date</span><span class="params">(time_stamp, format_string=<span class="string">"%Y-%m-%d-%H-%M-%S"</span>)</span>:</span></span><br><span class="line"><span class="function"> time_array </span>= time.localtime(time_stamp)</span><br><span class="line"> str_date = time.strftime(format_string, time_array)</span><br><span class="line"> <span class="keyword">return</span> str_date</span><br><span class="line"></span><br><span class="line"><span class="function">def <span class="title">_convert_to_png</span><span class="params">(src_path, dest_path)</span>:</span></span><br><span class="line"><span class="function"> """转换图片格式为png</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"> :param src_path: 源文件</span></span><br><span class="line"><span class="function"> :param dest_path: 目标文件</span></span><br><span class="line"><span class="function"> """</span></span><br><span class="line"><span class="function"> os.<span class="title">system</span><span class="params">(<span class="string">'sips -s format png {} --out {}'</span>.format(src_path, dest_path)</span>)</span></span><br><span class="line"><span class="function"> os.<span class="title">remove</span><span class="params">(src_path)</span></span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function">def <span class="title">saveClipboardImg</span><span class="params">()</span>:</span></span><br><span class="line"><span class="function"> global imgUrl</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"> <span class="keyword">try</span>:</span></span><br><span class="line"><span class="function"> img_path </span>= clipboard.get_pasteboard_img_path()</span><br><span class="line"> except CLIPBOARD_EXCEPTIONS as error:</span><br><span class="line"> # notice(str(error))</span><br><span class="line"> pass</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> notice(str(img_path))</span><br><span class="line"> file_name = os.path.split(img_path)[-<span class="number">1</span>]</span><br><span class="line"> file_type = file_name.split(<span class="string">'.'</span>)[-<span class="number">1</span>]</span><br><span class="line"> <span class="keyword">if</span> file_type == <span class="string">'tiff'</span>:</span><br><span class="line"> date = <span class="keyword">int</span>(time.time())</span><br><span class="line"> now = timestamp_to_date(date)</span><br><span class="line"> filename = str(now) </span><br><span class="line"> new_img_path = <span class="string">'{}{}.png'</span>.format(img_dir, filename)</span><br><span class="line"> imgUrl = <span class="string">'{}{}.png'</span>.format(<span class="string">'https://raw.githubusercontent.com/ownwell/image-bed/master/img/'</span>, filename)</span><br><span class="line"> notice(imgUrl)</span><br><span class="line"></span><br><span class="line"> # tiff --> png</span><br><span class="line"> _convert_to_png(img_path, new_img_path)</span><br><span class="line"> img_path = new_img_path</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> name = img_path.split(<span class="string">"/"</span>)[-<span class="number">1</span>]</span><br><span class="line"></span><br><span class="line"> imgUrl = <span class="string">'{}{}'</span>.format(<span class="string">'https://raw.githubusercontent.com/ownwell/image-bed/master/img/'</span>, name)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> # from PIL import Image</span><br><span class="line"> # if isinstance(im, Image.Image):</span><br><span class="line"> # global imgUrl</span><br><span class="line"></span><br><span class="line"> # print(im.format, im.size, im.mode)</span><br><span class="line"> # date = int(time.time())</span><br><span class="line"> # now = timestamp_to_date(date)</span><br><span class="line"> # filename = str(now) + ".jpg"</span><br><span class="line"> # imgUrl = "https://github.com/ownwell/image-bed/raw/master/"+filename</span><br><span class="line"> # notice(imgUrl)</span><br><span class="line"> # im.save(path + filename, im.format)</span><br><span class="line"> # width, height = im.size</span><br><span class="line"> # pix = im.load()</span><br><span class="line"> </span><br><span class="line"> # else:</span><br><span class="line"> # notice("not image")</span><br><span class="line"> # pass</span><br><span class="line"><span class="function">def <span class="title">pullToGithub</span><span class="params">()</span>:</span></span><br><span class="line"><span class="function"> cmd </span>= <span class="string">''</span><span class="string">'cd '</span><span class="string">''</span> + img_dir + <span class="string">''</span><span class="string">' ;git add .;git commit -m "blog_img";git push origin master</span></span><br><span class="line"><span class="string"> '</span><span class="string">''</span></span><br><span class="line"> subprocess.call(cmd, shell=True)</span><br><span class="line"></span><br><span class="line"><span class="function">def <span class="title">write_to_pasteboard</span><span class="params">(text)</span>:</span></span><br><span class="line"><span class="function"> """内容写入剪贴板</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"> :param text: 写入内容</span></span><br><span class="line"><span class="function"> """</span></span><br><span class="line"><span class="function"> os.<span class="title">system</span><span class="params">(<span class="string">'echo \'{}\' | pbcopy'</span>.format(text)</span>)</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function">def <span class="title">print_pasteboard_content</span><span class="params">()</span>:</span></span><br><span class="line"><span class="function"> """从剪贴板打印出内容"""</span></span><br><span class="line"><span class="function"> write_command </span>= (</span><br><span class="line"> <span class="string">'osascript -e \'tell application '</span></span><br><span class="line"> <span class="string">'"System Events" to keystroke "v" using command down\''</span></span><br><span class="line"> )</span><br><span class="line"> os.system(write_command)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function">def <span class="title">notice</span><span class="params">(msg, title=<span class="string">''</span>)</span>:</span></span><br><span class="line"><span class="function"> """通知 </span></span><br><span class="line"><span class="function"> :param msg: 通知消息</span></span><br><span class="line"><span class="function"> :param title: 通知标题</span></span><br><span class="line"><span class="function"> """</span></span><br><span class="line"><span class="function"> pass</span></span><br><span class="line"><span class="function"> os.<span class="title">system</span><span class="params">(<span class="string">'osascript -e \'display notification "{}" with title "{}"\''</span>.format(msg, title)</span>)</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function">def <span class="title">main</span><span class="params">(wf)</span>:</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"> <span class="title">saveClipboardImg</span><span class="params">()</span></span></span><br><span class="line"><span class="function"> <span class="title">pullToGithub</span><span class="params">()</span></span></span><br><span class="line"><span class="function"> <span class="title">notice</span><span class="params">(imgUrl)</span></span></span><br><span class="line"><span class="function"> md_img </span>= <span class="string">'![]({})'</span>.format(imgUrl)</span><br><span class="line"> write_to_pasteboard(md_img)</span><br><span class="line"> print_pasteboard_content();</span><br><span class="line"></span><br><span class="line"> # notice("1")</span><br><span class="line"> # saveClipboardImg()</span><br><span class="line"> # write_to_pasteboard("nihao")</span><br><span class="line"> # print_pasteboard_content()</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line"> wf = Workflow(libraries=[<span class="string">'./lib'</span>])</span><br><span class="line"> sys.exit(wf.run(main))</span><br></pre></td></tr></table></figure><p>解释下,<br>首先 python脚本获取粘贴板里的内容<br>而后,判断粘贴板里的内容是否为图片或者图片的路径,并将图片资源或给定路径图片放到我们的本地的git仓库<br>最后,通过触发git的add和push(<code>pullToGithub</code>方法里)将图片推到git的远程仓库上</p><h3><span id="总结">总结</span></h3><ol><li>hotkey触发运行pytho脚本</li><li>python脚本的作用就是赋值粘贴板资源到本地的git仓库,而后上传到git的远程端,并将图片列检拼接出</li></ol><p>具体代码已经上传到github上,<br><a href="https://raw.githubusercontent.com/ownwell/image-bed/master/img/user.workflow.6C32BCCF-38F2-4E9F-8839-B7968C0C0285" target="_blank" rel="noopener">https://raw.githubusercontent.com/ownwell/image-bed/master/img/user.workflow.6C32BCCF-38F2-4E9F-8839-B7968C0C0285</a></p>]]></content>
<summary type="html">
<p>使用mac也有些年头了,关于工具,可能常见的基本上都尝试过,比如使用<code>mac air</code>传递文件,用<code>brew</code>安装软件,<code>iterm</code>上搞很炫酷的主题,给 <code>VS Code</code>搞爆炸或者毛玻璃的效果,这些可能不是每天都用,但有一个软件是我每天必用,那就是<code>Alfred</code>,这个mac上的神器,我也是看了池建强老师的mac talk才开始使用,不过真的我就把原生的<code>Spotlight</code>直接给废弃到一边了,若是没有体会过的,或许你不知道<code>Alfred</code>是个啥玩意。</p>
</summary>
<category term="Tools" scheme="http://ownwell.github.io/categories/Tools/"/>
<category term="Tools" scheme="http://ownwell.github.io/tags/Tools/"/>
</entry>
<entry>
<title>Flutter 请求网络数据和列表展示</title>
<link href="http://ownwell.github.io/2019/05/16/flutter-learning-2/"/>
<id>http://ownwell.github.io/2019/05/16/flutter-learning-2/</id>
<published>2019-05-16T10:53:58.000Z</published>
<updated>2019-07-26T12:21:33.929Z</updated>
<content type="html"><![CDATA[<p>之前简单学习了Flutter的入门程序,那么今天就从实例出发,搞一个简单的demo。</p><a id="more"></a><p>先看下效果图吧:</p><p><img src="https://raw.githubusercontent.com/ownwell/image-bed/master/img/Kapture2019-07-2619.21.20.gif" alt></p><p>实现上面的功能,可以拆分为以下几步:</p><ol><li>请求网络接口数据,用异步的方式来请求网络接口</li><li>数据的解析和列表的展示。请求的数据实际上是json,json的解析和用ListView的展示</li><li>页面的跳转和参数的传递。因为页面之间的跳转也带有参数,如何跳转呢?</li><li>webview的展示</li></ol><h2><span id="请求网络接口数据">请求网络接口数据</span></h2><p>网络数据请求可以用最简单的自带的<code>http</code>。</p><ol><li>添加依赖<br>在使用前记得去项目的根项目下<code>pubspec.yaml</code>添加<code>http</code>模块的依赖项<figure class="highlight js"><table><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><br><span class="line">dependencies:</span><br><span class="line"> flutter:</span><br><span class="line"> sdk: flutter</span><br><span class="line"> </span><br><span class="line"></span><br><span class="line"> <span class="comment">// htt依赖项</span></span><br><span class="line"> http: <span class="string">'>=0.11.3+12'</span></span><br></pre></td></tr></table></figure></li></ol><p><code>pubspec.yaml</code>实际上类似于<code>maven</code>的<code>pom.xml</code>和<code>gradle</code>配置文件.</p><ol start="2"><li>调用<br>接着就可以直接通过网络去请求网络数据啦:<figure class="highlight java"><table><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="function"><span class="keyword">void</span> <span class="title">loadData</span><span class="params">()</span> async </span>{</span><br><span class="line"> String requestURL = <span class="string">'http://gank.io/api/today'</span>;</span><br><span class="line"> Client client = Client();</span><br><span class="line"> Response response = await client.get(requestURL);</span><br><span class="line"></span><br><span class="line"> String jsonString = response.body;</span><br><span class="line"> print(jsonString)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ol><p><code>async</code>和<code>await</code>要配套使用哦。</p><ol start="3"><li>注意事项 </li></ol><ul><li>请求数据尽量再这个widget(initState)的初始化时,去请求。 </li><li>记得添加网络访问的权限 uses-permission android:name=”android.permission.INTERNET”/></li></ul><blockquote><p>更多关于网络数据请求的内容可以参考<a href="https://flutter.dev/docs/cookbook" target="_blank" rel="noopener">CookBook</a>的文章:<br><a href="https://flutter.dev/docs/cookbook/networking/fetch-data" target="_blank" rel="noopener">Fetch data from the internet</a></p></blockquote><h2><span id="数据的解析和列表的展示">数据的解析和列表的展示</span></h2><ol><li>解析</li></ol><p>由于返回的数据是json,json在dart直接可以用索引和取key值的方式去解析。</p><figure class="highlight json"><table><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><br><span class="line"><span class="attr">"category"</span>: [],</span><br><span class="line"><span class="attr">"error"</span>: <span class="literal">false</span>,</span><br><span class="line"><span class="attr">"results"</span>: {</span><br><span class="line"><span class="attr">"Android"</span>: [],</span><br><span class="line"><span class="attr">"App"</span>: [],</span><br><span class="line"><span class="attr">"iOS"</span>: [],</span><br><span class="line"><span class="attr">"休息视频"</span>: [],</span><br><span class="line"><span class="attr">"前端"</span>: [],</span><br><span class="line"><span class="attr">"拓展资源"</span>: [],</span><br><span class="line"><span class="attr">"瞎推荐"</span>: [],</span><br><span class="line"><span class="attr">"福利"</span>: []</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>若想获取到节点IOS中的数组,你只需要下面一步:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dataList = ((json.decode(jsonString))[<span class="string">'results'</span>])[<span class="string">'iOS'</span>];</span><br></pre></td></tr></table></figure><p>想用json去解码,而后直接用取值的方式拿到json节点数据。</p><ol start="2"><li>展示<br>列表的展示尽量用和Android对应的组件,便于用自己熟悉的东西去对应,所以直接就用<code>ListView</code>。<br>View的使用,只能去一个个去熟悉,我就直接去找ListView的Doc,实现代码如下:</li></ol><figure class="highlight java"><table><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">return</span> ListView.separated(</span><br><span class="line"> itemCount: dataList.length,</span><br><span class="line"> itemBuilder: (BuildContext context, <span class="keyword">int</span> index) {</span><br><span class="line"> <span class="comment">// index就是每个item的索引值</span></span><br><span class="line"></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">var</span> item = dataList[index];</span><br><span class="line"> <span class="keyword">return</span> ListTile(</span><br><span class="line"> <span class="comment">//标题</span></span><br><span class="line"> title: Text(dataList[index][<span class="string">'desc'</span>] ),</span><br><span class="line"> <span class="comment">// 子标题</span></span><br><span class="line"> subtitle: Text(dataList[index][<span class="string">'publishedAt'</span>]),</span><br><span class="line"> onTap: () {</span><br><span class="line"> <span class="comment">//点击 页面跳转</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></span><br><span class="line"> },</span><br><span class="line"> <span class="comment">// 分割线</span></span><br><span class="line"> separatorBuilder: (context, index) {</span><br><span class="line"> <span class="keyword">return</span> Container(</span><br><span class="line"> color: Colors.blue,</span><br><span class="line"> height: <span class="number">3</span>,</span><br><span class="line"> );</span><br><span class="line"> },</span><br><span class="line"> );</span><br></pre></td></tr></table></figure><h2><span id="页面的跳转和参数的传递">页面的跳转和参数的传递</span></h2><p>页面是app交互的最基础的单位,就像Android原生的<code>Fragment</code>或者<code>Activity</code>。所以你极大不可能只写一个Fragment或者Activity(这里,会有杠精出现),所以页面间跳转就是页面交互的一个很必要手段,MD的设计中有很多炫酷的交互,也是为了更好的交互。</p><p>那么页面怎么跳转呢?我们给出一个简单的方式:Navigator.push。</p><figure class="highlight java"><table><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">// onTap 点击时操作</span></span><br><span class="line"> Navigator.push(context,</span><br><span class="line"> <span class="keyword">new</span> MaterialPageRoute(builder: (context) => <span class="keyword">new</span> SecondScreen</span><br><span class="line"> (url:item[<span class="string">'url'</span>], title: item[<span class="string">'desc'</span>])),</span><br><span class="line"> );</span><br></pre></td></tr></table></figure><p>其中<code>SecondScreen</code>就是我们的下一个页面,而<code>url</code>和<code>title</code>就是下个页面需要用到的浏览器需要展示的<code>url</code>和标题.</p><p>这样第一个页面就跳到第二个页面时, 可以将url和title传给第二个。<br>第二个页面怎么接收的呢?<br>可以知道第二个页面实际上是传给第二个页面的构造函数。所以接收的方式也是直接通过构造方法获取到:</p><figure class="highlight java"><table><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">var</span> url =<span class="string">''</span>;</span><br><span class="line"><span class="keyword">var</span> title = <span class="string">''</span>;</span><br><span class="line"><span class="keyword">final</span> _key = UniqueKey();</span><br><span class="line"></span><br><span class="line">SecondScreen({<span class="keyword">this</span>.url, <span class="keyword">this</span>.title});</span><br></pre></td></tr></table></figure><h2><span id="webview的展示">webview的展示</span></h2><ol><li><p>引导webview的依赖</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">webview_flutter: '0.3.10+3'</span><br></pre></td></tr></table></figure></li><li><p>加载url</p></li></ol><figure class="highlight java"><table><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">WebView(</span><br><span class="line"> key: _key,</span><br><span class="line"> javascriptMode: JavascriptMode.unrestricted,</span><br><span class="line"> initialUrl: url))</span><br></pre></td></tr></table></figure><p>经过上面4步,就可以直接简单实现一个请求网络数据后展示的列表,且列表数据可点击跳转到对应的webview页。</p>]]></content>
<summary type="html">
<p>之前简单学习了Flutter的入门程序,那么今天就从实例出发,搞一个简单的demo。</p>
</summary>
<category term="Flutter" scheme="http://ownwell.github.io/categories/Flutter/"/>
<category term="Flutter" scheme="http://ownwell.github.io/tags/Flutter/"/>
</entry>
<entry>
<title>Flutter 从一个demo开始</title>
<link href="http://ownwell.github.io/2019/05/12/Flutter-%E4%BB%8E%E4%B8%80%E4%B8%AAdemo%E5%BC%80%E5%A7%8B/"/>
<id>http://ownwell.github.io/2019/05/12/Flutter-从一个demo开始/</id>
<published>2019-05-12T06:31:02.000Z</published>
<updated>2019-07-26T12:23:34.736Z</updated>
<content type="html"><![CDATA[<p>Flutter已经出了稳定版,这对于很对开发者来说,终于可以长长吐一口气了,我也来踩坑啦。<br><img src="https://raw.githubusercontent.com/ownwell/image-bed/master/img/2019_06_12_16_28_57.jpg" alt></p><h1><span id="什么是flutter">什么是Flutter</span></h1><p>Flutter究竟是个啥,他对于开发者有什么益处,学习它有什么准备课程么?<br>针对这个问题,或许在开始写代码前,你要思考下?</p><h2><span id="混合开发">混合开发</span></h2><p>曾经接触过用<code>webview</code>和<code>JSbridege</code>的混合开发,也曾经在经历过<code>React Native</code>,总体来说,混合开发的体验不好,而RN对于之前的版本适配和复杂UI加载通讯总是让人觉得比原生差太多,Flutter试了下,除了包大了点点,可以完美避免这写问题,不过是骡子是马,我们都要先溜一圈。</p><p>Flutter还支持了Web,感觉Google大哥有点膨胀啊,你这是要统治全端么,不过说实话Dart这种语言最初也是给前端开发使用的。</p><h2><span id="dart">dart</span></h2><p>对于Flutter,我们需要了解dart的语法,不过不要死记硬背,没啥意思,感觉会js或者java足够了你能看懂,慢慢模仿就行了<br>我们这里简单介绍下dart吧,避免不必要的麻烦。</p><h3><span id="包引入">包引入</span></h3><p>dart的一个个库,也是用dart写的,未来有一天你的库也可以被更多人的使用。<br>只需要项引入java一样或者类似react js的引入其他包:</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="string">'package:http/http.dart'</span>;</span><br></pre></td></tr></table></figure><h3><span id="变量">变量</span></h3><p>dart也是给一个强类型语言,定义变量可以不用写明类型。</p><h4><span id="私有变量">私有变量</span></h4><p>对于私有变量,它不像<code>java</code>语言有privte这种access flag,所以要在命令上规范未下划线开始。</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">String</span> _firstName;</span><br></pre></td></tr></table></figure><h4><span id="私有方法">私有方法</span></h4><figure class="highlight dart"><table><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">static</span> _parsePirateNamesFromJSON(<span class="built_in">String</span> jsonString) {</span><br><span class="line"> <span class="built_in">Map</span> pirateNames = JSON.decode(jsonString);</span><br><span class="line"> names = pirateNames[<span class="string">'names'</span>];</span><br><span class="line"> appellations = pirateNames[<span class="string">'appellations'</span>];</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4><span id="类型转换">类型转换</span></h4><figure class="highlight dart"><table><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">void</span> updateBadge(Event e) {</span><br><span class="line"> <span class="built_in">String</span> inputName = (e.target <span class="keyword">as</span> InputElement).value;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4><span id="方法">方法</span></h4><p>有些方法可以直接使用表达式的值即为返回值的情况</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">String</span> toString() => pirateName;</span><br></pre></td></tr></table></figure><p>和之前的写法说再见吧:</p><figure class="highlight dart"><table><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="built_in">String</span> toString() {</span><br><span class="line"> <span class="keyword">return</span> pirateName;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4><span id="级联操作符">级联操作符(..)</span></h4><p>记得之前特别喜欢用链式的结构,现在你需要的来了,多个函数操作,用级联操作符:</p><figure class="highlight dart"><table><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">genButton..disabled = <span class="keyword">false</span></span><br><span class="line"> ..text = <span class="string">'Aye! Gimme a name!'</span>;</span><br></pre></td></tr></table></figure><p>这就相当于:</p><figure class="highlight dart"><table><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">genButton.disabled = <span class="keyword">false</span>;</span><br><span class="line">genButton.text = <span class="string">'Aye! Gimme a name!'</span>;</span><br></pre></td></tr></table></figure><h4><span id="变量可以加到字符串中">变量可以加到字符串中</span></h4><p>这个在shell和python中不是什么新鲜玩意,只需要加个$符号(见到美刀,就可以替换,也是给个钱迷啊)</p><figure class="highlight dart"><table><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="built_in">String</span> _firstName = <span class="string">"cyning"</span></span><br><span class="line"><span class="built_in">print</span> <span class="string">'<span class="subst">$_firstName</span> the <span class="subst">$_appellation</span>'</span>;</span><br></pre></td></tr></table></figure><h4><span id="参数">参数</span></h4><p>和其他解释性语言一样,支持参数可选</p><h4><span id="getters-and-setters">Getters and setters</span></h4><p>getter 和setter 是 Dart 中特殊的函数,看起来像是变量。可以像下面这样来重新定义 Spacecraft 类的 launchYear 属性:</p><figure class="highlight dart"><table><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="class"><span class="keyword">class</span> <span class="title">Spacecraft</span> </span>{</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line"> <span class="built_in">DateTime</span> launchDate;</span><br><span class="line"> <span class="built_in">int</span> <span class="keyword">get</span> launchYear => launchDate?.year;</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4><span id="继承">继承</span></h4><p>Dart 是单继承的。</p><figure class="highlight dart"><table><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="class"><span class="keyword">class</span> <span class="title">Orbiter</span> <span class="keyword">extends</span> <span class="title">Spacecraft</span> </span>{</span><br><span class="line"> <span class="built_in">num</span> altitude;</span><br><span class="line"> Orbiter(<span class="built_in">String</span> name, <span class="built_in">DateTime</span> launchDate, <span class="keyword">this</span>.altitude)</span><br><span class="line"> : <span class="keyword">super</span>(name, launchDate);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4><span id="异步操作">异步操作</span></h4><p>Dart 语言原生支持异步操作,主要是用两个关键词 await 和 async。<br>在函数上加<code>async</code>,不需要像 java 那样去 new Thread,在用到耗时的地方加上await。</p><figure class="highlight java"><table><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="comment">// 发起网络请求</span></span><br><span class="line">loadData() async{</span><br><span class="line"> String requestURL = <span class="string">'http://gank.io/api/today'</span>;</span><br><span class="line"> Client client = Client();</span><br><span class="line"> Response response = await client.get(requestURL);</span><br><span class="line"> </span><br><span class="line"> String jsonString = response.body;</span><br><span class="line"> print(jsonString);</span><br><span class="line"> </span><br><span class="line"> }</span><br></pre></td></tr></table></figure><h1><span id="第一个demo">第一个demo</span></h1><p>至于环境搭建,可以参考<a href="https://flutterchina.club/" target="_blank" rel="noopener">Flutter中文网</a>来一步步搭建,我使用的编辑器是<code>Vs code</code></p><h2><span id="新建">新建</span></h2><p><img src="https://raw.githubusercontent.com/ownwell/image-bed/master/img/2019_06_12_15_34_21.jpg" alt><br>需要耐心等待会儿,就会有个demo.</p><p>为了能验证是否生效,来跑下,还是这图上图,使用<code>starting debug</code>运行,手机或者虚拟机尽量连着开发机。</p><p>注意事项</p><blockquote><p>同步库时,需要将jcenter库,替换为阿里的镜像:</p></blockquote><figure class="highlight plain"><table><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></pre></td><td class="code"><pre><span class="line">buildscript {</span><br><span class="line"> repositories {</span><br><span class="line"> maven { url 'https://maven.aliyun.com/repository/google' }</span><br><span class="line"> maven { url 'https://maven.aliyun.com/repository/jcenter' }</span><br><span class="line"> maven { url 'http://maven.aliyun.com/nexus/content/groups/public' }</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"> dependencies {</span><br><span class="line"> classpath 'com.android.tools.build:gradle:3.2.1'</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">allprojects {</span><br><span class="line"> repositories {</span><br><span class="line"> maven { url 'https://maven.aliyun.com/repository/google' }</span><br><span class="line"> maven { url 'https://maven.aliyun.com/repository/jcenter' }</span><br><span class="line"> maven { url 'http://maven.aliyun.com/nexus/content/groups/public' }</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2><span id="项目结构">项目结构</span></h2><p>目前demo的目录结构如下,其中<code>android</code>文件下是一个正常的android程序。</p><p>| |<strong><strong>android<br>| |</strong></strong>ios<br>| |<strong><strong>lib<br>| | |</strong></strong>main.dart<br>| |___pubspec.yaml </p><p>lib下有个main.dart,这个就是我们需要操作的dart文佳。<br>pubspec.yaml是配置的相关。</p><h2><span id="hello-world跑起来">hello world跑起来</span></h2><p>可以修改lib/main.dart如下代码:</p><figure class="highlight dart"><table><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="string">'package:flutter/material.dart'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">void</span> main() => runApp(<span class="keyword">new</span> MyApp());</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyApp</span> <span class="keyword">extends</span> <span class="title">StatelessWidget</span> </span>{</span><br><span class="line"> <span class="meta">@override</span></span><br><span class="line"> Widget build(BuildContext context) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> MaterialApp(</span><br><span class="line"> title: <span class="string">'Welcome to Flutter'</span>,</span><br><span class="line"> <span class="comment">// 主题</span></span><br><span class="line"> theme: ThemeData(</span><br><span class="line"> primarySwatch: Colors.blue,</span><br><span class="line"> ),</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 首页的页面</span></span><br><span class="line"> home: <span class="keyword">new</span> Scaffold(</span><br><span class="line"> appBar: <span class="keyword">new</span> AppBar(</span><br><span class="line"> title: <span class="keyword">new</span> Text(<span class="string">'Welcome to Flutter'</span>),</span><br><span class="line"> ),</span><br><span class="line"> </span><br><span class="line"> body: <span class="keyword">new</span> Center(</span><br><span class="line"> child: <span class="keyword">new</span> Text(<span class="string">'Hello World'</span>),</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><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>p跑起来截图:<br><img src="https://raw.githubusercontent.com/ownwell/image-bed/master/img/2019_06_12_16_56_16.jpg" alt></p><ol><li><p>main函数使用了(=>)符号, 这是Dart中单行函数或方法的简写</p></li><li><p>Scaffold 是 Material library 中提供的一个widget, 它提供了默认的导航栏、标题和包含主屏幕widget树的body属性。widget树可以很复杂。</p></li><li><p>MyApp继承自StatelessWidget, Statelesswidgets是不可变的, 这意味着它们的属性不能改变 - 所有的值都是最终的.</p></li></ol><h2><span id="添加一个-有状态的部件stateful-widget">添加一个 有状态的部件(Stateful widget)</span></h2><p>我们需要替换MaterialApp下22行的<code>child: new Text('Hello World')</code>属性为一个<code>StatefulWidget</code>。<br>而Stateful widgets 持有的状态可能在widget生命周期中发生变化. 实现一个 stateful widget 至少需要两个类:</p><p>一个<code>StatefulWidget</code>类。<br>一个 <code>State</code>类。<br>StatefulWidget<code>类本身是不变的,但是</code>State类`在widget生命周期中始终存在.</p><figure class="highlight dart"><table><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="class"><span class="keyword">class</span> <span class="title">MyHomePage</span> <span class="keyword">extends</span> <span class="title">StatefulWidget</span> </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"> _MyHomePageState createState() => _MyHomePageState();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">_MyHomePageState</span> <span class="keyword">extends</span> <span class="title">State</span><<span class="title">MyHomePage</span>> </span>{</span><br><span class="line"> <span class="meta">@override</span></span><br><span class="line"> Widget build(BuildContext context) {</span><br><span class="line"> <span class="keyword">return</span> Text(<span class="string">"hello MyHomePage"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>当然了也可以使用过动态设置_MyHomePageState为其他文本,如一个单词,这就需要引入<code>english_words</code>库。<br>在项目的根路径下找到l<code>pubspec.yaml</code>,添加:<br><img src="https://raw.githubusercontent.com/ownwell/image-bed/master/img/2019_06_12_17_28_23.jpg" alt><br>同时在dart文件的import处添加:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">import 'package:english_words/english_words.dart';</span><br></pre></td></tr></table></figure><p>只需要在MyHomePage的build方法里修改代码如下:</p><figure class="highlight dart"><table><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">final</span> wordPair = <span class="keyword">new</span> WordPair.random();</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> Text(wordPair.asPascalCase);</span><br></pre></td></tr></table></figure><p>这样每次启动都会触发页面刷新,都会随机出现一个单词。</p><h2><span id="交互">交互</span></h2><p>我们还需要未这个添加一个交互,例如当我们点击时,就可以换一个单词,将Text替换可以点击的<code>RaisedButton</code>:</p><figure class="highlight plain"><table><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">final wordPair = new WordPair.random();</span><br><span class="line"> return RaisedButton(</span><br><span class="line"> child: Text(wordPair.asPascalCase),</span><br><span class="line"> onPressed: clickbtn,</span><br><span class="line"> );</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/ownwell/image-bed/master/img/111.gif" alt></p><p>好了这代码基本是否让我们对<code>StatefulWidget</code>的理解呢?</p><p>整体代码:<a href="https://github.com/ownwell/flutter_study/tree/first-demo" target="_blank" rel="noopener"></a></p>]]></content>
<summary type="html">
<p>Flutter已经出了稳定版,这对于很对开发者来说,终于可以长长吐一口气了,我也来踩坑啦。<br><img src="https://raw.githubusercontent.com/ownwell/image-bed/master/img/2019_06_12_16_2
</summary>
<category term="Flutter" scheme="http://ownwell.github.io/categories/Flutter/"/>
<category term="Flutter" scheme="http://ownwell.github.io/tags/Flutter/"/>
</entry>
<entry>
<title>Aspect和AOP打点调研</title>
<link href="http://ownwell.github.io/2018/12/23/Aspect%E5%92%8Caop%E6%89%93%E7%82%B9%E8%B0%83%E7%A0%94/"/>
<id>http://ownwell.github.io/2018/12/23/Aspect和aop打点调研/</id>
<published>2018-12-23T10:57:56.000Z</published>
<updated>2019-07-06T14:53:51.797Z</updated>
<content type="html"><![CDATA[<p>面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)是都是计算机编程架构。OOP在开发中,更多的是用抽象思维将一切事物都抽象为对象,学生类,订单类甚至是图形类,所谓的面向切面编程其实是对业务逻辑又进行了进一步的抽取,将多种业务逻辑中的公用部分抽取出来做成一种服务(比如日志记录,性能统计,安全验证等),从而实现代码复用。<br>而这些抽象的基础就是这些属性或者操作是固定的,如学生的事务中的登录,查询成绩等等,可是这些真的是固定的么?</p><p> 我们将一切对象都可以定义成类,相同的可以聚合成一个父类,不同的逻辑可以交给多态或者继承。<br> 而有些逻辑这很难用这OOP去解决,如打点问题,假设我们项目分为多个功能模块或者业务模块,而这些这些模块依赖基础模块。<br> 假如哪一天PM要求我们给各个业务模块打点(如点击事件打点),那么我们的思路肯定在每个模块下一个个打点,针对不同的模块,不同的view进行打点,主要是这些打点逻辑还是重复,和业务实际上关联性很弱,也就是说没它程序也没跑起来,只是在相同或相似时机(如打点模块的点击)去触发打点逻辑。如图所示:<br> <img src="https://raw.githubusercontent.com/ownwell/image-bed/master/img/2019-06-24-18-42-46.png" alt></p><p>这样机械性的工作,是不是可以考虑AOP呢?</p><p>那么什么是AOP呢?</p><h1><span id="aop">AOP</span></h1><p> AOP是Aspect Oriented Programming的缩写,中译文为面向切向编程,他是对OOP的补充。<br> 如上面说的打点的例子,就可以通过AOP很好实现,每个功能模块或者业务模块不需要改动,只需要将打点的逻辑上做一个切面,通过织入需要的代码即可,若你不理解可以看下面的例子。</p><h1><span id="对函数做切面">对函数做切面</span></h1><p> 我们写个简单的demo,有个类,里面只有onClick和onCreate方法。<br> <figure class="highlight java"><table><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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MainActivity</span> <span class="keyword">extends</span> <span class="title">AppCompatActivity</span> <span class="keyword">implements</span> <span class="title">View</span>.<span class="title">OnClickListener</span> </span>{</span><br><span class="line"></span><br><span class="line"> Button textView = <span class="keyword">null</span>;</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>{</span><br><span class="line"> <span class="keyword">super</span>.onCreate(savedInstanceState);</span><br><span class="line"> setContentView(R.layout.activity_main);</span><br><span class="line"></span><br><span class="line"> textView = findViewById(R.id.tvjj);</span><br><span class="line"></span><br><span class="line"> textView.setOnClickListener(<span class="keyword">this</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> <span class="keyword">void</span> <span class="title">onClick</span><span class="params">(View v)</span> </span>{</span><br><span class="line"> Log.d(<span class="string">"Aop"</span>, <span class="string">"onClick"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>我们给这个类加切面:</p><figure class="highlight java"><table><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="meta">@Aspect</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AspectTest</span> </span>{</span><br><span class="line"> <span class="keyword">final</span> String TAG = <span class="string">"AspectTest"</span>;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Before</span>(<span class="string">"execution(* *..MainActivity+.on**(..))"</span>)</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">method</span><span class="params">(JoinPoint joinPoint)</span> <span class="keyword">throws</span> Throwable </span>{</span><br><span class="line"> MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();</span><br><span class="line"> String className = joinPoint.getThis().getClass().getSimpleName();</span><br><span class="line"></span><br><span class="line"> Log.e(TAG, <span class="string">"class:"</span> + className);</span><br><span class="line"> Log.e(TAG, <span class="string">"method:"</span> + methodSignature.getName());</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>运行后的日志:</p><figure class="highlight java"><table><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="number">2019</span>-<span class="number">06</span>-<span class="number">23</span> <span class="number">18</span>:<span class="number">56</span>:<span class="number">21.661</span> <span class="number">3091</span>-<span class="number">3091</span>/cc.cyning.aspectjx E/AspectTest: -----</span><br><span class="line">2019-06-23 18:56:21.661 3091-3091/cc.cyning.aspectjx E/AspectTest: class:MainActivity</span><br><span class="line"><span class="number">2019</span>-<span class="number">06</span>-<span class="number">23</span> <span class="number">18</span>:<span class="number">56</span>:<span class="number">21.661</span> <span class="number">3091</span>-<span class="number">3091</span>/cc.cyning.aspectjx E/AspectTest: method:onCreate</span><br><span class="line"><span class="number">2019</span>-<span class="number">06</span>-<span class="number">23</span> <span class="number">18</span>:<span class="number">56</span>:<span class="number">24.819</span> <span class="number">3091</span>-<span class="number">3091</span>/cc.cyning.aspectjx E/AspectTest: -----</span><br><span class="line">2019-06-23 18:56:24.819 3091-3091/cc.cyning.aspectjx E/AspectTest: class:MainActivity</span><br><span class="line"><span class="number">2019</span>-<span class="number">06</span>-<span class="number">23</span> <span class="number">18</span>:<span class="number">56</span>:<span class="number">24.819</span> <span class="number">3091</span>-<span class="number">3091</span>/cc.cyning.aspectjx E/AspectTest: method:onClick</span><br><span class="line"><span class="number">2019</span>-<span class="number">06</span>-<span class="number">23</span> <span class="number">18</span>:<span class="number">56</span>:<span class="number">24.819</span> <span class="number">3091</span>-<span class="number">3091</span>/cc.cyning.aspectjx E/AspectTest: args <span class="number">1</span>: AppCompatButton</span><br></pre></td></tr></table></figure><p> 在<code>MainActivity</code>里我们什么也没做,只是在<code>AspectTest</code>里写了加了一个切面,在执行<code>MainActivity</code>里面的<code>on**</code>(里面只有<code>onCreate</code>,<code>onClick</code>)前加入了打印当前类和方法的,就可以在<code>onCreate</code>和<code>onClick</code>执行时打印出相关信息,是不是很神奇。</p><p>那么怎么怎么做切面,切面逻辑怎么写?下面就是我们的主角AspectJ</p><h1><span id="aspectj">AspectJ</span></h1><p>AspectJ是一个代码织入技术(code injection),提供了一套全新的语法实现,完全兼容Java(其实跟Java之间的区别,只是多了一些关键词而已)。同时,还提供了纯Java语言的实现,通过注解的方式,完成代码编织的功能。因此我们在使用AspectJ的时候有以下两种方式:</p><ol><li>使用AspectJ的语言进行开发</li><li>通过AspectJ提供的注解在Java语言上开发</li></ol><p> 在了解AspectJ的具体使用之前,先了解一下其中的一些基本的术语概念</p><h2><span id="常用术语">常用术语</span></h2><p>下面的一些常用术语是我们开发中必须知道的,我们自己做切面时这些术语</p><h3><span id="join-points">Join Points</span></h3><p>程序中可能作为代码注入目标的特定的点。join point 可以包含其它 join point。例如,一个方法调用可能在它返回之前引起其它方法调用。那么,Pointcut 就是一种语言构造,这种构造根据已定义的标准挑选一组 join point。</p><p>其常用的如下:</p><table><thead><tr><th>JoinPoints</th><th>说明</th><th>示例</th></tr></thead><tbody><tr><td>method call</td><td>函数调用</td><td>比如调用Log.e(),这是一处Joint point</td></tr><tr><td>method execution</td><td>函数执行</td><td>如实例中的onCreate的执行时,其内部代码</td></tr><tr><td>constructor call</td><td>构造函数调</td><td>与方法的调用类型</td></tr><tr><td>constructor</td><td>executor</td><td>构造函数执行<br>与方法的执行执行</td></tr><tr><td>field get</td><td>获取某个变量</td><td></td></tr><tr><td>field set</td><td>设置某个变量</td><td></td></tr><tr><td>static initialization</td><td>类初始化</td><td></td></tr><tr><td>initialization</td><td>object在构造函数中做的一些工作</td><td></td></tr><tr><td>handler</td><td>异常处理</td><td>对应try-catch()中,对应的catch块内的执行</td></tr></tbody></table><h3><span id="pointcuts">Pointcuts</span></h3><p>代码注入的位置,这个注意是个点或者多个调用点。</p><h3><span id="advice">Advice</span></h3><p>其实就是注入到class文件中的代码片,其他告知注入的位置,如是在方法前(Before),还是在方法后(After),或者是在体制替换整个方法(Around)</p><h3><span id="切点语法表达式">切点语法表达式</span></h3><p>可以拿刚才的例子来学习;</p><figure class="highlight java"><table><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">@Before</span>(<span class="string">"execution(* *..MainActivity+.on**(..))"</span>)</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">method</span><span class="params">(JoinPoint joinPoint)</span> <span class="keyword">throws</span> Throwable </span>{</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>可以知道是</p><ol><li>切点语法表达式 = Advice(Before属于Advice)+ Pointcuts<br>如<figure class="highlight java"><table><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="meta">@Around</span>(<span class="string">"onClickPointcuts() || onClickInXmlPointcuts() || onClickInButterKnifePointcuts()"</span>)</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">throttleClick</span><span class="params">(ProceedingJoinPoint joinPoint)</span> <span class="keyword">throws</span> Throwable </span>{</span><br><span class="line"></span><br><span class="line"> }</span><br></pre></td></tr></table></figure></li></ol><p><code>onClickPointcuts</code>就是一个Pointcut</p><ol start="2"><li>Pointcuts = 可能是多个Pointcut<br>Pointcut = Join Point的关键字(call,execution) + 函数,变量或者其他切入点</li></ol><figure class="highlight java"><table><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="meta">@Pointcut</span>(<span class="string">"execution(* android.view.View.OnClickListener.onClick(..))"</span>)</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onClickPointcuts</span><span class="params">()</span> </span>{</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">@Pointcut</span>( <span class="string">"execution(* android.support.v7.app.AppCompatViewInflater.DeclaredOnClickListener.onClick(..))"</span>)</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onClickInXmlPointcuts</span><span class="params">()</span> </span>{</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>那么这些<code>*</code>究竟代表啥呢?<br>若是之间接触过正则表达式,可能就很容易了。<br>|表达式 |含义|<br>|—|—|<br>|java.lang.String |匹配String类型|<br>|java.<em>.String | 匹配java包下的任何“一级子包”下的String类型,如匹配java.lang.String,但不匹配java.lang.ss.String|<br>|java..</em> | 匹配java包及任何子包下的任何类型,如匹配java.lang.String、java.lang.annotation.Annotation<br>|java.lang.*ing| 匹配任何java.lang包下的以ing结尾的类型|<br>|java.lang.Number+ |匹配java.lang包下的任何Number的自类型,如匹配java.lang.Integer,也匹配java.math.BigInteger|</p><h2><span id="常用的aspectj-切点语法表达式">常用的AspectJ 切点语法表达式</span></h2><h3><span id="call和execution">call和execution</span></h3><p><code>call</code>表示的外接的调用,而<code>execution</code>则是函数内部。</p><p><code>MainActivity.class</code><br><img src="https://raw.githubusercontent.com/ownwell/image-bed/master/img/2019-06-25-22-34-56.png" alt></p><p>测试execution</p><figure class="highlight java"><table><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="meta">@Before</span>(<span class="string">"execution(* cc.cyning.aspectjx.MainActivity.onTest(..))"</span>)</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">method</span><span class="params">(JoinPoint joinPoint)</span> <span class="keyword">throws</span> Throwable </span>{</span><br><span class="line"> <span class="comment">// 代码的行号</span></span><br><span class="line"> Log.e(TAG , joinPoint.getSourceLocation().getLine() + <span class="string">""</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>返回的结果:</p><figure class="highlight java"><table><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="number">8828</span>-<span class="number">8828</span>/cc.cyning.aspectjx D/Aop: onClick before</span><br><span class="line"><span class="number">8828</span>-<span class="number">8828</span>/cc.cyning.aspectjx E/AspectTest: <span class="number">42</span></span><br><span class="line"><span class="number">8828</span>-<span class="number">8828</span>/cc.cyning.aspectjx D/Aop: onTest</span><br><span class="line"><span class="number">8828</span>-<span class="number">8828</span>/cc.cyning.aspectjx D/Aop: onClick after</span><br></pre></td></tr></table></figure><p>测试call</p><figure class="highlight java"><table><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="meta">@Before</span>(<span class="string">"call(* cc.cyning.aspectjx.MainActivity.onTest(..))"</span>)</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">method</span><span class="params">(JoinPoint joinPoint)</span> <span class="keyword">throws</span> Throwable </span>{</span><br><span class="line"> String key = joinPoint.getSignature().toString();</span><br><span class="line"> <span class="comment">// 代码的行号</span></span><br><span class="line"> Log.e(TAG , joinPoint.getSourceLocation().getLine() + <span class="string">""</span>);</span><br><span class="line"></span><br><span class="line"> Log.e(TAG, <span class="string">"method:"</span> + key);</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>运行结果:</p><figure class="highlight java"><table><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="number">9003</span>-<span class="number">9003</span>/cc.cyning.aspectjx D/Aop: onClick before</span><br><span class="line"><span class="number">09003</span>-<span class="number">9003</span>/cc.cyning.aspectjx E/AspectTest: <span class="number">35</span></span><br><span class="line"><span class="number">9003</span>-<span class="number">9003</span>/cc.cyning.aspectjx D/Aop: onTest</span><br><span class="line"><span class="number">9003</span>-<span class="number">9003</span>/cc.cyning.aspectjx D/Aop: onClick after</span><br></pre></td></tr></table></figure><p>可以知道<code>call</code>上是运行的35行,而<code>execution</code>是运行在<code>onTest</code>的内部的.</p><p>对于更多的<code>ASpectJ</code>的内容,可以查看我文章末尾的参考文章。</p><p>还是回归到打点的问题。</p><h1><span id="view的点击打点">view的点击打点</span></h1><p>具体思路是想可以参考<a href="https://neyoufan.github.io/2017/07/11/android/%E7%BD%91%E6%98%93HubbleData%E4%B9%8BAndroid%E6%97%A0%E5%9F%8B%E7%82%B9%E5%AE%9E%E8%B7%B5/" target="_blank" rel="noopener">网易HubbleData之Android无埋点实践</a></p><p>那么我们可以先使用<code>ASpectJ</code>来处理点击view的打点。</p><p>对于view我们最简单的是在View.OnClickListener.onClick切面上加入打点逻辑,当时现在很多点击时间还真的不再这样实现。</p><p>问题:</p><ol><li><p>在xml的中设置点击事件</p><figure class="highlight java"><table><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"><Button</span><br><span class="line"> android:id=<span class="string">"@+id/button"</span></span><br><span class="line"> android:onClick=<span class="string">"onClick"</span></span><br><span class="line"> android:layout_width=<span class="string">"wrap_content"</span></span><br><span class="line"> android:layout_height=<span class="string">"wrap_content"</span></span><br><span class="line"> android:text=<span class="string">"Hello World!"</span></span><br><span class="line"> app:layout_constraintBottom_toBottomOf=<span class="string">"parent"</span></span><br><span class="line"> app:layout_constraintLeft_toLeftOf=<span class="string">"parent"</span></span><br><span class="line"> app:layout_constraintRight_toRightOf=<span class="string">"parent"</span></span><br><span class="line"> app:layout_constraintTop_toTopOf=<span class="string">"parent"</span>/></span><br></pre></td></tr></table></figure></li><li><p>ButterKnife注释实现点击事件</p><figure class="highlight java"><table><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="meta">@OnClick</span>(R.id.button)</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">onClickView</span><span class="params">(View view)</span></span></span><br></pre></td></tr></table></figure></li><li><p>lambda的表达式实现点击事件</p></li></ol><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">button.addClickListener(clickEvent -> doSth());</span><br></pre></td></tr></table></figure><h3><span id="xml中点击事件">xml中点击事件</span></h3><p>对于这个问题,最简单的就是看view的代码,不多说,看下源码:</p><figure class="highlight plain"><table><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">final TypedArray a = context.obtainStyledAttributes(attrs, sOnClickAttrs);</span><br><span class="line">final String handlerName = a.getString(0);</span><br><span class="line">if (handlerName != null) {</span><br><span class="line"> view.setOnClickListener(new DeclaredOnClickListener(view, handlerName));</span><br><span class="line">}</span><br><span class="line">a.recycle();</span><br></pre></td></tr></table></figure><p>可以在<code>DeclaredOnClickListener</code>做动作吧。</p><h3><span id="butterknife">ButterKnife</span></h3><p>不多说,可以看代码解决。<br>可以去拦截<code>butterknife.OnClick</code>下面的监听事件</p><h1><span id="参考">参考</span></h1><p><a href="https://juejin.im/post/58ad3944b123db00672cdeeb" target="_blank" rel="noopener">Android AOP学习之:AspectJ实践</a></p><p><a href="https://blog.csdn.net/eclipsexys/article/details/54425414" target="_blank" rel="noopener">看AspectJ在Android中的强势插入</a></p><p><a href="https://juejin.im/post/5a52e8a8f265da3e303c53fb" target="_blank" rel="noopener">AOP:利用Aspectj注入代码,无侵入实现各种功能,比如一个注解请求权限</a></p><p><a href="https://neyoufan.github.io/2017/07/11/android/%E7%BD%91%E6%98%93HubbleData%E4%B9%8BAndroid%E6%97%A0%E5%9F%8B%E7%82%B9%E5%AE%9E%E8%B7%B5/" target="_blank" rel="noopener">网易HubbleData之Android无埋点实践</a></p>]]></content>
<summary type="html">
<p>面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)是都是计算机编程架构。OOP在开发中,更多的是用抽象思维将一切事物都抽象为对象,学生类,订单类甚至是图形类,所谓的面向切面编程其实是对业务逻辑又进行了进一步的抽取,将多种业务逻辑
</summary>
<category term="Android" scheme="http://ownwell.github.io/categories/Android/"/>
<category term="Android" scheme="http://ownwell.github.io/tags/Android/"/>
</entry>
<entry>
<title>Gradle插件开发(3) - 无侵入的函数运行时间统计的实现</title>
<link href="http://ownwell.github.io/2018/09/04/android-gradle-3/"/>
<id>http://ownwell.github.io/2018/09/04/android-gradle-3/</id>
<published>2018-09-04T01:16:13.000Z</published>
<updated>2019-06-11T15:10:06.008Z</updated>
<content type="html"><![CDATA[<p><a href="http://ownwell.github.io/2018/08/20/android-gradle-2/">Gradle插件开发(2) - extensions和Task</a></p><p>经过前边两篇介绍,我们了解了Gradle的基础知识和如何写一个自己的插件,我们今天,开始实战,搞点有趣的东西。</p><h1><span id="准备">准备</span></h1><p>今天前面的介绍,我们是可以实现自己的自定义task,当时android在构建是一个很琐碎的过程,之前的各个环节都是task,为了让开发人员更少也更好写业务代码,后来出了一套<code>Transform API</code>。</p><p><img src="https://raw.githubusercontent.com/ownwell/image-bed/master/2019-06-11-23-03-00.jpg" alt></p><h2><span id="transform">Transform</span></h2><h3><span id="transform-介绍">Transform 介绍</span></h3><p><code>Transform</code>是特意为Android打造的,按照官网的解释如下:</p><p>Starting with 1.5.0-beta1, the Gradle plugin includes a Transform API allowing 3rd party plugins to manipulate compiled class files before they are converted to dex files.<br>(The API existed in 1.4.0-beta2 but it’s been completely revamped in 1.5.0-beta1)</p><p>The goal of this API is to simplify injecting custom class manipulations without having to deal with tasks, and to offer more flexibility on what is manipulated. The internal code processing (jacoco, progard, multi-dex) have all moved to this new mechanism already in 1.5.0-beta1.<br>Note: this applies only to the javac/dx code path. Jack does not use this API at the moment.</p><p>简单解释下就是:</p><ol><li><code>Transforms</code>是重新引入的,主要作用在对class的处理上,也是在生成dex文件前。</li><li><code>Transforms</code>有很强大功能,避免了大家使用task,内部可以处理<code>jacoco, progard, multi-dex</code>等过程。</li></ol><p>在Android studio下我们若是执行<code>./gradlew tasks</code>就会发现很多以<code>那么Transforms</code>开头的task。</p><h3><span id="transform-api">Transform API</span></h3><p>我们可以注册多个transform,这个类似于<code>task</code>流式关系。<br>这写API可以参考<a href="https://juejin.im/entry/59776f2bf265da6c4741db2b" target="_blank" rel="noopener">如何理解 Transform API</a>,可以认为<code>transform</code>是码头搬运的师父,你加入一个人,不影响个整体的码头的云端,前提是你要按照码头的规矩办事。<br>那么<code>transform</code>遵循码头的那些规矩呢?当然就是<code>Transforms</code>的API了,你要严格按照这个API定义每一步。<br>那么Transforms 有哪些API呢:</p><figure class="highlight java"><table><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></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">XXXTransform</span> <span class="keyword">extends</span> <span class="title">Transform</span> </span>{</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">getName</span><span class="params">()</span> </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 class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> Set<QualifiedContent.ContentType> getInputTypes() {</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 class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> Set<? <span class="keyword">super</span> QualifiedContent.Scope> getScopes() {</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 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">isIncremental</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">false</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> <span class="keyword">void</span> <span class="title">transform</span><span class="params">(TransformInvocation transformInvocation)</span> <span class="keyword">throws</span> TransformException, InterruptedException, IOException </span>{</span><br><span class="line"> <span class="keyword">super</span>.transform(transformInvocation);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>来解释下每个函数的作用:</p><ol><li><p><code>getName</code>定义<code>transform</code>的名字,便于程序区分</p></li><li><p><code>getInputTypes</code>拦截输入的流的类型,哪些它会拦截呢?</p><figure class="highlight java"><table><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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TransformManager</span> <span class="keyword">extends</span> <span class="title">FilterableStreamCollection</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> Set<ContentType> CONTENT_CLASS;</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> Set<ContentType> CONTENT_JARS;</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> Set<ContentType> CONTENT_RESOURCES;</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> Set<ContentType> CONTENT_NATIVE_LIBS;</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> Set<ContentType> CONTENT_DEX;</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> Set<ContentType> CONTENT_JACK;</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>getScopes是指transform的作用域,可以管理子模块还是其他的,具体参考<code>TransformManager</code>:</p><figure class="highlight java"><table><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="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> Set<Scope> SCOPE_FULL_PROJECT = Sets.immutableEnumSet(</span><br><span class="line"> Scope.PROJECT,</span><br><span class="line"> Scope.PROJECT_LOCAL_DEPS,</span><br><span class="line"> Scope.SUB_PROJECTS,</span><br><span class="line"> Scope.SUB_PROJECTS_LOCAL_DEPS,</span><br><span class="line"> Scope.EXTERNAL_LIBRARIES);</span><br><span class="line"> </span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> Set<QualifiedContent.ScopeType> SCOPE_FULL_INSTANT_RUN_PROJECT =</span><br><span class="line"> <span class="keyword">new</span> ImmutableSet.Builder<QualifiedContent.ScopeType>()</span><br><span class="line"> .addAll(SCOPE_FULL_PROJECT)</span><br><span class="line"> .add(InternalScope.MAIN_SPLIT)</span><br><span class="line"> .build();</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> Set<Scope> SCOPE_FULL_LIBRARY = Sets.immutableEnumSet(</span><br><span class="line"> Scope.PROJECT,</span><br><span class="line"> Scope.PROJECT_LOCAL_DEPS);</span><br></pre></td></tr></table></figure></li><li><p>isIncremental:用于指明是否是增量构建</p></li><li><p><code>transform(TransformInvocation transformInvocation)</code>是整个类的核心,inputs中是传过来的输入流,其中有两种格式,一种是jar包格式一种是目录格式。outputProvider 获取到输出目录,最后将修改的文件复制到输出目录,这一步必须做不然编译会报错。</p></li></ol><h3><span id="注册transform">注册<code>Transform</code></span></h3><p>注册一个<code>Transform</code>到项目里,需要通过<code>Plugin</code>实现,</p><figure class="highlight java"><table><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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TranPlugin</span> <span class="keyword">implements</span> <span class="title">Plugin</span><<span class="title">Project</span>> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">apply</span><span class="params">(Project project)</span> </span>{</span><br><span class="line"> System.out.println(<span class="string">"------------------开始----------------------"</span>);</span><br><span class="line"> <span class="comment">//AppExtension就是build.gradle中android{...}这一块</span></span><br><span class="line"> def android = project.extensions.getByType(AppExtension)</span><br><span class="line"></span><br><span class="line"> <span class="comment">//注册自己的Transform</span></span><br><span class="line"> def classTransform = <span class="keyword">new</span> ClassTransform(project);</span><br><span class="line"> android.registerTransform(classTransform);</span><br><span class="line"></span><br><span class="line"> System.out.println(<span class="string">"------------------结束了吗----------------------"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3><span id="实现自己的transform">实现自己的<code>Transform</code></span></h3><figure class="highlight java"><table><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></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">ClassTransform</span> <span class="keyword">extends</span> <span class="title">Transform</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> Project mProject;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">ClassTransform</span><span class="params">(Project p)</span> </span>{</span><br><span class="line"> <span class="keyword">this</span>.mProject = p;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/** transform的名称</span></span><br><span class="line"><span class="comment"> * transformClassesWithMyClassTransformForDebug 运行时的名字</span></span><br><span class="line"><span class="comment"> */</span></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">getName</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"MyClassTransform"</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"> <span class="comment">//CLASSES和RESOURCES,CLASSES代表处理的java的class文件,RESOURCES代表要处理java的资源</span></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> Set<QualifiedContent.ContentType> getInputTypes() {</span><br><span class="line"> <span class="keyword">return</span> TransformManager.CONTENT_CLASS;</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"> * 指Transform要操作内容的范围,官方文档Scope有7种类型:</span></span><br><span class="line"><span class="comment"> * EXTERNAL_LIBRARIES 只有外部库</span></span><br><span class="line"><span class="comment"> * PROJECT 只有项目内容</span></span><br><span class="line"><span class="comment"> * PROJECT_LOCAL_DEPS 只有项目的本地依赖(本地jar)</span></span><br><span class="line"><span class="comment"> * PROVIDED_ONLY 只提供本地或远程依赖项</span></span><br><span class="line"><span class="comment"> * SUB_PROJECTS 只有子项目。</span></span><br><span class="line"><span class="comment"> * SUB_PROJECTS_LOCAL_DEPS 只有子项目的本地依赖项(本地jar)。</span></span><br><span class="line"><span class="comment"> * TESTED_CODE 由当前变量(包括依赖项)测试的代码</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> Set<QualifiedContent.Scope> getScopes() {</span><br><span class="line"> <span class="keyword">return</span> TransformManager.SCOPE_FULL_PROJECT;</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"> * 指明当前Transform是否支持增量编译</span></span><br><span class="line"><span class="comment"> */</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">boolean</span> <span class="title">isIncremental</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">false</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"><span class="comment"> * Transform中的核心方法</span></span><br><span class="line"><span class="comment"> * inputs中是传过来的输入流,其中有两种格式,一种是jar包格式一种是目录格式。</span></span><br><span class="line"><span class="comment"> * outputProvider 获取到输出目录,最后将修改的文件复制到输出目录,这一步必须做不然编译会报错</span></span><br><span class="line"><span class="comment"> */</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">void</span> <span class="title">transform</span><span class="params">(Context context,</span></span></span><br><span class="line"><span class="function"><span class="params"> Collection<TransformInput> inputs,</span></span></span><br><span class="line"><span class="function"><span class="params"> Collection<TransformInput> referencedInputs,</span></span></span><br><span class="line"><span class="function"><span class="params"> TransformOutputProvider outputProvider,</span></span></span><br><span class="line"><span class="function"><span class="params"> <span class="keyword">boolean</span> isIncremental)</span> <span class="keyword">throws</span> IOException, TransformException, InterruptedException </span>{</span><br><span class="line"> System.out.println(<span class="string">"----------------进入transform了--------------"</span>)</span><br><span class="line"></span><br><span class="line"> </span><br><span class="line"> System.out.println(<span class="string">"--------------结束transform了----------------"</span>)</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>若这样,什么都不写,是我们的<code>transform</code>会报错,我们可以将遍历<code>input</code>文件,:</p><figure class="highlight java"><table><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">//遍历input</span></span><br><span class="line"> inputs.each {</span><br><span class="line"> TransformInput input -></span><br><span class="line"> <span class="comment">//遍历文件夹</span></span><br><span class="line"> input.directoryInputs.each {</span><br><span class="line"> DirectoryInput directoryInput -></span><br><span class="line"> </span><br><span class="line"></span><br><span class="line"> <span class="comment">// 获取output目录</span></span><br><span class="line"> def dest = outputProvider.getContentLocation(directoryInput.name,</span><br><span class="line"> directoryInput.contentTypes, directoryInput.scopes, Format.DIRECTORY)</span><br><span class="line"> println(directoryInput.file.getPath() + <span class="string">" ---> "</span> + dest.toPath())</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 将input的目录复制到output指定目录</span></span><br><span class="line"> FileUtils.copyDirectory(directoryInput.file, dest)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">////遍历jar文件 对jar不操作,但是要输出到out路径</span></span><br><span class="line"> input.jarInputs.each {</span><br><span class="line"> JarInput jarInput -></span><br><span class="line"> <span class="comment">// 重命名输出文件(同目录copyFile会冲突)</span></span><br><span class="line"> def jarName = jarInput.name</span><br><span class="line"> println(<span class="string">"jar = "</span> + jarInput.file.getAbsolutePath())</span><br><span class="line"> def md5Name = DigestUtils.md5Hex(jarInput.file.getAbsolutePath())</span><br><span class="line"> <span class="keyword">if</span> (jarName.endsWith(<span class="string">".jar"</span>)) {</span><br><span class="line"> jarName = jarName.substring(<span class="number">0</span>, jarName.length() - <span class="number">4</span>)</span><br><span class="line"> }</span><br><span class="line"> def dest = outputProvider.getContentLocation(jarName + md5Name, jarInput.contentTypes, jarInput.scopes, Format.JAR)</span><br><span class="line"> FileUtils.copyFile(jarInput.file, dest)</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>可以看到我们什么都没做,就是copy,不过copy文件是需要重新命名一个文件。<br>这样就可以将码头搬运过程中,从上一个工人人的东西,直接传给了下一个工人,只是有少些的改动。<br>那我们若是想在其中进行少些的改动呢?这就需要用到一个神奇的工具<code>javassist</code>。</p><h2><span id="javassist">Javassist</span></h2><p>Javaassist可以用 Javassist 改变 Java 类的字节码,而无需真正了解关于字节码或者 Java 虚拟机(Java virtual machine JVM)结构的任何内容,这样他就可以直接修改class文件的字节码。</p><h3><span id="javassist-基础">Javassist 基础</span></h3><p><code>Javassist</code> 使您可以检查、编辑以及创建 Java 二进制类。检查方面基本上与通过 Reflection API 直接在 Java 中进行的一样,但是当想要修改类而不只是执行它们时,则另一种访问这些信息的方法就很有用了。这是因为 JVM 设计上并没有提供在类装载到 JVM 中后访问原始类数据的任何方法,这项工作需要在 JVM 之外完成。</p><p>Javassist 使用 javassist.ClassPool 类跟踪和控制所操作的类。这个类的工作方式与 JVM 类装载器非常相似,但是有一个重要的区别是它不是将装载的、要执行的类作为应用程序的一部分链接,类池使所装载的类可以通过 Javassist API 作为数据使用。</p><p>装载到类池中的类由 javassist.CtClass 实例表示。与标准的 Java java.lang.Class 类一样, CtClass 提供了检查类数据(如字段和方法)的方法。不过,这只是 CtClass 的部分内容,它还定义了在类中添加新字段、方法和构造函数、以及改变类、父类和接口的方法。奇怪的是,Javassist 没有提供删除一个类中字段、方法或者构造函数的任何方法。</p><p>字段、方法和构造函数分别由 javassist.CtField、javassist.CtMethod 和 javassist.CtConstructor 的实例表示。这些类定义了修改由它们所表示的对象的所有方法的方法,包括方法或者构造函数中的实际字节码内容。</p><h3><span id="读写">读写</span></h3><figure class="highlight java"><table><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">ClassPool pool = ClassPool.getDefault();</span><br><span class="line">CtClass cc = pool.get(<span class="string">"me.cyning.cc.Rectangle"</span>);</span><br><span class="line">cc.setSuperclass(pool.get(<span class="string">"me.cyning.cc.Point"</span>));</span><br><span class="line">cc.writeFile();</span><br></pre></td></tr></table></figure><p>这样就将<code>test.Rectangle</code>的父类设置为<code>test.Point</code>,其中 <code>pool.get("test.Rectangle")</code>就是用默认的ClassPool来装寨对应的class,并且可以使用装载的<code>CtClass</code>.</p><p>调用 writeFile() 后,这项修改会被写入原始类文件.<br>怎么验证呢?</p><figure class="highlight java"><table><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">Class rc = cc.toClass();</span><br><span class="line">System.out.println(rc.getSuperclass());</span><br></pre></td></tr></table></figure><h3><span id="函数">函数</span></h3><p>我们可以寨函数的前面和后面插入我们的值,如何做呢?</p><figure class="highlight java"><table><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception </span>{</span><br><span class="line"> ClassPool cp = ClassPool.getDefault();</span><br><span class="line"></span><br><span class="line"> CtClass cc = cp.get(<span class="string">"me.cyning.Hello"</span>);</span><br><span class="line"></span><br><span class="line"> CtMethod m = cc.getDeclaredMethod(<span class="string">"say"</span>);</span><br><span class="line"> m.insertBefore(<span class="string">"{ System.out.println(\"before Hello.say():\"); }"</span>);</span><br><span class="line"> m.insertAfter(<span class="string">"{ System.out.println(\"after Hello.say():\"); }"</span>);</span><br><span class="line"></span><br><span class="line"> Class c = cc.toClass();</span><br><span class="line"> Hello h = (Hello)c.newInstance();</span><br><span class="line"> h.say();</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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Hello</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">say</span><span class="params">()</span> </span>{</span><br><span class="line"> System.out.println(<span class="string">"Hello"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>先将<code>me.cyning.Hello</code>load到Javassist的ClassPool中,获取到<code>me.cyning.Hello</code>类中的<code>say</code>方法,可以在方法开始和结尾来加入函数。<br>是不是有点意思。</p><h1><span id="打点统计">打点统计</span></h1><p>于是我们可以在我们类中直接使用Javassist来修改我们的class字节码。<br>接着<code>transform</code>中的<code>transform</code>方法来处理,所有的class到<code>transform</code>方法中都是以class文件存在的,所以拦截文件夹。<br><img src="https://raw.githubusercontent.com/ownwell/image-bed/master/2019-06-11-21-59-43.jpg" alt></p><p>只需要在适当的方法里注入如下代码:</p><figure class="highlight java"><table><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"> ``````````````````````println(<span class="string">"方法名 = "</span> + mMethod.getName())</span><br><span class="line"> println(<span class="string">"返回类型 = "</span> + mMethod.getReturnType().getName())</span><br><span class="line"><span class="comment">// println("参数类型 = " + mMethod.getParameterTypes())</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"> mMethod.addLocalVariable(<span class="string">"startMs"</span>, CtClass.longType);</span><br><span class="line"></span><br><span class="line"> mMethod.insertBefore(<span class="string">"startMs = System.currentTimeMillis();"</span>);</span><br><span class="line"> String body = <span class="string">"{"</span> +</span><br><span class="line"> <span class="string">"final long endMs = System.currentTimeMillis();"</span> +</span><br><span class="line"></span><br><span class="line"> <span class="string">"System.out.println(\"Executed in ms: ["</span> + className + <span class="string">","</span> + mMethod.getName() +</span><br><span class="line"> <span class="string">"] ---> \" + (endMs-startMs));}"</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> println(body)</span><br><span class="line"> mMethod.insertAfter(body)</span><br></pre></td></tr></table></figure><p>找到对应的class的看下,生效:<br><img src="https://raw.githubusercontent.com/ownwell/image-bed/master/img/2019-06-11-22-05-41.jpg" alt></p><p>最后的代码:<br><a href="https://github.com/ownwell/Gradle-plugin" target="_blank" rel="noopener">https://github.com/ownwell/Gradle-plugin</a></p><h1><span id="参考文章">参考文章</span></h1><p><a href="http://tools.android.com/tech-docs/new-build-system/transform-api" target="_blank" rel="noopener">Transform API</a><br><a href="https://www.jianshu.com/p/c9ce643e2f22" target="_blank" rel="noopener">Android Plugin Transform 初探</a><br><a href="http://blog.lanxijun.com/?p=98" target="_blank" rel="noopener">Gradle通过Transform API实现代码注入</a><br><a href="http://wiki.jikexueyuan.com/project/deep-android-gradle/" target="_blank" rel="noopener">深入理解 Android 之 Gradle</a><br><a href="https://github.com/jboss-javassist/javassist/wiki/Tutorial-1" target="_blank" rel="noopener">Tutorial 1</a><br><a href="https://www.diycode.cc/topics/786" target="_blank" rel="noopener">Android ASM 插桩初步实现</a><br><a href="https://yq.aliyun.com/articles/70337" target="_blank" rel="noopener">Android热修复技术——QQ空间补丁方案解析(3)</a><br><br><a href="http://tech.lede.com/2017/02/11/rd/android/android_aop/" target="_blank" rel="noopener">网易乐得-Android AOP之字节码插桩</a></p>]]></content>
<summary type="html">
<p><a href="http://ownwell.github.io/2018/08/20/android-gradle-2/">Gradle插件开发(2) - extensions和Task</a></p>
<p>经过前边两篇介绍,我们了解了Gradle的基础知识和如何写一
</summary>
<category term="Android" scheme="http://ownwell.github.io/categories/Android/"/>
<category term="Gradle" scheme="http://ownwell.github.io/tags/Gradle/"/>
</entry>
<entry>
<title>Gradle插件开发(2) - extensions和Task</title>
<link href="http://ownwell.github.io/2018/08/20/android-gradle-2/"/>
<id>http://ownwell.github.io/2018/08/20/android-gradle-2/</id>
<published>2018-08-20T15:08:41.000Z</published>
<updated>2018-09-23T13:41:58.238Z</updated>
<content type="html"><![CDATA[<h1><span id="extensions">extensions</span></h1><p>Gradle是支持配置,外部的配置文件来设置插件的一些输入条件,在插件中是通过<code>extensions</code>实现的。</p><h2><span id="配置">配置</span></h2><p>如我们在项目的<code>build.gradle</code>下配置如下:</p><figure class="highlight java"><table><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">cyMessage {</span><br><span class="line"> message = <span class="string">'hello '</span></span><br><span class="line"> greeter = <span class="string">'cyning'</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这代表一条这条消息,<code>message</code>代表消息内容,<code>greeter</code>代表消息源。</p><p>那么插件如何获取这条消息呢?<br>我们需要在<code>Plugin</code>来注册一个实体类来获取。</p><figure class="highlight java"><table><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="function"><span class="keyword">void</span> <span class="title">apply</span><span class="params">(Project project)</span> </span>{</span><br><span class="line"> def extension = project.extensions.create(<span class="string">"cyMessage"</span>, Message)</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><h2><span id="实体">实体</span></h2><p>其中Message是个实体类:</p><br><div style="background:#6666;padding: 5px;">代码片段 1</div><figure class="highlight java"><table><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">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Message</span> </span>{</span><br><span class="line"> String message = <span class="string">"empty"</span></span><br><span class="line"> String greeter = <span class="string">"none"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><br><h2><span id="使用">使用</span></h2><p>我们可以自己写一个<code>task</code>(下一节就是会介绍task)来验证<code>extensions</code>获取是否成功。</p><div style="background:#6666;padding: 5px;">代码片段 2</div><figure class="highlight java"><table><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">def extension = project.extensions.create(<span class="string">"cyMessage"</span>, Message)</span><br><span class="line"></span><br><span class="line">project.task(<span class="string">'sendMessage'</span>) {</span><br><span class="line"> doLast {</span><br><span class="line"> println <span class="string">"${extension.message} from ${extension.greeter}"</span></span><br><span class="line"> println project.cyMessage.message + <span class="string">"from ${extension.greeter}"</span></span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>可以注意到<br><br>1.<code>extensions.create("cyMessage", Message)</code>返回的是<code>Message</code>的实例</p><p>2.通过<code>extensions.createproject注册了一个</code>cyMessage`的属性。</p><p>所以获取代码片段中的<code>message</code>也有两种方式”<br><code>${extension.message}</code>和<code>project.cyMessage.message</code>。</p><h1><span id="task">Task</span></h1><p>Task是我们插件的任务单位,也就是说插件实际上就是一个个task,还记得我们常用的clean命令,实际上clean也是task,在android项目中,我们可以用<code>gradlew tasks</code>来查看这个项目中的task。</p><p><img src="http://pc0oz0yc3.bkt.clouddn.com/MacHi%202018-08-13%2023-34-35_2018813233832.png" alt></p><blockquote><p>每个task实际上是可以分组的,如上图的<code>Android tasks</code> <code>Build tasks</code>,每个任务后面的小字实际上是task的描述,</p></blockquote><h2><span id="task的创建">task的创建</span></h2><p>task目前可以有多种方法创建和操作。</p><h3><span id="gradle脚本">gradle脚本</span></h3><figure class="highlight gradle"><table><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">task</span> hello1 << {</span><br><span class="line"> <span class="keyword">println</span> <span class="string">'hello1'</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">task</span> hello2 {</span><br><span class="line"> <span class="keyword">doLast</span> {</span><br><span class="line"> <span class="keyword">println</span> <span class="string">'hello2'</span>}</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">task</span> hello3 {</span><br><span class="line"> <span class="keyword">println</span> <span class="string">"task ----"</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">hello3.<span class="keyword">leftShift</span> {</span><br><span class="line"> <span class="keyword">print</span>(<span class="string">"hello world 3"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ol><li><code>doLast</code>相当于是在任务执行<code>Task</code>结束前要执行的,是这个<code>Task</code>的最后一个步骤。</li><li><code><<</code>和<code>leftShift</code>是同一个意思也表示在相当于是在任务执行Task结束前要执行的,</li></ol><h3><span id="groovy脚本创建">groovy脚本创建</span></h3><p>用<code>gradle</code>脚本能容易写出脚本,但是对于一些复杂的<code>Task</code>还是需要我们用<code>groovy</code>来实现,可以控制控制他的灵活度。<br>我们可以来写我们的第一个<code>Task</code>.</p><figure class="highlight groovy"><table><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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">FirstTask</span> <span class="keyword">extends</span> <span class="title">DefaultTask</span> {</span></span><br><span class="line"></span><br><span class="line"> String message = <span class="string">'This is default message'</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// @TaskAction 表示该Task要执行的动作,即在调用该Task时,hello()方法将被执行</span></span><br><span class="line"> <span class="meta">@TaskAction</span></span><br><span class="line"> <span class="keyword">def</span> hello(){</span><br><span class="line"> println <span class="string">"Hello world. $message"</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><code>@TaskAction</code>有注释,我就不再重复了,<code>$message</code>是我们内部定义的一个变量,默认是<code>message</code></p><p>我们用这个<code>task</code>时只需要,写入我们的type为<code>FirstTask</code>.</p><figure class="highlight groovy"><table><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">// hello使用了默认的message值</span></span><br><span class="line">task task1(<span class="string">type:</span>FirstTask)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 重新设置了message的值</span></span><br><span class="line">task task2(<span class="string">type:</span>FirstTask){</span><br><span class="line"> message =<span class="string">"I am an android developer"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2><span id="task-属性">task 属性</span></h2><p><img src="http://pc0oz0yc3.bkt.clouddn.com/1537709701.png?imageMogr2/thumbnail/!70p" alt></p><p>dependsOn可以关联依赖关系, group更像是我们的java代码的报名,是task的一种表示,多个task可以熟悉同一个group, description是我们在编写task的介绍,你可以认为是<code>task的一种注释</code>.</p><figure class="highlight java"><table><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">// 定义一个名字为rygTask的task,属于renyugang分组,并且依赖myTask1和myTask2两个task。</span></span><br><span class="line">project.task('hello', group: "cc.cyning", description: "我自己的Task", dependsOn: ["task1", "task2"] ) </br></span><br><span class="line">.doLast {</span><br><span class="line"> println <span class="string">"execute rygTask"</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">`</span><br></pre></td></tr></table></figure><p>这样<code>hello</code>Task就在<code>task1</code>和<code>task2</code>执行完后,继续执行。</p><h2><span id="使用">使用</span></h2><p>可以直接在终端下输入</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./gradlew :app:task2</span><br></pre></td></tr></table></figure><p>app就是引入我们的task的项目,当然你可以可以用<code>android studio</code>的GUI界面.</p><h1><span id="task和extensions自定义插件"><code>task</code>和<code>extensions</code>自定义插件</span></h1><p>使用<code>extensions</code>传参,使用 <code>task</code>来实现功能,我们可以来尝试下,写自己的插件了,有了<code>task</code>和<code>extensions</code>这个两个概念以及自己的学习能力,写个简单的插件应该问题不大吧。</p><p>我也改造了<a href="http://owenwell.github.io/2018/07/30/android-gradle_1/" target="_blank" rel="noopener">Gradle插件开发(1) - Hello world</a>的插件,下载地址:<a href="https://github.com/ownwell/Gradle-plugin" target="_blank" rel="noopener">ownwell/Gradle-plugin</a></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git clone -b v1.0 https:<span class="comment">//github.com/ownwell/Gradle-plugin.git</span></span><br></pre></td></tr></table></figure><h1><span id="参考">参考</span></h1><ol><li><p><a href="http://www.cnblogs.com/davenkin/p/gradle-learning-2.html" target="_blank" rel="noopener">创建Task的多种方法</a></p></li><li><p><a href="https://docs.gradle.org/current/userguide/custom_plugins.html" target="_blank" rel="noopener">Writing Custom Plugins</a></p></li></ol>]]></content>
<summary type="html">
<h1><span id="extensions">extensions</span></h1><p>Gradle是支持配置,外部的配置文件来设置插件的一些输入条件,在插件中是通过<code>extensions</code>实现的。</p>
<h2><span id="配置">
</summary>
<category term="Android" scheme="http://ownwell.github.io/categories/Android/"/>
<category term="Gradle" scheme="http://ownwell.github.io/tags/Gradle/"/>
</entry>
<entry>
<title>conda切换Python2和Python3环境</title>
<link href="http://ownwell.github.io/2018/08/17/conda-for-python2or3/"/>
<id>http://ownwell.github.io/2018/08/17/conda-for-python2or3/</id>
<published>2018-08-16T16:21:57.000Z</published>
<updated>2018-08-16T16:26:03.778Z</updated>
<content type="html"><![CDATA[<p>由于mac使用到期,公司给还了新的mac,安装时安装了<code>Anaconda(for 3.6+)</code>, Python 2.7慢慢没人维护了,是时候开始切换到3.6+上了。但是很尴尬的是,有些不再维护的库,却依然只支持<code>python2.7</code>,搞得很湿头痛,后来在网上查了资料,发现用<code>conda</code>是可以做到的。</p><h2><span id="conda">conda</span></h2><p><code>conda</code>是<code>Anaconda</code>的工具箱,它是 pip 和 vitualenv 的组合,也就是说他可以像pip来管理包,也可以像vitualenv来切换环境</p><h2><span id="切换python2x和python3x">切换Python2.x和Python3.x</span></h2><p>那么怎么切换Python2.x和Python3.x呢。</p><figure class="highlight python"><table><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="comment"># 基于 python3.6 创建一个名为test_py3 的环境</span></span><br><span class="line">conda create --name test_py3 python=<span class="number">3.6</span> </span><br><span class="line"></span><br><span class="line"><span class="comment"># 基于 python2.7 创建一个名为test_py2 的环境</span></span><br><span class="line">conda create --name test_py2 python=<span class="number">2.7</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 激活 test 环境</span></span><br><span class="line">activate test_py2 <span class="comment"># windows</span></span><br><span class="line">source activate test_py2 <span class="comment"># linux/mac</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 切换到python3</span></span><br><span class="line">source test_py3</span><br></pre></td></tr></table></figure><p>在mac下只要能<code>source test_py2</code>或者<code>source test_py3</code>做到只有切换</p><h2><span id="conda安装包">conda安装包</span></h2><figure class="highlight python"><table><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><br><span class="line"><span class="comment"># 安装 matplotlib </span></span><br><span class="line">conda install matplotlib</span><br><span class="line"><span class="comment"># 查看已安装的包</span></span><br><span class="line">conda list </span><br><span class="line"><span class="comment"># 包更新</span></span><br><span class="line">conda update matplotlib</span><br><span class="line"><span class="comment"># 删除包</span></span><br><span class="line">conda remove matplotlib</span><br><span class="line"></span><br><span class="line"><span class="comment"># 搜索</span></span><br><span class="line">conda search flask</span><br></pre></td></tr></table></figure><h2><span id="配置高速下载">配置高速下载</span></h2><p>打开我们的<code>~/.condarc</code>文件,配置如下</p><figure class="highlight sh"><table><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"> channels:</span><br><span class="line"> - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/</span><br><span class="line"> - defaults</span><br><span class="line">show_channel_urls: <span class="literal">true</span></span><br><span class="line">ssl_verify: <span class="literal">true</span></span><br><span class="line">~</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p>由于mac使用到期,公司给还了新的mac,安装时安装了<code>Anaconda(for 3.6+)</code>, Python 2.7慢慢没人维护了,是时候开始切换到3.6+上了。但是很尴尬的是,有些不再维护的库,却依然只支持<code>python2.7</code
</summary>
<category term="Tools" scheme="http://ownwell.github.io/categories/Tools/"/>
<category term="Python" scheme="http://ownwell.github.io/tags/Python/"/>
</entry>
<entry>
<title>试了一把Baidu的语言处理sdk</title>
<link href="http://ownwell.github.io/2018/08/16/try-baidu-ai/"/>
<id>http://ownwell.github.io/2018/08/16/try-baidu-ai/</id>
<published>2018-08-16T15:58:49.000Z</published>
<updated>2018-08-16T16:02:49.556Z</updated>
<content type="html"><![CDATA[<p>不知怎么滴,一开始都唱衰Baidu的自媒体,突然之间改了风向,改打击腾讯了,看来Robin的在<code>Summer Party</code>上的正能量还是很有用的么?</p><p>今天无意间看了<code>ai.baidu.com</code>发现已经有了语言分析的sdk,于是乎试了一把,挺好用,就过来按理给大家把。</p><p>我用的java版本的语言处理基础技术。<br>先注册,获取appid等应用信息,下载sdk。<br>下面就是很有意思,我其实一直想学习如何给文章打标签。有了这个sdk,瞬间解决l我的问题。</p><p>我从头条上搞到了一条新闻,试了下。</p><figure class="highlight java"><table><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="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">sample2</span><span class="params">(AipNlp client)</span> </span>{</span><br><span class="line"> String title = <span class="string">"房叔房婶都跪了!“房祖宗”坐拥 15645 套房产"</span>;</span><br><span class="line"> String content = <span class="string">"港元(约合人民币 2.06 亿元)。其中,减税最多的那位,也就是坐拥 15645 套房产的房祖宗,获得减税额度高达 1.026 亿港元(约合人民币 8200 万元)。另外 9 位,即使是名单中的最后一位,也拥有 1258 套房产,免税额为 890 万港元(约合人民币 713 万元)。"</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 传入可选参数调用接口</span></span><br><span class="line"> HashMap<String, Object> options = <span class="keyword">new</span> HashMap<String, Object>();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 文章标签</span></span><br><span class="line"> JSONObject res = client.keyword(title, content, options);</span><br><span class="line"> System.out.println(res.toString(<span class="number">2</span>));</span><br><span class="line"></span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>结果很赞。</p><figure class="highlight java"><table><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><br><span class="line"> <span class="string">"log_id"</span>: <span class="number">403842984771490524</span>,</span><br><span class="line"> <span class="string">"items"</span>: [</span><br><span class="line"> {</span><br><span class="line"> <span class="string">"score"</span>: <span class="number">0.836735</span>,</span><br><span class="line"> <span class="string">"tag"</span>: <span class="string">"港元人民币"</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="string">"score"</span>: <span class="number">0.80301</span>,</span><br><span class="line"> <span class="string">"tag"</span>: <span class="string">"购房"</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="string">"score"</span>: <span class="number">0.776263</span>,</span><br><span class="line"> <span class="string">"tag"</span>: <span class="string">"房地产业"</span></span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>居然给每个标签还打了分。是不是可以为自己的博客,打了个tag呢?</p><p>更多好玩的,还在摊上中……</p>]]></content>
<summary type="html">
<p>不知怎么滴,一开始都唱衰Baidu的自媒体,突然之间改了风向,改打击腾讯了,看来Robin的在<code>Summer Party</code>上的正能量还是很有用的么?</p>
<p>今天无意间看了<code>ai.baidu.com</code>发现已经有了语言分析的s
</summary>
<category term="编程" scheme="http://ownwell.github.io/categories/%E7%BC%96%E7%A8%8B/"/>
<category term="AI" scheme="http://ownwell.github.io/tags/AI/"/>
</entry>
<entry>
<title>VS code的好用的插件和方便的配置</title>
<link href="http://ownwell.github.io/2018/08/16/good-tools-for-vs-code/"/>
<id>http://ownwell.github.io/2018/08/16/good-tools-for-vs-code/</id>
<published>2018-08-16T15:33:01.000Z</published>
<updated>2019-07-25T14:54:52.759Z</updated>
<content type="html"><![CDATA[<p>作为一个Android的开发者,很多时候是和<code>Android Studio</code>打交道,可是他实在太重,于是就习惯了一个很轻盈的文本工具,没错,有很多选择,比如<a href="https://www.sublimetext.com/" target="_blank" rel="noopener">sublimetext</a>,<a href="https://atom.io/" target="_blank" rel="noopener">atom</a>,不过我还是喜欢Vscode,出身贵族(微软),插件很多,主要是的操作方便,颜值很高,不信,给你图感受下。</p><p><img src="https://raw.githubusercontent.com/ownwell/image-bed/master/img/2019-07-25-22-42-04.png" alt="图1"></p><p>有没有觉得很舒服。</p><p>这就给大家介绍几款在使用Vs Code常用的工具或者配置</p><h1><span id="常用插件">常用插件</span></h1><p><code>VS code</code>集成了很多插件,也有自己的插件应用市场,你可以直接在VS code里面搜索。<br><code>VS code</code>集成的插件如代码高亮,Markdown编辑预览等功能也是很赞的。那我们就给的大家推荐几个我常用的几款插件。</p><h2><span id="code-blue">code blue</span></h2><p>如上图中的所示,我们可以看到这样的配合,很保护眼睛,且很美观,很惊艳吧。<br>如上图所示,蓝色背景,不是很艳丽,也不显得压抑,蓝色刚刚好。</p><p>可以直接去<code>VS Code</code>的时长搜索<code>code blue</code>进行下载。</p><h2><span id="json-tools">JSON Tools</span></h2><p>这个插件主要是用于格式化,使用它可以直接格式化json<br><img src="!%5B%5D(https://raw.githubusercontent.com/ownwell/image-bed/master/img/json_1.gif)" alt></p><h2><span id="material-icon-theme">Material Icon Theme</span></h2><p>可以是我们的文件看起来很美观哦,这个对于开发html/js可能更适合,会用图标区分每个文件的类型。</p><p><img src="https://raw.githubusercontent.com/ownwell/image-bed/master/img/2019-07-25-22-53-18.png" alt></p><p>## </p><h1><span id="常用配置">常用配置</span></h1><h2><span id="配置vs-code到环境变量">配置<code>VS code</code>到环境变量</span></h2><p>给我们的code起个别名,使得我们在命令终端就可以直接打开<code>VS code</code>。<br>配置也很简单,只需要在你的shell配置里起个别名形式即可。<br>在<code>~/.zshrc</code>(若你使用的bash,可以在<code>~/.bashrc</code>)下配置如下命令:</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">export</span> PATH=/Applications/Visual\ Studio\ Code.app/Contents/Resources/app/bin:<span class="variable">$PATH</span></span><br></pre></td></tr></table></figure><p>就可以直接在terminal下直接输入<code>code ~/.zshrc</code>直接打开zshrc配置了(有点类似vim)</p>]]></content>
<summary type="html">
<p>作为一个Android的开发者,很多时候是和<code>Android Studio</code>打交道,可是他实在太重,于是就习惯了一个很轻盈的文本工具,没错,有很多选择,比如<a href="https://www.sublimetext.com/" target="_
</summary>
<category term="Tools" scheme="http://ownwell.github.io/categories/Tools/"/>
<category term="VS code" scheme="http://ownwell.github.io/tags/VS-code/"/>
</entry>
<entry>
<title>Gradle插件开发(1) - Hello world</title>
<link href="http://ownwell.github.io/2018/07/30/android-gradle_1/"/>
<id>http://ownwell.github.io/2018/07/30/android-gradle_1/</id>
<published>2018-07-30T13:52:10.000Z</published>
<updated>2018-08-20T15:19:19.807Z</updated>
<content type="html"><![CDATA[<p>目前的开发工具主要是在Android Studio上,对这个工具真的是又恨又爱,只能收希望它越来越好。<br>这几天,一直在看Gradle插件的相关的东西,希望将自己的这些学习成长的经历记录下来。由易到难,开发一个自己的插件。</p><blockquote><p>准备工作</p><ol><li>Android Studio(建议3.0+)</li><li>gradle 4.0+</li></ol></blockquote><a id="more"></a><p>我们需要开发一个能打印<code>hello world</code>的gradle插件,看到hello world,是不是觉得凉凉了,哈哈,一个好的程序员都是从hello world开始的,千里之行始于足下。<br>我们希望希望开发一个简单的插件,就是打印一行:hello world。</p><p>先解释下,所以的gradle的插件,其实就是一个可以挂载在我们工作空间的一个工具,但我们运行我们的gradle脚本时,我们就可以运行我们的插件,这个插件是为我们做一些人工也可以替代的工作,甚至是多个task任务。</p><h2><span id="项目结构">项目结构</span></h2><p>我们先在我们的工作控件新建一个Android library项目plugin,仅保留src下的main其他的都删掉吧.</p><p><img src="http://pc0oz0yc3.bkt.clouddn.com/MacHi%202018-08-01%2022-28-39_20188122298.png" alt="20188122298"></p><p>src/main下新建一个groovy和resources的文件夹。<br>其中groovy的文件用来存源码,resources存放静态资源和配置,build.gradle是我们的build环境。<br>先来看下build.gradle吧。</p><figure class="highlight java"><table><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">apply plugin: <span class="string">'groovy'</span></span><br><span class="line"></span><br><span class="line">dependencies {</span><br><span class="line"> <span class="comment">//gradle sdk</span></span><br><span class="line"> <span class="function">compile <span class="title">gradleApi</span><span class="params">()</span></span></span><br><span class="line"><span class="function"> <span class="comment">//groovy sdk</span></span></span><br><span class="line"><span class="function"> compile <span class="title">localGroovy</span><span class="params">()</span></span></span><br><span class="line"><span class="function">}</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function">repositories </span>{</span><br><span class="line"> mavenCentral()</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><br>一个插件的主要工作其实是在src文件下,所以我们需要在<code>src/main/groovy</code>下新建一个文件包名<code>cc.cyning.plugin</code>,在这个包下新建一个<code>MyPlugin.groovy</code>文件。<br>MyPlugin.groovy文件:</p><figure class="highlight java"><table><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="keyword">import</span> org.gradle.api.Plugin</span><br><span class="line"><span class="keyword">import</span> org.gradle.api.Project</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyPlugin</span> <span class="keyword">implements</span> <span class="title">Plugin</span><<span class="title">Project</span>> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">apply</span><span class="params">(Project project)</span> </span>{</span><br><span class="line"> System.out.println(<span class="string">"========================"</span>);</span><br><span class="line"> System.out.println(<span class="string">"hello world"</span>);</span><br><span class="line"> System.out.println(<span class="string">"========================"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote><p>在groovy下不是应该用groovy语言么?<br>其实用java也是ok的,对于不会groovy的同学用java肯定是可以的。</p></blockquote><p>插件实现了<code>org.gradle.api.Plugin</code>接口,我们需要实现<code>apply</code>方法。</p><p>插件的工作完成,可是怎么标识<code>MyPlugin</code>文件是插件的入口文件呢?</p><p>在<code>src/main/resources</code>下我们需要定义一个<code>properties</code>文件:</p><p>resources<br><br> └── META-INF<br><br> └── gradle-plugins<br><br> └── cyning.properties<br></p><p> cyning.property:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">implementation-<span class="class"><span class="keyword">class</span></span>=cc.cyning.plugin.MyPlugin</span><br></pre></td></tr></table></figure><p>这样就是一个完整的插件项目的结构,groovy是我们的实现代码,·、<code>resources/META-INF</code>配置插件的实现类。</p><h2><span id="插件使用">插件使用</span></h2><h3><span id="maven-本地仓库配置">maven 本地仓库配置</span></h3><p>为了插件的使用,我们需要引入maven本地仓库,怎么上传远程仓库,可以参考我的另外一篇拙作:<a href="http://ownwell.github.io/2016/07/14/push-library-to-jcenter/">开源自己的项目到JCenter</a>,不过为了插架开发没必要这么麻烦。</p><blockquote><p>若是你了解可以直接跳过本小节内容。</p></blockquote><p>我们需要在<code>build.gradle</code>下配置上传到本地的maven配置:</p><figure class="highlight groovy"><table><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></pre></td><td class="code"><pre><span class="line">apply <span class="string">plugin:</span> <span class="string">'groovy'</span></span><br><span class="line">apply <span class="string">plugin:</span> <span class="string">'maven'</span></span><br><span class="line"></span><br><span class="line">......</span><br><span class="line"></span><br><span class="line">group=<span class="string">'cc.cyning.plugin'</span></span><br><span class="line">version=<span class="string">'1.0.0'</span></span><br><span class="line"></span><br><span class="line">uploadArchives {</span><br><span class="line"> repositories {</span><br><span class="line"> mavenDeployer {</span><br><span class="line"> <span class="comment">//提交到远程服务器:</span></span><br><span class="line"> <span class="comment">// repository(url: "http://www.xxx.com/repos") {</span></span><br><span class="line"> <span class="comment">// authentication(userName: "admin", password: "admin")</span></span><br><span class="line"> <span class="comment">// }</span></span><br><span class="line"> <span class="comment">//本地的Maven地址设置为在项目的maven文件下(提前新建mave这个文件夹)</span></span><br><span class="line"> repository(<span class="string">url:</span> <span class="string">"file://${rootProject.projectDir}/maven"</span>)</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>使用<code>./gradlew :plugin:uploadArchives</code>即可在项目下看到maven文件下产生了文件:</p><p><img src="http://pc0oz0yc3.bkt.clouddn.com/MacHi%202018-08-01%2023-02-28_20188123242.png" alt="20188123242"></p><p>而我们的项目怎么用呢?</p><h3><span id="使用插件">使用插件</span></h3><p>上一步,我们已经生成maven插件,为了能让插件运行,我们需要导入这个gradle插件。<br>app的<code>build.gradle</code>配置:</p><figure class="highlight groovy"><figcaption><span>{</span></figcaption><table><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">buildscript {</span><br><span class="line"> repositories {</span><br><span class="line"> maven {<span class="comment">//本地Maven仓库地址 切记:三个斜杠,</span></span><br><span class="line"> url <span class="string">"file:///${rootProject.projectDir}/maven/"</span></span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> dependencies {</span><br><span class="line"> <span class="comment">//格式为-->group:module:version</span></span><br><span class="line"> classpath <span class="string">'cc.cyning.plugin:plugin: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">//cyning为resources/META-INF/gradle-plugins</span></span><br><span class="line"><span class="comment">//下的properties文件名称</span></span><br><span class="line">apply <span class="string">plugin:</span> <span class="string">'cyning'</span></span><br></pre></td></tr></table></figure><p>运行我们的插件:</p><figure class="highlight shell"><table><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="meta">></span><span class="bash">./gradlew :app:clean </span></span><br><span class="line"></span><br><span class="line"><span class="meta">></span><span class="bash"> Configure project :app </span></span><br><span class="line">========================</span><br><span class="line">hello world</span><br><span class="line">========================</span><br></pre></td></tr></table></figure><h2><span id="小结">小结</span></h2><p>我们这节的目的就是能熟悉插件的卡法流程,尤其是开发插件和使用的各种配置,要对应上,properties的配置,以及gradle使用。</p>]]></content>
<summary type="html">
<p>目前的开发工具主要是在Android Studio上,对这个工具真的是又恨又爱,只能收希望它越来越好。<br>这几天,一直在看Gradle插件的相关的东西,希望将自己的这些学习成长的经历记录下来。由易到难,开发一个自己的插件。</p>
<blockquote>
<p>准备工作</p>
<ol>
<li>Android Studio(建议3.0+)</li>
<li>gradle 4.0+</li>
</ol>
</blockquote>
</summary>
<category term="Android" scheme="http://ownwell.github.io/categories/Android/"/>
<category term="Gradle" scheme="http://ownwell.github.io/tags/Gradle/"/>
</entry>
<entry>
<title>numpy中的axis使用</title>
<link href="http://ownwell.github.io/2018/06/19/how-to-use-axis/"/>
<id>http://ownwell.github.io/2018/06/19/how-to-use-axis/</id>
<published>2018-06-19T12:23:32.000Z</published>
<updated>2018-08-19T13:25:07.434Z</updated>
<content type="html"><![CDATA[<h2><span id="axis">axis</span></h2><p>在数据统计时,我们经常会使用numpy+pandas来进行统计,有一个很有意思的问题,就是轴的问题,这个类似于数学中的x轴,y轴,如下面个的例子。</p><figure class="highlight python"><table><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">data = pd.DataFrame(np.arange(<span class="number">16</span>).reshape(<span class="number">4</span>,<span class="number">4</span>),columns=list(<span class="string">'abcd'</span>)) </span><br><span class="line">data</span><br></pre></td></tr></table></figure><blockquote><p>本篇文章的水所有代码是在jupyter notebook下进行的。</p></blockquote><p><img src="http://pc0oz0yc3.bkt.clouddn.com/1534682161.png?imageMogr2/thumbnail/!70p" alt></p><p>但我们需要自己计算某一咧的平均数是如何我们可以通过<code>axis</code>来解决这个问题。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">data.mean(axis=<span class="number">1</span>)</span><br></pre></td></tr></table></figure><p>其结果是</p><figure class="highlight python"><table><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="number">0</span> <span class="number">1.5</span></span><br><span class="line"><span class="number">1</span> <span class="number">5.5</span></span><br><span class="line"><span class="number">2</span> <span class="number">9.5</span></span><br><span class="line"><span class="number">3</span> <span class="number">13.5</span></span><br></pre></td></tr></table></figure><h2><span id="axis-1-或者0的含义">axis = 1 或者0的含义</span></h2><p><code>axis</code>实际上就是表示轴。对于一个二维空间,axis=1代表横轴,axis=0按照竖轴。</p><p>上述代码是计算横轴的数字的平均值,所以是1.</p><p><img src="http://pc0oz0yc3.bkt.clouddn.com/1534682461.png?imageMogr2/thumbnail/!70p" alt></p><p>再来一个例子:</p><figure class="highlight python"><table><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">func = <span class="keyword">lambda</span> x : x.max()- x.min()</span><br><span class="line">result = data.apply(func)</span><br></pre></td></tr></table></figure><p>得出的结果肯定是</p><figure class="highlight java"><table><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">a <span class="number">12</span></span><br><span class="line">b <span class="number">12</span></span><br><span class="line">c <span class="number">12</span></span><br><span class="line">d <span class="number">12</span></span><br></pre></td></tr></table></figure><p>由于<code>axis</code>默认是0,所以func函数操作的数字都是一个竖轴上。</p><h2><span id="小结">小结</span></h2><p><code>axis</code>实际上是每次运算都是按照某一个轴进行的,也就行说他的上一个或者下一个数字正好处于一个轴上。</p>]]></content>
<summary type="html">
<h2><span id="axis">axis</span></h2><p>在数据统计时,我们经常会使用numpy+pandas来进行统计,有一个很有意思的问题,就是轴的问题,这个类似于数学中的x轴,y轴,如下面个的例子。</p>
<figure class="highligh
</summary>
<category term="Python" scheme="http://ownwell.github.io/categories/Python/"/>
<category term="pandas" scheme="http://ownwell.github.io/tags/pandas/"/>
</entry>
<entry>
<title>JNI开发笔记(2) -- Native要校验APP安全</title>
<link href="http://ownwell.github.io/2018/05/02/jni-security-4-app/"/>
<id>http://ownwell.github.io/2018/05/02/jni-security-4-app/</id>
<published>2018-05-02T13:52:10.000Z</published>
<updated>2018-08-17T05:49:00.036Z</updated>
<content type="html"><![CDATA[<p>由于使用NDK开发,可以中间的数据放入到so中,这样是的关键数据更安全。因为破解原生代码相对来说太容易,而so文件相对来说门槛较高。<br>我们本篇就是从安全角度来使用NDK开发,将重要的数据放入NDK中,同时将重要的加密也放入到NDK开发,这样在一定程度上可以保证APP应用的安全。</p><a id="more"></a><h1><span id="校验apk安装包信息">校验APK安装包信息</span></h1><h2><span id="获取应用包名">获取应用包名</span></h2><p>在<a href="http://ownwell.github.io/2018/05/02/JNI%E5%BC%80%E5%8F%91%E7%AC%94%E8%AE%B0%EF%BC%881%EF%BC%89/">上篇文章</a>中,我们可以通过NDK的方法来获取APK的包名。</p><figure class="highlight c++"><table><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="keyword">extern</span> <span class="string">"C"</span> JNIEXPORT jstring</span><br><span class="line">JNICALL</span><br><span class="line">Java_me_cyning_helloworld_NativeUtils_getPackageName(</span><br><span class="line"> JNIEnv *env, jclass clazz, jobject instance) {</span><br><span class="line"> jclass nativeClass = env->GetObjectClass(instance);</span><br><span class="line"> jmethodID jmethodID1 = env->GetMethodID(nativeClass, <span class="string">"getPackageName"</span>, <span class="string">"()Ljava/lang/String;"</span>);</span><br><span class="line"> jstring packageName = <span class="keyword">static_cast</span><jstring>(env->CallObjectMethod(instance, jmethodID1));</span><br><span class="line"> <span class="keyword">return</span> packageName;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>Java层调用:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">native</span> String <span class="title">getPackageName</span><span class="params">(Context context)</span></span>;</span><br></pre></td></tr></table></figure><p> 但是在实际的应用中,能不能不传Context来获取到全文的Context呢?答案是可以的!</p><figure class="highlight java"><table><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">Class<?> activityThreadClzz = Class.forName(<span class="string">"android.app.ActivityThread"</span>);</span><br><span class="line">Method currentApplication = activityThreadClzz.getMethod(<span class="string">"currentApplication"</span>);</span><br><span class="line">Application application = (Application) currentApplication.invoke(<span class="keyword">null</span>);</span><br></pre></td></tr></table></figure><p>我们可以参考:<a href="http://androidxref.com/7.1.1_r6/xref/frameworks/base/core/java/android/app/ActivityThread.java#1827" target="_blank" rel="noopener">ActivityThread</a>。由于<code>currentApplication</code>是个静态方法,可以通过反射获取到一个Application对象。</p><p>结合之前的代码我们可以合并如下代码:</p><figure class="highlight java"><table><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"><span class="function"><span class="keyword">static</span> jobject <span class="title">getApplication</span><span class="params">(JNIEnv *env)</span> </span>{</span><br><span class="line"> jobject application = NULL;</span><br><span class="line"> jclass activity_thread_clz = env->FindClass(<span class="string">"android/app/ActivityThread"</span>);</span><br><span class="line"> <span class="keyword">if</span> (activity_thread_clz != NULL) {</span><br><span class="line"> <span class="comment">// 得到ActivityThread的currentApplication的静态方法id</span></span><br><span class="line"> jmethodID currentApplication = env->GetStaticMethodID(</span><br><span class="line"> activity_thread_clz, <span class="string">"currentApplication"</span>, <span class="string">"()Landroid/app/Application;"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (currentApplication != NULL) {</span><br><span class="line"> <span class="comment">// 调用ActivityThread的静态方法,返回结果application</span></span><br><span class="line"> application = env->CallStaticObjectMethod(activity_thread_clz, currentApplication);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> LOGE(<span class="string">"Cannot find method: currentApplication() in ActivityThread."</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 释放内存</span></span><br><span class="line"> env->DeleteLocalRef(activity_thread_clz);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> LOGE(<span class="string">"Cannot find class: android.app.ActivityThread"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> application;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">static</span> jstring <span class="title">getPackageName</span><span class="params">( JNIEnv *env,jobject instance )</span> </span>{</span><br><span class="line"> jclass nativeClass = env->GetObjectClass(instance);</span><br><span class="line"> jmethodID jmethodID1 = env->GetMethodID(nativeClass, <span class="string">"getPackageName"</span>, <span class="string">"()Ljava/lang/String;"</span>);</span><br><span class="line"> jstring packageName = static_cast<jstring>(env->CallObjectMethod(instance, jmethodID1));</span><br><span class="line"> <span class="keyword">return</span> packageName;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//根据context来获取包名</span></span><br><span class="line">extern <span class="string">"C"</span> JNIEXPORT jstring</span><br><span class="line">JNICALL</span><br><span class="line">Java_me_cyning_helloworld_NativeUtils_getPackageName(</span><br><span class="line"> JNIEnv *env, jclass clazz) {</span><br><span class="line"> <span class="keyword">return</span> getPackageName(env, getApplication(env));</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>但是得到包名就安全了么,NO!有些”居心不良”的人可能会改我的包签名,好了那怎么就校验下包签名,不合法的APP让它直接GG。<br>怎么获取包签名信息呢?</p><h2><span id="获取应用的签名信息">获取应用的签名信息</span></h2><p>怎么获取一个APP的签名信息呢?还是和获取包名一样,我们需要拿到在java上的实现。</p><figure class="highlight java"><table><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">PackageManager pm = context.getPackageManager();</span><br><span class="line"> PackageInfo pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);</span><br><span class="line"> Signature[] signatures = pi.signatures;</span><br><span class="line"> Signature signature0 = signatures[<span class="number">0</span>];</span><br><span class="line"> String string = signature0.toCharsString();</span><br></pre></td></tr></table></figure><p>用NDK怎么实现呢?<br>若是你不知道C怎么调用java,这是个很好的学习范例:</p><figure class="highlight java"><table><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></pre></td><td class="code"><pre><span class="line">extern <span class="string">"C"</span> JNIEXPORT jint</span><br><span class="line">JNICALL</span><br><span class="line"> Java_me_cyning_helloworld_NativeUtils_getSignature(JNIEnv *env, jclass clazz) {</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 获取context</span></span><br><span class="line"> jobject context_obj = getApplication(env);</span><br><span class="line"> <span class="comment">// 获取到context的类</span></span><br><span class="line"> jclass context_clazz = env->GetObjectClass(context_obj);</span><br><span class="line"> jstring packageName = getPackageName(env, context_obj);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//获取到context中的getPackageManager的方法id</span></span><br><span class="line"> jmethodID getPackageManagerMethod = env->GetMethodID(context_clazz, <span class="string">"getPackageManager"</span>, <span class="string">"()Landroid/content/pm/PackageManager;"</span>);</span><br><span class="line"> <span class="comment">// 调用getPackageManager方法,获取到PackageManager对象</span></span><br><span class="line"> jobject pm_obj = env->CallObjectMethod(context_obj, getPackageManagerMethod);</span><br><span class="line"></span><br><span class="line"> jclass pm_clazz = env->GetObjectClass(pm_obj);</span><br><span class="line"> jmethodID getPackageInfoMethod = env->GetMethodID(pm_clazz, <span class="string">"getPackageInfo"</span>, <span class="string">"(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;"</span>);</span><br><span class="line"> jobject packageInfo_obj = env->CallObjectMethod(pm_obj, getPackageInfoMethod, packageName, <span class="number">64</span>);</span><br><span class="line"></span><br><span class="line"> jclass pi_clazz = env->GetObjectClass(packageInfo_obj);</span><br><span class="line"> <span class="comment">// Signature[] signatures = pi.signatures; 返回值是数组[]</span></span><br><span class="line"> jfieldID signatures_field = env->GetFieldID(pi_clazz, <span class="string">"signatures"</span>, <span class="string">"[Landroid/content/pm/Signature;"</span>);</span><br><span class="line"></span><br><span class="line"> jobject signatures = env->GetObjectField(packageInfo_obj, signatures_field);</span><br><span class="line"> jobjectArray signatures_array = (jobjectArray) signatures;</span><br><span class="line"></span><br><span class="line"> jobject signature0 = env->GetObjectArrayElement(signatures_array, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> jclass signature_clazz = env->GetObjectClass(signature0);</span><br><span class="line"> jmethodID toCharsStringMethod = env->GetMethodID(signature_clazz, <span class="string">"toCharsString"</span>, <span class="string">"()Ljava/lang/String;"</span>);</span><br><span class="line"> jstring signature_string = (jstring)env->CallObjectMethod(signature0, toCharsStringMethod);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 释放内存</span></span><br><span class="line"> env->DeleteLocalRef(context_obj);</span><br><span class="line"> env->DeleteLocalRef(context_clazz);</span><br><span class="line"> env->DeleteLocalRef(packageName);</span><br><span class="line"> env->DeleteLocalRef(pm_obj);</span><br><span class="line"> env->DeleteLocalRef(pm_clazz);</span><br><span class="line"> env->DeleteLocalRef(packageInfo_obj);</span><br><span class="line"> env->DeleteLocalRef(pi_clazz);</span><br><span class="line"> env->DeleteLocalRef(signatures);</span><br><span class="line"> env->DeleteLocalRef(signature0);</span><br><span class="line"> env->DeleteLocalRef(signatures_array);</span><br><span class="line"> env->DeleteLocalRef(signature_clazz);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> signature_string;</span><br></pre></td></tr></table></figure><p>实现的对应代码:<br><img src="http://7xwwa2.com1.z0.glb.clouddn.com/2018-05-03-21-54-19.png" alt></p><p>这样就可以将获取的签名和本地的签名比较:</p><figure class="highlight java"><table><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="keyword">const</span> <span class="keyword">char</span> *sign = env->GetStringUTFChars(signature_string, NULL);</span><br><span class="line"> <span class="keyword">int</span> result = strcmp(sign,</span><br><span class="line"> <span class="string">"app的签名"</span>);</span><br><span class="line"> env->ReleaseStringUTFChars(signature_string, sign);</span><br><span class="line"> env->DeleteLocalRef(signature_string);</span><br><span class="line"> <span class="keyword">if</span> (result == <span class="number">0</span>) { <span class="comment">// 签名一致</span></span><br><span class="line"> <span class="keyword">return</span> JNI_OK;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> JNI_ERR;</span><br></pre></td></tr></table></figure><p>重写<code>JNI_OnLoad</code>:</p><figure class="highlight java"><table><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="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 = NULL;</span><br><span class="line"> <span class="keyword">if</span> (vm->GetEnv((<span class="keyword">void</span> **) &env, JNI_VERSION_1_4) != JNI_OK) {</span><br><span class="line"> <span class="keyword">return</span> JNI_ERR;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (verifySign(env) == JNI_OK) {</span><br><span class="line"> <span class="keyword">return</span> JNI_VERSION_1_4;</span><br><span class="line"> }</span><br><span class="line"> LOGE(<span class="string">"签名不一致!"</span>);</span><br><span class="line"> <span class="keyword">return</span> JNI_ERR;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这样就会在<code>System.loadLibrary("native-lib");</code>时,就会调用<code>JNI_OnLoad</code>,当签名不一致时就会直接导致崩溃。</p><h1><span id="加密">加密</span></h1><p> 当然了,我们也可以在NDK层实现加密,如网络请求时,将参数加密等,这样相对来说:</p><ol><li><p>安全 底层实现的加密不易破解</p></li><li><p>高效 c实现的加密相对来说速度快,效率高</p><p>具体的代码可以参考这篇<a href="http://blog.majiajie.me/2016/04/12/NDK%E5%AE%9E%E7%8E%B0%E7%9A%84%E5%8A%A0%E5%AF%86-%E8%A7%A3%E5%AF%86%E5%BA%93-Codec/" target="_blank" rel="noopener">文章</a>,不过可以根据需求来自己定义加密算法。</p></li></ol>]]></content>
<summary type="html">
<p>由于使用NDK开发,可以中间的数据放入到so中,这样是的关键数据更安全。因为破解原生代码相对来说太容易,而so文件相对来说门槛较高。<br>我们本篇就是从安全角度来使用NDK开发,将重要的数据放入NDK中,同时将重要的加密也放入到NDK开发,这样在一定程度上可以保证APP应用的安全。</p>
</summary>
<category term="Android" scheme="http://ownwell.github.io/categories/Android/"/>
<category term="JNI开发" scheme="http://ownwell.github.io/tags/JNI%E5%BC%80%E5%8F%91/"/>
</entry>
<entry>
<title>JNI开发笔记(1)- 入门</title>
<link href="http://ownwell.github.io/2018/04/27/JNI%E5%BC%80%E5%8F%91%E7%AC%94%E8%AE%B0%EF%BC%881%EF%BC%89/"/>
<id>http://ownwell.github.io/2018/04/27/JNI开发笔记(1)/</id>
<published>2018-04-27T13:52:10.000Z</published>
<updated>2018-08-20T15:23:46.655Z</updated>
<content type="html"><![CDATA[<p>Android Studio 3.0 更新了很多新特性,其中对C++开发者也越来越友好。目前Android Studio默认构建工具是CMake(当然也是支持ndk-build),我们将使用Cmake来开始编写我们的Helleworld。</p><h2><span id="准备">准备</span></h2><p> 更新我们的sdk-tool中的LLDB、CMake和NDK三个选项。</p><p><img src="http://7xwwa2.com1.z0.glb.clouddn.com/MacHi%202018-05-02%2021-19-17.png" alt></p><h2><span id="创建项目">创建项目</span></h2><p>可以先创建一个helloWorld项目来看下它和普通的Android项目的区别。<br><img src="http://7xwwa2.com1.z0.glb.clouddn.com/2018-05-02-21-25-11.png" alt></p><p><img src="http://7xwwa2.com1.z0.glb.clouddn.com/2018-05-02-21-25-59.png" alt></p><p>这样一步步的next创建了一个简单的Helleworld的用CMake作为构建工具的JNI程序。</p><p>来看下它和普通的Android程序的区别吧。</p><p><img src="http://7xwwa2.com1.z0.glb.clouddn.com/2018-05-02-21-32-48.png" alt></p><p>这三处使我们和一般应用程序中所没有的,所以我们在普通程序中添加这三处也是可以添加自己的c/c++代码的。</p><ol><li><p>cpp文件夹使我们的c/c++代码代码的目录,这和src是我们的源代码目录类似。</p></li><li><p><code>CMakeLists.txt</code>是和Gradle交互的一个桥梁,里面的内容类似于我们之前写的make文件。</p></li><li><p>在主APP下要写两处<code>externalNativeBuild</code>,一处是可以根据处理的来打某一个平台的so文件,而另一处是为了将<code>CMakeLists.txt</code>和Gradle构建关联。</p></li></ol><p>运行项目,通过<code>Analyze APK</code>是可以直接看到打出来的APK是有这个so的。</p><blockquote><p>配置某个平台(如x86)的so</p></blockquote><p>如上图上的标记3,在第一处添加cpu对应的架构:</p><blockquote></blockquote><figure class="highlight java"><table><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></pre></td><td class="code"><pre><span class="line">android {</span><br><span class="line"> defaultConfig {</span><br><span class="line"></span><br><span class="line"> ......</span><br><span class="line"> externalNativeBuild {</span><br><span class="line"> cmake {</span><br><span class="line"> cppFlags <span class="string">""</span></span><br><span class="line"> }</span><br><span class="line"> ndk {</span><br><span class="line"> abiFilters <span class="string">'x86'</span></span><br><span class="line"> }</span><br><span class="line"><span class="comment">// ndk {</span></span><br><span class="line"><span class="comment">// abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a','armeabi'</span></span><br><span class="line"><span class="comment">// }</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> buildTypes {</span><br><span class="line"> ......</span><br><span class="line"> }</span><br><span class="line"> ......</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2><span id="编写一个简单的jni程序">编写一个简单的JNI程序</span></h2><p>之前在网易工作时,发现网易系的有些应用是通过so文件来保证数据校验,如加密一个字符串,同时会传一个Context,这样可以通过context来校验是不是正版应用,校验应用的安全信息。我们就来实现一个通过Context来获取包名。</p><ol><li><p>native方法<br>我们是通过java代码调用native方法,所以先声明一个native方法:</p><figure class="highlight java"><table><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">package</span> me.cyning.helloworld;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> android.content.Context;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">NativeUtils</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">native</span> String <span class="title">getPackageName</span><span class="params">(Context context)</span></span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>native代码<br>在我们的<code>native-lib.cpp</code>来实现具体的功能:</p><figure class="highlight c++"><table><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="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><jni.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><string></span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">extern</span> <span class="string">"C"</span> JNIEXPORT jstring</span><br><span class="line">JNICALL</span><br><span class="line">Java_me_cyning_helloworld_NativeUtils_getPackageName(</span><br><span class="line"> JNIEnv *env, jclass clazz, jobject instance) {</span><br><span class="line"> jclass nativeClass = env->GetObjectClass(instance);</span><br><span class="line"> jmethodID jmethodID1 = env->GetMethodID(nativeClass, <span class="string">"getPackageName"</span>, <span class="string">"()Ljava/lang/String;"</span>);</span><br><span class="line"> jstring packageName = <span class="keyword">static_cast</span><jstring>(env->CallObjectMethod(instance, jmethodID1));</span><br><span class="line"> <span class="keyword">return</span> packageName;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ol><p>让我们来解释下:</p><ul><li><p>函数签名:Java-(包名+类型+函数)(参数)</p></li><li><p>jni编程的类型和java类型的区别<br><img src="http://7xj9f0.com1.z0.glb.clouddn.com/944365-e2d3218e2bf0934f.png" alt></p></li><li><p>JNI 函数访问 Java 对象的变量<br><img src="http://7xj9f0.com1.z0.glb.clouddn.com/2018-05-02-22-35-22.png" alt></p></li></ul><p>结合上面的具体代码,很容易理解,先拿到<code>Context</code>对应的class类,通过这个类得到<code>getPackageName</code>方法的id,通过<code>CallObjectMethod</code>这个<code>Context</code>实例的<code>getPackageName</code>方法,看着很像反射的用法。</p><h2><span id="参考">参考</span></h2><ul><li><a href="https://blog.csdn.net/cloverjf/article/details/78652245" target="_blank" rel="noopener">https://blog.csdn.net/cloverjf/article/details/78652245</a></li></ul>]]></content>
<summary type="html">
<p>Android Studio 3.0 更新了很多新特性,其中对C++开发者也越来越友好。目前Android Studio默认构建工具是CMake(当然也是支持ndk-build),我们将使用Cmake来开始编写我们的Helleworld。</p>
<h2><span id=
</summary>
<category term="Android" scheme="http://ownwell.github.io/categories/Android/"/>
<category term="JNI开发" scheme="http://ownwell.github.io/tags/JNI%E5%BC%80%E5%8F%91/"/>
</entry>
<entry>
<title>白话ACSCII,GBK,Unicode,UTF-8</title>
<link href="http://ownwell.github.io/2018/04/01/introduce-to-unicode/"/>
<id>http://ownwell.github.io/2018/04/01/introduce-to-unicode/</id>
<published>2018-04-01T13:48:26.000Z</published>
<updated>2018-04-01T13:55:17.000Z</updated>
<content type="html"><![CDATA[<p>ACSCII,GBK,Unicode,UTF-8这些名词都跟编码有关系,究竟是什么关系呢,看网上的确有很多解释,也很详尽,不过我觉得还是很有必要把我自己学习的历程记录下来。</p><h1><span id="acscii码">ACSCII码</span></h1><p>在计算机的世界是只有0和1,数据的计算,传输,保存等过程都是通过0和1,可以说0和1是组成计算机的元素。<br>对于最早使用计算机的美国人,为了记录a,b,c,d等这些字母,1,2,3等还有其他的符号,共收集了128个(2的7次方),于是为了更好记录就有了ASCII(American Standard Code for Information Interchange,美国信息互换标准代码,ASCⅡ)码,可以看下每个符号对应的ACSCII码。</p><p><img src="http://7xj9f0.com1.z0.glb.clouddn.com/2018-03-27_21-33-06.png" alt><br><img src="http://7xj9f0.com1.z0.glb.clouddn.com/2018-03-27%2021-33-56.png" alt><br>这对于使用英语的人已经满足了人的大部分需求,因为有了26个字母,有了数字和各种控制符,就可以玩转计算机。但是,随着计算机传入我们国家,ACSCII码肯定不够用,怎么办?</p><h1><span id="gb-2312以及gbk">GB 2312以及GBK</span></h1><p>早先的ASCII编码字符集由于受到单字节的限制。随着国内计算机的发展,中国国家标准总局发布了一套《信息交换用汉字编码字符集》的国家标准,其标准号就是<strong>GB 2312</strong>。这个字符集共收入汉字6763个和非汉字图形字符682个,同时,收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682个全角字符。整个字符集分成94个区,每区有94个位。,具体的编码表可以参照<a href="http://tools.jb51.net/table/gb2312" target="_blank" rel="noopener">GB2312简体中文编码表</a>。</p><p><img src="http://7xj9f0.com1.z0.glb.clouddn.com/2018-03-27%2022-00-06.png" alt="第03区"></p><p>可以通过上面的第03区的编码表看到每个区位对应一个字符,因此可用所在的区和位来对汉字进行两字节编码。</p><p>实际上,GB2312收录的只是常用汉字,对于一些生僻字,繁体字,GB2312依然不够,于是又有了后来的GBK字符集以及编码规范,但GBK是兼容GB2312的。</p><p>相信GBK和GB2312也是大最常见的字符集,但是这只是对国内的计算机,对于一些希腊字母以及其他国外的文字,怎么通信呢?这就需要一个国际化的字符集或者标准,于是unicode编码就在这种情况下诞生啦。</p><h1><span id="unicode字符集">Unicode字符集</span></h1><p>在<code>ACSCII码</code>满足不了非英语系的语言后,出现了各种各样的编码格式如简体汉字的GBK字符集,台湾繁体的Big5字符集(大五码),日语的JIS等等各自为战,这对于国际化来说是一个很纠结的问题,打开一个网页若是不知道是什么编码就可能是一篇乱码的火星文。于是有人提出了搞一个国际化的标准Unicode。 Unicode最初我们常见肯定是两个字节的,也就是说16位,如汉字的“李”对应的Unicode就是<code>674E</code>,这样对于大部分的非英语系的文字是没有问题的。但是,对于之前说过的<code>ACSCII码</code>采用两个字节的太浪费.若是<code>ACSCII码</code>若是采用两个字节的话,第一个字节的都是0填充,而低位才是真正有效的,这杨来看,对于经常使用英文字母(包含在<code>ACSCII码</code>)的歪果仁来说,这个Unicode一点都不高效。所以怎么高效传送Unicode呢?也是出现了后来各式各样的编码方式:UTF-8,UTF-16,UTF-32,但是我们常用的可能就是UTF-8,UTF-16。</p><p>那么怎么用utf-16来表示一个字符呢,需要先讲下具体的规则。<br>UTF-16是目前各大操作系统和框架支持比较好的编码方式,可以认为它是双字节的,如上文所述,采用16位存储的方式。</p><p><code>UTF-8</code>则是将一些比较长的字符,按照一定的规则分成若干个八位来存储,同时兼容<code>ACSCII码</code>。<br>对于只需要7位都可以存储的:<br>0XXXXXXX<br>若是超过7位的,则是需要在高位来存储表示来表示整个编码的长度:<br>如110XXXXX 10XXXXXX 两个字节,一个字节是一个单元,其中其中110表示这个编码串的长度是2<br>,10是低位单元的前缀。所以有效字符应该是去掉110和后面的10,<br>1110XXXX 10XXXXXX 10XXXXXX 三个字节,110表示三个字节,后面的两个10是低位单元的前缀。</p><p>如李的UTF-8编码:</p><figure class="highlight sh"><table><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"># 李 16进制</span></span><br><span class="line">E6 9D 8E</span><br><span class="line"><span class="comment"># 李 16进制</span></span><br><span class="line">11100110 10011101 10001110</span><br></pre></td></tr></table></figure><p>当我们把110 10去掉后,发现其实他就是UTF-16对应的<code>674E</code>。UTF-8转UTF-16,其实是就是将每个单元的前缀(110,10)去掉,后不够一个字节的,在第一个字节的高位用0填充。<br>如汉字李的UTF-8编码:</p><ol><li>由于第一个单元的高位含有三个1,可知和紧随其后的两个字节是一个完整的字</li><li>去掉1110 10得到<code>0110 011101 001110,正好16位,得到的结果为:</code>674E`</li></ol><p>所以当我们需要自己编码或者转码时,只需要参照UTF-8的学习方法即可,谨以此文,抛砖引玉。</p>]]></content>
<summary type="html">
<p>ACSCII,GBK,Unicode,UTF-8这些名词都跟编码有关系,究竟是什么关系呢,看网上的确有很多解释,也很详尽,不过我觉得还是很有必要把我自己学习的历程记录下来。</p>
<h1><span id="acscii码">ACSCII码</span></h1><p>在
</summary>
<category term="编程" scheme="http://ownwell.github.io/categories/%E7%BC%96%E7%A8%8B/"/>
<category term="编程" scheme="http://ownwell.github.io/tags/%E7%BC%96%E7%A8%8B/"/>
</entry>
<entry>
<title>数据分析--NBA哪个月的比赛最多</title>
<link href="http://ownwell.github.io/2018/03/25/python-data-analysis-nba-01/"/>
<id>http://ownwell.github.io/2018/03/25/python-data-analysis-nba-01/</id>
<published>2018-03-24T16:00:00.000Z</published>
<updated>2018-04-01T16:11:04.000Z</updated>
<content type="html"><![CDATA[<p>我是个NBA伪球迷,去打篮球很少,但是天天看球赛,什么NBA,CBA甚至连NCAA的八强赛也关注,前几天接触Python的数据分析,发现挺有意思,于是我就自己搞了一个简单的程序来分析—从96-97赛季开始,平均哪个月的NBA比赛最多。</p><a id="more"></a><h3><span id="数据">数据</span></h3><p>我自己喜欢搞爬虫,自己爬了一点数据到数据库,至于怎么爬取,请关注公众号:<strong>爱代码的Cyning</strong>,后续会将爬虫代码程序发出来。<br>我将爬取的数据放到了Mysql的数据库,大概的数据如下:<br><img src="http://7xj9f0.com1.z0.glb.clouddn.com/20180401230409.png" alt></p><h3><span id="数据库连接和数据获取">数据库连接和数据获取</span></h3><ul><li><strong>安装pymysql</strong> </li></ul><p>目前使用的是Python3.5+,那么Python怎么链接Mysql呢?<br>首先需要安装<code>pymysql</code></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip3 install -i http://pypi.douban.com/simple --trusted-host pypi.douban.com pymysql</span><br></pre></td></tr></table></figure><ul><li>获取数据库的数据</li></ul><p>跟其他语言获取数据库方式一样,拿到游标就可以拿到数据:</p><figure class="highlight python"><table><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="keyword">with</span> MySQLdb.connect(host=<span class="string">'localhost'</span>, user=<span class="string">'root'</span>, passwd=<span class="string">'root'</span>, port=<span class="number">3306</span>, db=<span class="string">"nba"</span>) <span class="keyword">as</span> cursor:</span><br><span class="line"> <span class="comment"># 使用cursor()方法获取操作游标</span></span><br><span class="line"> sql = <span class="string">"select * from `match-time`"</span></span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> <span class="comment"># 执行SQL语句</span></span><br><span class="line"> cursor.execute(sql)</span><br><span class="line"> <span class="comment"># 获取所有记录列表</span></span><br><span class="line"> results = cursor.fetchall()</span><br><span class="line"> p = np.array(results)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> p</span><br><span class="line"></span><br><span class="line"> <span class="keyword">except</span>:</span><br><span class="line"> print(<span class="string">"Error: unable to fecth data"</span>)</span><br><span class="line"> <span class="keyword">return</span> np.nan</span><br></pre></td></tr></table></figure><p>通过<code>MySQLdb</code>库来拿到数据库的游标,执行sql语句,就可以通过<code>.fetchall()</code>获取数据库中所有的列表信息。</p><ul><li>Numpy分析</li></ul><p>在Python3.X上开发是个很开心的过程,因为Python平台下丰富的第三方库,为我们提供了各式各样的轮子,我们只需要按照自己的需求组装或者组合。我们可以通过Numpy库来处理目前我们能拿到的数据。</p><p>上面的<code>np.array</code>其实就是将从数据库的二维数据向量化。</p><p>我们需要将1996-1997赛季到2016-2017赛季所有的按照月份给列出来,首先我们需要获取每隔赛季有哪几个月是比赛月(常规赛和季后赛),因为每隔赛季的情况千差万别,如停摆,全明星赛,以及各种因素会导致每隔赛季有所差异。</p><p>那我们如何拿到每隔赛季的月份呢:</p><ol><li>过滤2017-2018赛季(比赛尚未结束)</li><li>按照月份来去重,拿到有比赛的月份(比赛月)</li><li>每一个的比赛求总和</li></ol><p><strong>过滤2017-2018赛季以及求月份</strong></p><figure class="highlight python"><table><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"># 从数据库获取数据</span></span><br><span class="line">data_arr = getAllData()</span><br><span class="line"><span class="comment"># 过滤2017-2018赛季</span></span><br><span class="line">data_arr = data_arr[ (data_arr[:, <span class="number">6</span>] != <span class="string">"2017-2018"</span>)]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 拿到月份</span></span><br><span class="line">month_list = np.unique(data_arr[:, <span class="number">4</span>])</span><br></pre></td></tr></table></figure><p>输出结果</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[<span class="string">'1'</span> <span class="string">'10'</span> <span class="string">'11'</span> <span class="string">'12'</span> <span class="string">'2'</span> <span class="string">'3'</span> <span class="string">'4'</span> <span class="string">'5'</span> <span class="string">'6'</span>]</span><br></pre></td></tr></table></figure><p>跟实际情况是一致的。</p><p><strong>求每个月份比赛数</strong></p><p>只需将刚才的比赛月一一遍历,求和即可:</p><figure class="highlight python"><table><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">month_list_result = []</span><br><span class="line"><span class="keyword">for</span> month <span class="keyword">in</span> month_list:</span><br><span class="line"> <span class="comment"># 和month相同的月份的向量</span></span><br><span class="line"> month_list_data = data_arr[data_arr[:,<span class="number">4</span>] == month]</span><br><span class="line"> <span class="comment"># 求和,Y轴</span></span><br><span class="line"> match_cnt = np.sum(month_list_data[:, <span class="number">1</span>].astype(int), axis=<span class="number">0</span>)</span><br><span class="line"> month_list_result.append([month, match_cnt])</span><br></pre></td></tr></table></figure><p>将获取的<code>month_list_result</code>存到csv文件中:</p><figure class="highlight python"><table><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">file_name = os.path.join(<span class="string">"./"</span>, <span class="string">"month_cnt.csv"</span>)</span><br><span class="line">save_stats_to_csv(np.array(month_list_result), file_name, headers=[<span class="string">'month'</span>, <span class="string">"match_cnt"</span>])</span><br></pre></td></tr></table></figure><ul><li>结果</li></ul><p>打开csv文件,用excel的图标展示如下:<br><img src="http://7xj9f0.com1.z0.glb.clouddn.com/201804012343.png" alt></p><p>通过这个图,我们很容易知道:<strong>NBA 3月份的比赛最多</strong></p>]]></content>
<summary type="html">
<p>我是个NBA伪球迷,去打篮球很少,但是天天看球赛,什么NBA,CBA甚至连NCAA的八强赛也关注,前几天接触Python的数据分析,发现挺有意思,于是我就自己搞了一个简单的程序来分析—从96-97赛季开始,平均哪个月的NBA比赛最多。</p>
</summary>
<category term="数据分析" scheme="http://ownwell.github.io/categories/%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90/"/>
<category term="Python" scheme="http://ownwell.github.io/tags/Python/"/>
</entry>
<entry>
<title>(译文)手把手教你用Java实现AOP</title>
<link href="http://ownwell.github.io/2018/03/20/aop/"/>
<id>http://ownwell.github.io/2018/03/20/aop/</id>
<published>2018-03-19T16:00:00.000Z</published>
<updated>2018-04-01T14:41:17.000Z</updated>
<content type="html"><![CDATA[<h3><span id="介绍">介绍</span></h3><p>众所周知,AOP(面向切面编程)是web框架Spring的特色功能之一。通过设置横切关注点(cross cutting concerns),AOP提供了极高的扩展性。</p><a id="more"></a><p>那AOP在Spring中是怎样运作的呢?当你只能使用core java,却需要AOP技术时,这个问题的解答变得极为关键。不仅如此,在高级技术岗位的面试中,此类问题也常作为考题出现。这不,我的朋友最近参加了一个面试,就被问到了这样一个棘手的问题——如何在不使用Spring及相关库,只用core Java的条件下实现AOP。因此,我将在本文中提供一份大纲,帮助大家了解如何只用core Java实现一个AOP(当然啦,这种AOP在功能上有一定的局限性)。注意,本文不是一篇有关Spring AOP与Java AOP的对比研究,而是有关在core Java中借助固有的设计模式 。</p><p>想必大家已经知道AOP是什么,也知道在Spring框架中如何使用它,因此本文只着眼于如何在不用Spring的前提下实现AOP。首先,我们得知道,Spring是借助了JDK proxy和CGlib两种技术实现AOP的。JDK dynamic proxy提供了一种灵活的方式来hook一个方法并执行指定的操作,但执行操作时得有一个限制条件:必须先提供一个相关的接口以及该接口的实现类。实践出真知,让我们透过一个案例来理解这句吧!现在有一个计算器程序,用于完成一些数学运算。让我们来考虑下除法功能,此时的问题是:如果core framework 已经具备了一份实现除法的代码,我们能否在代码执行时劫持(highjack)它并执行额外的校验呢?答案是肯定的,我将用下面提供的代码片段来证明这点。首先来看基础接口的代码:</p><figure class="highlight java"><table><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">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Calculator</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">calculate</span><span class="params">( <span class="keyword">int</span> a , <span class="keyword">int</span> b)</span></span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>该接口实现类的代码如下:</p><figure class="highlight java"><table><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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CalculatorImpl</span> <span class="keyword">implements</span> <span class="title">Calculator</span> </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">calculate</span><span class="params">(<span class="keyword">int</span> a, <span class="keyword">int</span> b)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> a/b;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>假设我们既不能修该上面的代码,也不能对核心库进行任何改动,怎样才能完美地实现校验功能呢?不如试下JDK dynamic proxy的功能吧。</p><figure class="highlight java"><table><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">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SomeHandler</span> <span class="keyword">implements</span> <span class="title">InvocationHandler</span> </span>{</span><br><span class="line"></span><br><span class="line"><span class="comment">// Code omitted for simplicity…..</span></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> Object <span class="title">invoke</span><span class="params">(Object proxy, Method method, Object[] params)</span> <span class="keyword">throws</span> Throwable </span>{</span><br><span class="line"><span class="comment">// Your complex business validation and logic</span></span><br><span class="line"> Object result = method.invoke(targetObject ,params);</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>让我们通过测试类来看看由JDK dynamic proxy实现的校验功能的效果如何。</p><figure class="highlight java"><table><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="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> CalculatorImpl calcImpl = <span class="keyword">new</span> CalculatorImpl();</span><br><span class="line"> Calculator proxied = (Calculator)ProxyFactory.getProxy (Calculator.class, calcImpl, </span><br><span class="line"> <span class="keyword">new</span> SomeHandler(calcImpl));</span><br><span class="line"> <span class="keyword">int</span> result = proxied.calculate(<span class="number">20</span>, <span class="number">10</span>);</span><br><span class="line"> System.out.println(<span class="string">"FInal Result :::"</span> + result);</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>从结果可以看出,简单地实现功能强大的<strong>InvocationHandler</strong>接口,我们便能得到一个hooking implementation。按照JDK文档的描述,<strong>InvocationHandler</strong>接口是借助一个代理实例(proxy instance)来处理一个方法调用的。</p><p>现在我们已经知道,InvocationHandler的invoke()方法能够帮助我们解决问题。那么再来解决一个新问题——怎样才能在方法执行的前后执行操作呢?说的更具体一些,我们能通过添加多个aop(before、after、around)来hook一个方法吗(译注:原文为add multiple aops,但我认为Handler是充当Aspect的角色)?答案同样是肯定的。按照以下的步骤建立一个精简的代码模板便能满足这样的需求:</p><ol start="2"><li>创建一个抽象类,用于将aop应用于目标对象上。</li><li>创建名为BeforeHandler 和 AfterHandler的两个aop。前者在方法执行之前工作,而后者则在方法执行结束后工作。</li><li>创建一个代理类,使所有的aop handler和目标对象只需作为参数传入,就能创建一个hook。</li><li>加入你自己的业务逻辑或者横切关注点。</li><li>最后,通过传入相关的参数创建代理对象(proxy object)。</li></ol><h3><span id="技术实现概要">技术实现概要</span></h3><p>(译注:此处是核心代码片段,如果想运行该实例,需进入下方提供的链接下载完整代码)</p><p>创建一个handler的抽象类:</p><figure class="highlight java"><table><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">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">AbstractHandler</span> <span class="keyword">implements</span> <span class="title">InvocationHandler</span> </span>{</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> Object targetObject;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setTargetObject</span><span class="params">(Object targetObject)</span> </span>{</span><br><span class="line"><span class="keyword">this</span>.targetObject = targetObject;</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>创建名为<strong>BeforeHandler</strong>和<strong>AfterHandler</strong>的两个易扩展的handler抽象类:</p><figure class="highlight java"><table><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">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">BeforeHandler</span> <span class="keyword">extends</span> <span class="title">AbstractHandler</span> </span>{</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">handleBefore</span><span class="params">(Object proxy, Method method, Object[] args)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">public</span> Object <span class="title">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span> <span class="keyword">throws</span> Throwable </span>{</span><br><span class="line">handleBefore(proxy, method, args);</span><br><span class="line"><span class="keyword">return</span> method.invoke(getTargetObject(), args);</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><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">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">AfterHandler</span> <span class="keyword">extends</span> <span class="title">AbstractHandler</span> </span>{</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">handleAfter</span><span class="params">(Object proxy, Method method, Object[] args)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">public</span> Object <span class="title">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span> <span class="keyword">throws</span> Throwable </span>{</span><br><span class="line">Object result = method.invoke(getTargetObject(), args);</span><br><span class="line">handleAfter(proxy, method, args);</span><br><span class="line"><span class="keyword">return</span> result;</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>创建Proxy的工厂类:</p><figure class="highlight java"><table><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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ProxyFactory</span> </span>{</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Object <span class="title">getProxy</span><span class="params">(Object targetObject,</span></span></span><br><span class="line"><span class="function"><span class="params">List handlers)</span> </span>{</span><br><span class="line"><span class="comment">//Code to get the proxy</span></span><br><span class="line"><span class="keyword">return</span> proxyObject;</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line"><span class="keyword">return</span> targetObject;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>以下为测试代码:</p><figure class="highlight java"><table><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">CalculatorImpl calcImpl = <span class="keyword">new</span> CalculatorImpl();</span><br><span class="line">BeforeHandler before = <span class="keyword">new</span> BeforeHandlerImpl();</span><br><span class="line">AfterHandler after = <span class="keyword">new</span> AfterHandlerImpl();</span><br><span class="line">List<AbstractHandler> handlers = <span class="keyword">new</span> ArrayList<AbstractHandler>();</span><br><span class="line">handlers.add(before);</span><br><span class="line">handlers.add(after);</span><br><span class="line">Calculator proxy = (Calculator) ProxyFactory.getProxy(calcImpl,</span><br><span class="line">handlers);</span><br><span class="line"><span class="keyword">int</span> result = proxy.calculate(<span class="number">20</span>, <span class="number">10</span>);</span><br></pre></td></tr></table></figure><h3><span id="配置">配置</span></h3><p>以上的代码片段简明扼要地解释了AOP在结构上的实现(structural implementation)。当然,如果能通过实际的测试将其运用到现实中去,那就再好不过了。读者可在下面的链接中获取完整的工程文件,并在Java编辑器中配置它们,最后通过其中的测试类来检验效果。</p><ul><li><a href="https://github.com/debjava/aopusingjdkdynamicproxy" target="_blank" rel="noopener">https://github.com/debjava/aopusingjdkdynamicproxy</a></li></ul><h3><span id="总结">总结</span></h3><p>希望这篇简短的有关AOP文章能够帮助到大家。需说明的是,本文只实现了before和after两种aop,而另外两种,即“Around”和“Throw”,则希望读者自行完成。</p><hr><p>原文:<a href="https://www.javacodegeeks.com/2015/03/create-your-own-aop-in-java.html" target="_blank" rel="noopener">Create your own AOP in Java</a></p>]]></content>
<summary type="html">
<h3 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h3><p>众所周知,AOP(面向切面编程)是web框架Spring的特色功能之一。通过设置横切关注点(cross cutting concerns),AOP提供了极高的扩展性。</p>
</summary>
<category term="Java" scheme="http://ownwell.github.io/categories/Java/"/>
<category term="译文" scheme="http://ownwell.github.io/tags/%E8%AF%91%E6%96%87/"/>
</entry>
<entry>
<title>Center OS下安装常用软件</title>
<link href="http://ownwell.github.io/2018/03/19/server-install-apps/"/>
<id>http://ownwell.github.io/2018/03/19/server-install-apps/</id>
<published>2018-03-19T13:39:28.000Z</published>
<updated>2018-03-19T15:07:33.000Z</updated>
<content type="html"><![CDATA[<p>在腾讯云上买了个服务器,3年多,这也是为自己学习的投资。拿到新机器,我们需要搭常见的环境,如java,python等,中间一顿折腾,后来居然还重置了机器。今天就把周末自己折腾的结果整理下,防止有人进坑。</p><h1><span id="安装python环境">安装python环境</span></h1><p>python不必多说,是各种语言(Java,Oc,sh脚本)的粘合剂,提供丰富的库,语言简洁,人生苦短,我用Python。</p><p>安装Python可以去下载Python3.x(不要再用Python2.7了!!),也可以通过<a href="https://www.anaconda.com/download/" target="_blank" rel="noopener">Anaconda</a>安装。</p><p>我推荐使用<a href="https://www.anaconda.com/download/" target="_blank" rel="noopener">Anaconda</a>安装,能省去很多体力活。下面就开始安装Anaconda:</p><p><img src="http://7xj9f0.com1.z0.glb.clouddn.com/MacHi%202018-03-19%2022-30-36.png" alt><br>DownLoad实际上是个sh文件,我们通过wget来下载,并通过sh来执行这个安装文件:</p><figure class="highlight sh"><table><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">wget https://repo.continuum.io/archive/Anaconda3-5.1.0-Linux-x86_64.sh</span><br><span class="line"></span><br><span class="line">sh Anaconda3-5.1.0-Linux-x86_64.sh</span><br></pre></td></tr></table></figure><p>然后执行这个脚本,就进入安装步骤。</p><p>只要安装完Anaconda,pyhton就安装成功了,对了和数据分析的,Numpy等工具库都已经安装成功。</p><h1><span id="安装java8">安装java8</span></h1><p>Java是我我接触时间比较久的语言,所以必须安装它,首选Java8.</p><ol><li><p>下载:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" "http://download.oracle.com/otn-pub/java/jdk/8u162-b12/0da788060d494f5095bf8624735fa2f1/jdk-8u162-linux-x64.rpm”</span><br></pre></td></tr></table></figure></li><li><p>yum 安装</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yum localinstall jdk-<span class="number">8</span>u162-linux-x64.rpm -y</span><br></pre></td></tr></table></figure></li><li><p>配置<br>这样java就安装到<code>/usr/java/jdk1.8.0_162</code>.在自己的shell的配置文件(如bash的.bashrc)配置:</p><figure class="highlight java"><table><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">export JAVA_HOME=/usr/java/jdk1.8.0_162</span><br><span class="line">export JRE_HOME=/usr/java/jdk1.8.0_162/jre</span><br><span class="line">export PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin</span><br></pre></td></tr></table></figure></li></ol><p>最后通过<code>java -version</code>验证下。</p><h1><span id="mysql的安装和配置">mysql的安装和配置</span></h1><p>安装mysql走了点弯路,不过没关系,后面居然都是很顺利的。</p><h2><span id="安装">安装</span></h2><p>之前我用<code>yum install mysql</code> 是没问题的,但是安装<code>mysql-server</code> 时提示没找到,于是就搜了下,还需要添加yum源.</p><p><img src="http://7xj9f0.com1.z0.glb.clouddn.com/2018-03-19-01.png" alt></p><figure class="highlight java"><table><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">wget <span class="string">'https://dev.mysql.com/get/mysql57-community-release-el7-11.noarch.rpm'</span></span><br><span class="line"></span><br><span class="line">sudo rpm -Uvh mysql57-community-release-el7-<span class="number">11</span>.noarch.rpm</span><br><span class="line"></span><br><span class="line">yum repolist all | grep mysql</span><br></pre></td></tr></table></figure><p><img src="http://7xj9f0.com1.z0.glb.clouddn.com/2018-03-19_02.png-blog" alt></p><p>就找到5.7的,开始安装:</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo yum install mysql-community-server</span><br></pre></td></tr></table></figure><h1><span id="开启服务">开启服务</span></h1><p>开启mysql的服务:</p><figure class="highlight sh"><table><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">sudo service mysqld start </span><br><span class="line">sudo systemctl start mysqld</span><br><span class="line">sudo systemctl status mysqld</span><br></pre></td></tr></table></figure><p><img src="http://7xj9f0.com1.z0.glb.clouddn.com/MacHi%202018-03-19_03.png-blog" alt></p><h2><span id="设置密码">设置密码</span></h2><p>这个密码让我纠结很久,因为我想登录mysql,但是发现我没有密码!!</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql -uroot -p <span class="comment">#输入mysql密码</span></span><br></pre></td></tr></table></figure><p>后来记得我之前在mac安装时会有个临时密码弹窗,感觉在linux也类似。上网查了下果然如此,只需要找到这个临时密码,再重设置就OK。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo grep <span class="string">'temporary password'</span> /var/<span class="built_in">log</span>/mysqld.lo查看到的密码</span><br></pre></td></tr></table></figure><p><img src="http://7xj9f0.com1.z0.glb.clouddn.com/MacHi%202018-03-19_04.png" alt></p><p>再通过密码登录mysql数据库,设置密码:</p><figure class="highlight sh"><table><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">mysql -uroot -p <span class="comment">#输入mysql临时密码</span></span><br><span class="line"></span><br><span class="line">ALTER USER <span class="string">'root'</span>@<span class="string">'localhost'</span> IDENTIFIED BY <span class="string">'root1244'</span>; <span class="comment"># 登录mysql设置新密码root1244</span></span><br></pre></td></tr></table></figure><p>退出mysql后,就可以快乐使用了。</p><blockquote><p>5.6之后有个安全设置,可以重置密码,修改数据库是否可以被远程访问等<code>mysql_secure_installation</code></p></blockquote>]]></content>
<summary type="html">
<p>在腾讯云上买了个服务器,3年多,这也是为自己学习的投资。拿到新机器,我们需要搭常见的环境,如java,python等,中间一顿折腾,后来居然还重置了机器。今天就把周末自己折腾的结果整理下,防止有人进坑。</p>
<h1><span id="安装python环境">安装pyt
</summary>
<category term="编程" scheme="http://ownwell.github.io/categories/%E7%BC%96%E7%A8%8B/"/>
<category term="服务器" scheme="http://ownwell.github.io/tags/%E6%9C%8D%E5%8A%A1%E5%99%A8/"/>
</entry>
<entry>
<title>位运算妙用</title>
<link href="http://ownwell.github.io/2018/03/12/%E4%BD%8D%E8%BF%90%E7%AE%97%E5%A6%99%E7%94%A8/"/>
<id>http://ownwell.github.io/2018/03/12/位运算妙用/</id>
<published>2018-03-12T15:14:21.000Z</published>
<updated>2019-07-06T16:21:34.585Z</updated>
<content type="html"><![CDATA[<p>位运算妙用<br>位运算作为最基本的计算机操作运算符,在很多时候用到绝对是很好的技巧。</p><h1><span id="异或x-or">异或(x or)</span></h1><p>异或运算就是一个神器,异或有个最基本的原理就是自己异或等于0。</p><p>如result3 ^ 3 结果为0<br><a href="https://leetcode-cn.com/problems/single-number/" target="_blank" rel="noopener">136. 只出现一次的数字</a></p><p>可以解决数字只出现一次的情况,因为若是两次肯定为0,只出现一次,就很好解决;</p><figure class="highlight java"><table><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="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">singleNumber</span><span class="params">(<span class="keyword">int</span>[] nums)</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> num = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < nums.length; i++) {</span><br><span class="line"> num = num ^ nums[i];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> num;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p>位运算妙用<br>位运算作为最基本的计算机操作运算符,在很多时候用到绝对是很好的技巧。</p>
<h1><span id="异或x-or">异或(x or)</span></h1><p>异或运算就是一个神器,异或有个最基本的原理就是自己异或等于0。</p>
<p>如resu
</summary>
<category term="算法" scheme="http://ownwell.github.io/categories/%E7%AE%97%E6%B3%95/"/>
<category term="算法" scheme="http://ownwell.github.io/tags/%E7%AE%97%E6%B3%95/"/>
</entry>
<entry>
<title>top K的解题思路</title>
<link href="http://ownwell.github.io/2018/03/11/top-K%E7%9A%84%E8%A7%A3%E9%A2%98%E6%80%9D%E8%B7%AF/"/>
<id>http://ownwell.github.io/2018/03/11/top-K的解题思路/</id>
<published>2018-03-11T14:20:37.000Z</published>
<updated>2019-07-22T11:38:11.618Z</updated>
<content type="html"><![CDATA[<p>很早记得,出去面试,经常有人问:最大值怎么求,对一个经过变成训练的人,或许都会解这个题目,因为最大值是我们程序员的一个最最基本的素养。其实还可以拓展就最K大的值,也就是我们常说的<code>top K</code>问题,最大其实就是top 1大,还有第二大,第三大的问题吧。</p><a id="more"></a><p>其实对于K的问题,最简单的方式,就是先排序,排完序,孰大孰小,一目了然,这个是最基础的思路,不过本文会从其他更高效的路径出发,来解题。</p><h2><span id="最值求法">最值求法</span></h2><p>关于最大值求法,直接附伪代码:</p><figure class="highlight plain"><table><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">int max(arr) </span><br><span class="line"> if(arr == null) </span><br><span class="line"> throw expection</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> max = arr[0]</span><br><span class="line"></span><br><span class="line"> for(i = 1; i<arr.length ; i ++) </span><br><span class="line"> if(arr[i] > max)</span><br><span class="line"> max = arr[i]</span><br></pre></td></tr></table></figure><p>这个就是将最大值默认设置未第一个值,后面就和这个最大值比较,比这个最大值大的就直接替换掉,再和后面的值比较,直到最后,得到的最大值肯定是整个数组中最大的一个。</p><h2><span id="冒泡">冒泡</span></h2><p>冒泡排序其实就是这个思路,最大值(或者最小值)肯定在最后,次大在最大值(或者最小值)前面。</p><p>如对<code>76,18,99,35, 12</code> 进行排序:<br><img src="https://wiki.jikexueyuan.com/project/easy-learn-algorithm/images/2.1.png" alt></p><p>冒泡一次,可将最小值求出,以此类推,第一次,需要<code>76,18,99,35</code>排序,次小值<code>18</code>就被冒泡出。</p><p>所以冒泡是可以解决<code>top K</code>的问题的,如求第二大的值,只需要进行K次冒泡即可:</p><figure class="highlight java"><table><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// k表示第k大 k>=1</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">top_k_max</span><span class="params">(arr, k)</span> </span></span><br><span class="line"><span class="function"> <span class="title">if</span><span class="params">(arr == <span class="keyword">null</span>)</span> </span></span><br><span class="line"><span class="function"> throw expection</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"> </span></span><br><span class="line"><span class="function"> <span class="title">for</span><span class="params">(j = <span class="number">0</span>; j < k; j++)</span></span></span><br><span class="line"><span class="function"> <span class="title">for</span><span class="params">(i = <span class="number">0</span>; i < arr.length -j ; i++)</span></span></span><br><span class="line"><span class="function"> max </span>= arr[<span class="number">0</span>]</span><br><span class="line"> <span class="keyword">if</span>(arr[i] > arr[i+<span class="number">1</span>]):</span><br><span class="line"> exchange arr[i] with max <span class="comment">// 相互交换</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> arr[arr.length -k]</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">快排,绝对是高频词出现的排序算法,像java默认的数组排序就是排序的变种,那么top k咋还能跟快排有关系呢?我们就需要从快排的基本实现说起:</span><br><span class="line"></span><br><span class="line">```java</span><br><span class="line"></span><br><span class="line"> Quick-sort(Arr, p, r)</span><br><span class="line"> <span class="comment">// 分层两部分</span></span><br><span class="line"> q = Partition(Arr, p, r)</span><br><span class="line"> <span class="comment">// 比目标值大的</span></span><br><span class="line"> Quick-sort(A, p, q-<span class="number">1</span>)</span><br><span class="line"> Quick-sort(A, q+<span class="number">1</span>, r)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> Partition(Arr, p, r)</span><br><span class="line"> <span class="comment">// 将最右边的作为目标值</span></span><br><span class="line"> x = Arr[r]</span><br><span class="line"></span><br><span class="line"> i = p-<span class="number">1</span></span><br><span class="line"> <span class="keyword">for</span>(j = p to r-<span class="number">1</span>)</span><br><span class="line"> <span class="keyword">if</span>(A[j] <= x)</span><br><span class="line"> i = i +<span class="number">1</span></span><br><span class="line"> exchange A[i] with A[j]</span><br><span class="line"> exchange A[i+<span class="number">1</span>] with x</span><br></pre></td></tr></table></figure><p>我们假设是一个数据<code>2,8,7,1,3.5,6,4</code></p><p>可以知道,第一轮排序</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">2 1 3 4 7 5 6 8</span><br></pre></td></tr></table></figure><p>作为参考物的4 放到第4位,所以<code>4</code>肯定是第5大(7 5 6 8都比5大)的,因为前面的比它小(或相等),后面比它大(或相等),这么一说你是不是有点思路啦。</p><blockquote><p>你求K大,若是按照从小到大的顺序排序的话,肯定是在<code>A.size - K</code>位置上,需要转化下</p></blockquote><p>其实快速排序将整个长数组,切成两部分,假设它所在的位置M,那他肯定是是第<code>A.size-M</code>大,如果这个<code>A.size-M</code>的值比K大,说明你要找的第k大是在比它大的那部分里面,再进行一次Partition,肯定还会有个中间数,在比较,知道你找到即可。<br>如刚才的<code>2 1 3 4 7 5 6 8</code>若是你求第6大,那么 <code>A.size-M</code> = 8-3 (<code>4</code>说在位置)< 6 ,所以去左边找,若是 <code>A.size-M</code> > k,去右边。</p><figure class="highlight java"><table><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="comment">//若是求k大,从小到大排序应该是A.size-k </span></span><br><span class="line">Qsort-top-k(Arr, p, r, k)</span><br><span class="line"> <span class="comment">// 分层两部分</span></span><br><span class="line"> q = Partition(Arr, p, r)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(A.size-k < q) </span><br><span class="line"> <span class="comment">// 左边</span></span><br><span class="line"> <span class="keyword">return</span> Qsort-top-k(Arr, p, q-<span class="number">1</span>, k)</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span>(A.size-k > q) </span><br><span class="line"> <span class="keyword">return</span> Qsort-top-k(Arr, q+<span class="number">1</span>, r, k)</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="keyword">return</span> Arr[q];</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p>很早记得,出去面试,经常有人问:最大值怎么求,对一个经过变成训练的人,或许都会解这个题目,因为最大值是我们程序员的一个最最基本的素养。其实还可以拓展就最K大的值,也就是我们常说的<code>top K</code>问题,最大其实就是top 1大,还有第二大,第三大的问题吧。</p>
</summary>
<category term="算法" scheme="http://ownwell.github.io/categories/%E7%AE%97%E6%B3%95/"/>
<category term="算法" scheme="http://ownwell.github.io/tags/%E7%AE%97%E6%B3%95/"/>
</entry>
</feed>