-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
1692 lines (1465 loc) · 230 KB
/
atom.xml
File metadata and controls
1692 lines (1465 loc) · 230 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>JrDong’s Blog</title>
<link href="/atom.xml" rel="self"/>
<link href="http://ibat.xyz/"/>
<updated>2019-01-18T03:00:00.849Z</updated>
<id>http://ibat.xyz/</id>
<author>
<name>JrDong</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>重构-在对象之间搬移特性</title>
<link href="http://ibat.xyz/2019/01/18/%E9%87%8D%E6%9E%84-%E5%9C%A8%E5%AF%B9%E8%B1%A1%E4%B9%8B%E9%97%B4%E6%90%AC%E7%A7%BB%E7%89%B9%E6%80%A7/"/>
<id>http://ibat.xyz/2019/01/18/重构-在对象之间搬移特性/</id>
<published>2019-01-18T02:52:15.000Z</published>
<updated>2019-01-18T03:00:00.849Z</updated>
<content type="html"><![CDATA[<h1 id="重构-在对象之间搬移特性"><a href="#重构-在对象之间搬移特性" class="headerlink" title="重构-在对象之间搬移特性"></a>重构-在对象之间搬移特性</h1><p>《重构:改善既有代码的设计》一书学习笔记。</p>
<h2 id="原则"><a href="#原则" class="headerlink" title="原则"></a>原则</h2><ol>
<li>不要让一个类承担太多责任</li>
<li>迪米特法则:一个对象应该对其他对象保持最少的了解,类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。 </li>
<li>能复用的代码尽量抽取,引入外加函数或引入本地扩展</li>
</ol>
<h2 id="方法"><a href="#方法" class="headerlink" title="方法"></a>方法</h2><h3 id="Move-Method(搬移函数)"><a href="#Move-Method(搬移函数)" class="headerlink" title="Move Method(搬移函数)"></a>Move Method(搬移函数)</h3><p>A类中有个函数a,并且含有一个类引用B,如果a函数的逻辑与B相关比较大,则可以考虑将a函数搬移到B中。</p>
<h3 id="Move-Field(搬移字段)"><a href="#Move-Field(搬移字段)" class="headerlink" title="Move Field(搬移字段)"></a>Move Field(搬移字段)</h3><p>对于一个字段,在其所驻类之外的另一个类中有更多的函数使用它,则考虑搬移该字段。或者搬移另一个类的函数,这取决于实际情况。</p>
<h3 id="Extract-Class(提炼类)"><a href="#Extract-Class(提炼类)" class="headerlink" title="Extract Class(提炼类)"></a>Extract Class(提炼类)</h3><p>一个类应该是一个清楚的抽象,处理一些明确的责任。但是在实际开发中,随着业务越来越大,类会不断扩展。当给某个类加一个新的责任时,你会觉得这点责任不足以为它新增一个类,于是随着责任不断增加,这个类就会变得过分复杂。</p>
<h4 id="做法"><a href="#做法" class="headerlink" title="做法"></a>做法</h4><ul>
<li>决定如果分解类的责任</li>
<li>建立一个新类,用以承载旧类的某些责任。</li>
<li>建立从旧类访问新类的连接关系,有可能是双向连接,但是尽量不要建立新类到旧类的连接。</li>
</ul>
<h4 id="范例"><a href="#范例" class="headerlink" title="范例"></a>范例</h4><pre><code>class Person...
public String getName() {
return _name;
}
public String getTelephoneNumber() {
return ("(" + _officeAreaCode + ") " + _officeNumber);
}
String getOfficeAreaCode() {
return _officeAreaCode;
}
void setOfficeAreaCode(String arg) {
_officeAreaCode = arg;
}
String getOfficeNumber() {
return _officeNumber;
}
void setOfficeNumber(String arg) {
_officeNumber = arg;
}
private String _name;
private String _officeAreaCode;
private String _officeNumber;
</code></pre><p>可以将电话号码相关的内容抽取出来,变为TelephoneNumber类 如下:</p>
<pre><code>class Person...
private String _name;
private TelephoneNumber _officeTelephone = new TelephoneNumber();
public String getName() {
return _name;
}
public String getTelephoneNumber(){
return _officeTelephone.getTelephoneNumber();
}
TelephoneNumber getOfficeTelephone() {
return _officeTelephone;
}
class TelephoneNumber...
private String _number;
private String _areaCode;
public String getTelephoneNumber() {
return ("(" + _areaCode + ") " + _number);
}
String getAreaCode() {
return _areaCode;
}
void setAreaCode(String arg) {
_areaCode = arg;
}
String getNumber() {
return _number;
}
void setNumber(String arg) {
_number = arg;
}
</code></pre><p>这样做后TelephoneNumber便成了Person的一个属性,但是还需要思考一个问题,要不要把TelephoneNumber暴露出去,如果暴露出去,那么其他类直接改TelephoneNumber的内容,Pserson是不知情的,这就会带来一些问题。所以非必要情况下,可以只暴露某些方法,或者只暴露给部分用户,也就是protect或者default作用域下的</p>
<h3 id="Inline-Class(将类内联化)"><a href="#Inline-Class(将类内联化)" class="headerlink" title="Inline Class(将类内联化)"></a>Inline Class(将类内联化)</h3><p>你的某个class没有做太多事情(没有承担足够责任)。<br>将class的所有特性搬移到另一个class中,然后移除原class。<br>与提炼类正好相反,如果一个class不再承担足够 责任、不再有单独存在的理由〔这通常是因为此前的重构动作移走了这个class的 责任),这时候找出与这个class关联最频繁的class,将两个class合并成一个。与上述相同,不做赘述了。 </p>
<h3 id="Hide-Delegate(隐藏「委托关系」)"><a href="#Hide-Delegate(隐藏「委托关系」)" class="headerlink" title="Hide Delegate(隐藏「委托关系」)"></a>Hide Delegate(隐藏「委托关系」)</h3><p>「封装」意味每个对象都应该尽可能少了解系统的其他部分。如此一来,一旦发生变化,需要了解这一 变化的对象就会比较少——这会使变化比较容易进行。<br>这个原则有点像迪米特法则,即一个对象应该对其他对象保持最少的了解。<br>我们从一个例子来理解</p>
<pre><code>class Person {
Department _department;
public Department getDepartment() {
return _department;
}
public void setDepartment(Department arg) {
_department = arg;
}
}
class Department {
private String _chargeCode;
private Person _manager;
public Department (Person manager) {
_manager = manager;
}
public Person getManager() {
return _manager;
}
...
</code></pre><p>如果客户希望知道某人的经理是谁,他必须先取得Department对象: </p>
<pre><code>manager = john.getDepartment().getManager();
</code></pre><p>这样的编码就是对客户揭露了Department的工作原理,于是客户知道:Department用以追踪「经理」这条信息。如果对客户隐藏Department,可以减少耦合(coupling)。 为了这一目的,我在Person中建立一个简单的委托函数: </p>
<pre><code>public Person getManager() {
return _department.getManager();
}
</code></pre><p>现在,我得修改Person的所有客户,让它们改用新函数:</p>
<pre><code>manager = john.getManager();
</code></pre><p>只要完成了对Department所有函数的委托关系,并相应修改了Person的所有客 户,我就可以移除Person中的访问函数getDepartment()了。</p>
<h3 id="Remove-Middle-Man(移除中间人)"><a href="#Remove-Middle-Man(移除中间人)" class="headerlink" title="Remove Middle Man(移除中间人)"></a>Remove Middle Man(移除中间人)</h3><p>某个class做了过多的简单委托动作(simple delegation)。<br>让客户直接调用delegate(受托类)。<br>这个与上面的隐藏委托关系正好相反。这个很好理解,如果一个类委托了另一个类的所有方法,那么就没必要设置委托了。 </p>
<h3 id="Introduce-Foreign-Method(引入外加函数)"><a href="#Introduce-Foreign-Method(引入外加函数)" class="headerlink" title="Introduce Foreign Method(引入外加函数)"></a>Introduce Foreign Method(引入外加函数)</h3><p>你正在使用一个class,它真的很好,为你提供了你想要的所有服务。而后,你又需要一项新服务,这个class却无法供应。于是你开始咒骂:「为什么不能做这件事?」如果可以修改源码,你便可以自行添加一个新函数; 如果不能,你就得在客户端编码,补足你要的那个函数。<br>如果client class只使用这项功能一次,那么额外编码工作没什么大不了,甚至可能根本不需要原本提供服务的那个class。然而如果你需要多次使用这个函数,你就得不断重复这些代码,这些重复性代码应该被抽出来放进同一个函数中。如果你发现自己为一个server class建立了大量外加函数,或如果你发现有许多classes都需要同样的外加函数,你就不应该再使用本项重构,而应该使用 Introduce Local Extension。 </p>
<pre><code>Date newStart = new Date (previousEnd.getYear(),previousEnd.getMonth(), previousEnd.getDate() + 1);
</code></pre><p>为了避免每次像上面这样写,可以引入外加函数 </p>
<pre><code> Date newStart = nextDay(previousEnd);
private static Date nextDay(Date arg) {
return new Date (arg.getYear(),arg.getMonth(), arg.getDate() + 1);
}
</code></pre><h3 id="Introduce-Local-Extension(引入本地扩展)"><a href="#Introduce-Local-Extension(引入本地扩展)" class="headerlink" title="Introduce Local Extension(引入本地扩展)"></a>Introduce Local Extension(引入本地扩展)</h3><p>同样的,当一个类中,没有你需要的函数时,但是这个函数需要被大量的classes使用,这时候就需要使用本方法。如果这个类可以修改其源码,那么可以直接在源码中修改,但是大部分情况应该是不可以修改的。这时候就可以建立一个新class,使它包含这些额外函数。可以成为source class的subclass (子类〕或wrapper(外覆类) </p>
<h4 id="使用subclass"><a href="#使用subclass" class="headerlink" title="使用subclass"></a>使用subclass</h4><p>首先,我要建立一个新的DateSub class来表示「日期」,并使其成为Date的subclass: </p>
<pre><code>class DateSub extends Date{
Date nextDay() {
return new Date (getYear(),getMonth(), getDate() + 1);
}
}
</code></pre><p>当需要获得nextDay时,就可调用DateSub中的nextDay();</p>
<h4 id="使用wrapper"><a href="#使用wrapper" class="headerlink" title="使用wrapper"></a>使用wrapper</h4><p>声明一个wrapping class: </p>
<pre><code>class DateWrapper {
private Date _original;
public DateWrapper (String dateString) {
_original = new Date(dateString);
};
}
</code></pre>]]></content>
<summary type="html">
<h1 id="重构-在对象之间搬移特性"><a href="#重构-在对象之间搬移特性" class="headerlink" title="重构-在对象之间搬移特性"></a>重构-在对象之间搬移特性</h1><p>《重构:改善既有代码的设计》一书学习笔记。</p>
<h2
</summary>
<category term="Android" scheme="http://ibat.xyz/categories/Android/"/>
</entry>
<entry>
<title>重构-重新组织函数</title>
<link href="http://ibat.xyz/2019/01/17/%E9%87%8D%E6%9E%84-%E9%87%8D%E6%96%B0%E7%BB%84%E7%BB%87%E5%87%BD%E6%95%B0/"/>
<id>http://ibat.xyz/2019/01/17/重构-重新组织函数/</id>
<published>2019-01-17T02:52:15.000Z</published>
<updated>2019-01-17T02:58:55.788Z</updated>
<content type="html"><![CDATA[<h1 id="重新组织函数"><a href="#重新组织函数" class="headerlink" title="重新组织函数"></a>重新组织函数</h1><p>《重构:改善既有代码的设计》一书学习笔记。<br>并没有完全写出书中所有例子,只摘抄了自己认为比较常用的一些,如果感兴趣可以阅读书籍.</p>
<h2 id="原则"><a href="#原则" class="headerlink" title="原则"></a>原则</h2><ol>
<li>代码的命名和函数应该让程序员看到后就像注释一样,可以不看这段代码的逻辑就知道这段代码是做什么的。</li>
<li>一个函数只做一件事情,如果函数超过一定行数,比如说一屏,那么这段代码一定做了不止一件事情,可以将函数进行拆分。</li>
</ol>
<h2 id="方法"><a href="#方法" class="headerlink" title="方法"></a>方法</h2><h3 id="提取临时变量"><a href="#提取临时变量" class="headerlink" title="提取临时变量"></a>提取临时变量</h3><p>例如:</p>
<pre><code>private double getPrice(){
int basePrice = quantity * itemPrice;
double discontFactor;
if (basePrice > 1000){
discontFactor = 0.85;
} else{
discontFactor = 0.95;
}
return basePrice * discontFactor;
}
</code></pre><p>step1<br>basePrice是一个通用的计算方式,数量*单价,所以为了保证其他函数也可以调用,我们把临时变量basePrice抽取成basePrice(),结果如下:</p>
<pre><code>private double getPrice(){
double discontFactor;
if (basePrice() > 1000){
discontFactor = 0.85;
} else{
discontFactor = 0.95;
}
return basePrice() * discontFactor;
}
private int basePrice(){
return quantity * itemPrice;
}
</code></pre><p>step2<br>变量discontFactor,也可以用类似方法提取 </p>
<pre><code>private double getPrice(){
return basePrice() * discontFactor();
}
private int basePrice(){
return quantity * itemPrice;
}
private double getDiscontFactor(){
if (basePrice() > 1000){
return 0.85;
} else{
return 0.95;
}
}
</code></pre><p>ok,经过上面的提取,我们直接看getPrice方法,可以一眼明确,价格 = 总价 * 折扣,甚至我们根本不用关心总价和折扣是怎么来的,总之我要的只是一个价格.</p>
<h3 id="加入解释性变量"><a href="#加入解释性变量" class="headerlink" title="加入解释性变量"></a>加入解释性变量</h3><p>例如,我们经常可以看到,if判断语句中做了复杂的判断,一般令人难以理解,这时候应该将复杂的判断用一个解释行的变量来代替</p>
<pre><code>if((platform.toUpperCase().indexOf("MAC")))
||(platform.toUpperCase().indexOf("WINDOWS")){
}
</code></pre><p>乍一看很长一段判断,不容易让人一眼看明白,我们就可以加入解释性变量解决</p>
<pre><code>boolean isMacOs = platform.toUpperCase().indexOf("MAC") > -1;
boolean isWindowsOs = platform.toUpperCase().indexOf("WINDOWS");
if(isMacOs || isWindowsOs){
}
</code></pre><p>这样同样的我们不用关心是怎么判断是mac还是windows的,if语句变得清晰明了.</p>
<h3 id="移除对参数的赋值"><a href="#移除对参数的赋值" class="headerlink" title="移除对参数的赋值"></a>移除对参数的赋值</h3><p>如果不必要,不要直接对函数的参数赋值,但有些时候是需要的,个人感觉不是很强制.<br>例1,容易让人误会的赋值,赋值对基本数据类型无效,所以新命名一个变量比较好理解: </p>
<pre><code>public static void main(String[] args){
int x = 5;
triple(x);
print("x = " + x);
}
private static void triple(int x){
x = x * 3;
print("x = " + x);
}
</code></pre><p>例2,对对象赋值</p>
<pre><code>public static void main(String[] args){
Date d1 = new Date("1 Apr 98");
nextDateUpdate(d1);
print("d1 after nextDay" + d1);
Date d2 = new Date("1 Apr 98");
nextDateReplace(d2);
print("d2 after nextDay" + d2);
}
private static void nextDateUpdate(Date date){
date.setDate(date.getDate() + 1);
print("date in nextDay" + date);
}
private static void nextDateReplace(Date date){
date = new Date(date.getYear(), date.getMonth(), date.getDate() + 1);
print("date in nextDay" + date);
}
</code></pre><h3 id="替换算法"><a href="#替换算法" class="headerlink" title="替换算法"></a>替换算法</h3><p>替换更清晰的算法,例如:</p>
<pre><code>private String foundPerson(String[] people){
for (int i = 0 ; i < people.length ; i++){
if("Don".equals(people[i])){
return "Don";
}
if("John".equals(people[i])){
return "John";
}
if("Kent".equals(people[i])){
return "Kent";
}
}
return "";
}
</code></pre><p>看起来似乎也比较清晰,但是试想一下,person变成了10个,甚至更多怎么办,是不是要写10个if判断。所以我们采用另一种方式</p>
<pre><code>private String foundPerson(String[] people){
List candidates = Arrays.asList(new String[]{"Don", "John","Kent"});
for (int i = 0 ; i < people.length ; i++){
if(condidates.contains(people[i])){
return people[i];
}
}
return "";
}
</code></pre><p>这样如果加更多的人名,只需要往条件中添加就可以.</p>
]]></content>
<summary type="html">
<h1 id="重新组织函数"><a href="#重新组织函数" class="headerlink" title="重新组织函数"></a>重新组织函数</h1><p>《重构:改善既有代码的设计》一书学习笔记。<br>并没有完全写出书中所有例子,只摘抄了自己认为比较常用的一些
</summary>
<category term="Android" scheme="http://ibat.xyz/categories/Android/"/>
</entry>
<entry>
<title>Android setContentView与findViewById源码解析</title>
<link href="http://ibat.xyz/2017/07/24/Android-setContentView%E4%B8%8EfindViewById%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/"/>
<id>http://ibat.xyz/2017/07/24/Android-setContentView与findViewById源码解析/</id>
<published>2017-07-24T07:29:28.000Z</published>
<updated>2017-07-24T12:48:19.000Z</updated>
<content type="html"><![CDATA[<p>当我们给Activity设置布局时,都是直接调用setContentView来完成的,但具体Android是怎么把布局加载到window,又是怎么通过findViewById获取view对象的,我们可能并没有太关心,下面就结合源码来分析下这个过程。</p>
<h1 id="Android-setContentView"><a href="#Android-setContentView" class="headerlink" title="Android setContentView"></a>Android setContentView</h1><p>打开Activity的源码发现,setContentView有三个重载方法, </p>
<ol>
<li>public void setContentView(int layoutResID); </li>
<li>public void setContentView(View view); </li>
<li>public void setContentView(View view, ViewGroup.LayoutParams params)<br>我们就来看下最常用的第一个方法: </li>
</ol>
<pre><code class="java"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setContentView</span><span class="params">(<span class="keyword">int</span> layoutResID)</span> </span>{
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
</code></pre>
<p>这个方法调用了,Window类中的setContentView()方法,其他方法也是调用了Window类中的setContentView(),但是Window是一个抽象类,在Activity的attach方法中被初始化,其实是一个PhoneWindow实例,所以这个setContentView方法在PhoneWindow中实现。 </p>
<pre><code class="java"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setContentView</span><span class="params">(<span class="keyword">int</span> layoutResID)</span> </span>{
<span class="comment">// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window</span>
<span class="comment">// decor, when theme attributes and the like are crystalized. Do not check the feature</span>
<span class="comment">// before this happens.</span>
<span class="keyword">if</span> (mContentParent == <span class="keyword">null</span>) {
installDecor();
} <span class="keyword">else</span> <span class="keyword">if</span> (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
<span class="keyword">if</span> (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
<span class="keyword">final</span> Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} <span class="keyword">else</span> {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
<span class="keyword">final</span> Callback cb = getCallback();
<span class="keyword">if</span> (cb != <span class="keyword">null</span> && !isDestroyed()) {
cb.onContentChanged();
}
}
</code></pre>
<p>首先判断mContentParent是否为空,如果为空的话则调用installDecor()方法,其次判断是否设置了FEATURE_CONTENT_TRANSITIONS属性,如果没有的话则移除所有view(从这里我们可以得出setContentView可以调用多次,反正会removeAllViews),然后调用LayoutInflater.inflate(),将我们设置的布局文件添加到mContentParent中。接着获取了一个Callback对象,那这个是在Activity的attach方法中设置的一个回调 </p>
<pre><code class="java">mWindow.setCallback(<span class="keyword">this</span>);
</code></pre>
<p>所以可以得出在Activity中一定有一个onContentChanged回调,我们来看下这个回调 </p>
<pre><code class="java"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onContentChanged</span><span class="params">()</span> </span>{}
</code></pre>
<p> 额,空空如也。但是我们可以在自己的Activity中重写这个回调,用于在setContentView之后做一些事情,比如findViewById,但貌似实际场景也不需要。。。<br> 好了,现在我们回到上面提到的installDecor()方法,好长,我们捡重要的看吧。 </p>
<pre><code class="java"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">installDecor</span><span class="params">()</span> </span>{
<span class="comment">//初始化decorView</span>
<span class="keyword">if</span> (mDecor == <span class="keyword">null</span>) {
mDecor = generateDecor();
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(<span class="keyword">true</span>);
<span class="keyword">if</span> (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != <span class="number">0</span>) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
<span class="comment">//初始化mContentParent</span>
<span class="keyword">if</span> (mContentParent == <span class="keyword">null</span>) {
mContentParent = generateLayout(mDecor);
......
<span class="comment">//设置一堆属性值</span>
}
}
</code></pre>
<p>看下PhoneWindow中的generateDecor()方法 </p>
<pre><code class="java"><span class="function"><span class="keyword">protected</span> DecorView <span class="title">generateDecor</span><span class="params">()</span> </span>{
<span class="keyword">return</span> <span class="keyword">new</span> DecorView(getContext(), -<span class="number">1</span>);
}
</code></pre>
<p>只是单纯的new了一个DecorView实例。这个DecorView是什么鬼。其实它是PhoneWindow的一个内部类,是整个window界面最顶层的view。包含ActionBar,内容块等。好了,现在我们缕一下Window,PhoneWindow,decorView的关系 </p>
<p>1.Window类是一个抽象类,提供了绘制窗口的一组通用API。可以将之理解为一个载体,各种View在这个载体上显示。<br>2.PhoneWindow是Window的一个子类,是Window的具体实现,包含一个内部类DecorView,PhoneWindow是将decorView进行了一定包装,并提供一些方法用于操作窗口。<br>3。DecorView继承自FrameLayout,是窗口的根view。 </p>
<p>好了,接着看mContentParent的初始化,generateLayout(mDecor).这里传入了上一部初始化好的DecorView. 又是一个长方法,我们还是挑出重要的部分。 </p>
<pre><code class="java"><span class="function"><span class="keyword">protected</span> ViewGroup <span class="title">generateLayout</span><span class="params">(DecorView decor)</span> </span>{
<span class="comment">// Apply data from current theme.</span>
TypedArray a = getWindowStyle();
<span class="comment">//......</span>
<span class="comment">//根据定义的style设置一些值,比如是否显示ActionBar,</span>
<span class="comment">// Inflate the window decor.</span>
<span class="keyword">int</span> layoutResource;
<span class="keyword">int</span> features = getLocalFeatures();
<span class="comment">//......</span>
<span class="comment">//根据设定好的features值选择不同的窗口修饰布局文件,</span>
<span class="comment">//得到layoutResource值,系统定义了不同的layout,比如 </span>
<span class="comment">//R.layout.screen_custom_title,R.layout.screen_simple</span>
<span class="comment">//把选中的窗口修饰布局文件添加到DecorView对象里,并且指定contentParent值</span>
View in = mLayoutInflater.inflate(layoutResource, <span class="keyword">null</span>);
decor.addView(in, <span class="keyword">new</span> ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) in;
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
<span class="keyword">if</span> (contentParent == <span class="keyword">null</span>) {
<span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(<span class="string">"Window couldn't find content container view"</span>);
}
<span class="comment">//......</span>
<span class="comment">//继续一堆属性设置,返回contentParent</span>
<span class="keyword">return</span> contentParent;
}
</code></pre>
<p>根据不同的features值,设定layoutResource,最终添加到decorView中,所以我们通过在xml中设置的theme,还有在代码中设置的requestWindowFeature,都是用来设置features值,这也是为什么requestWindowFeature方法必须在setContentView之前的原因。<br>这样看来,如果我们设置我们的Theme为NoTitleBar,最终layoutResource的值为R.layout.screen_simple </p>
<pre><code class="xml"><span class="tag"><<span class="name">LinearLayout</span> <span class="attr">xmlns:android</span>=<span class="string">"http://schemas.android.com/apk/res/android"</span>
<span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span>
<span class="attr">android:layout_height</span>=<span class="string">"match_parent"</span>
<span class="attr">android:fitsSystemWindows</span>=<span class="string">"true"</span>
<span class="attr">android:orientation</span>=<span class="string">"vertical"</span>></span>
<span class="tag"><<span class="name">ViewStub</span> <span class="attr">android:id</span>=<span class="string">"@+id/action_mode_bar_stub"</span>
<span class="attr">android:inflatedId</span>=<span class="string">"@+id/action_mode_bar"</span>
<span class="attr">android:layout</span>=<span class="string">"@layout/action_mode_bar"</span>
<span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span>
<span class="attr">android:layout_height</span>=<span class="string">"wrap_content"</span>
<span class="attr">android:theme</span>=<span class="string">"?attr/actionBarTheme"</span> /></span>
<span class="tag"><<span class="name">FrameLayout</span>
<span class="attr">android:id</span>=<span class="string">"@android:id/content"</span>
<span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span>
<span class="attr">android:layout_height</span>=<span class="string">"match_parent"</span>
<span class="attr">android:foregroundInsidePadding</span>=<span class="string">"false"</span>
<span class="attr">android:foregroundGravity</span>=<span class="string">"fill_horizontal|top"</span>
<span class="attr">android:foreground</span>=<span class="string">"?android:attr/windowContentOverlay"</span> /></span>
<span class="tag"></<span class="name">LinearLayout</span>></span>
</code></pre>
<p>来看下去处标题栏后的视图树 </p>
<p><img src="/img/setContentView1.png" alt="setContentView"> </p>
<p>所以installDecor主要是初始化了PhoneWindow中的DecorView.和contentParent,之后在setContentView()中通过mLayoutInflater.inflate(layoutResID, mContentParent);将layoutResId,add到初始化好的contentParent中。<br>大家是否好奇状态栏怎么被加载进DecorView的,我们来看下DecorView中的updateColorViewInt方法 </p>
<pre><code class="java"><span class="function"><span class="keyword">private</span> View <span class="title">updateColorViewInt</span><span class="params">(View view, <span class="keyword">int</span> sysUiVis, <span class="keyword">int</span> systemUiHideFlag,
<span class="keyword">int</span> translucentFlag, <span class="keyword">int</span> color, <span class="keyword">int</span> height, <span class="keyword">int</span> verticalGravity,
String transitionName, <span class="keyword">int</span> id, <span class="keyword">boolean</span> hiddenByWindowFlag)</span> </span>{
......
<span class="keyword">if</span> (view == <span class="keyword">null</span>) {
<span class="keyword">if</span> (show) {
view = <span class="keyword">new</span> View(mContext);
view.setBackgroundColor(color);
view.setTransitionName(transitionName);
view.setId(id);
addView(view, <span class="keyword">new</span> LayoutParams(LayoutParams.MATCH_PARENT, height,
Gravity.START | verticalGravity));
}
} <span class="keyword">else</span> {
......
}
<span class="keyword">return</span> view;
}
</code></pre>
<p>可以看到直接new了一个view,这个view就是状态栏,然后将状态栏添加到了DecorView,其实这个状态栏只是一个单纯的占位view。被updateColorViews方法调用,比如当我们调用setStatusBarColor时就是调用了updateColorViews这个方法。这里先不做过多介绍。</p>
<h1 id="findViewById"><a href="#findViewById" class="headerlink" title="findViewById"></a>findViewById</h1><p>那么将layout添加进decorView中后,我们是怎么通过findViewById找到View的呢?<br>看下Activity的findViewById方法 </p>
<pre><code class="java"><span class="comment">/**
* Finds a view that was identified by the id attribute from the XML that
* was processed in {<span class="doctag">@link</span> #onCreate}.
*
* <span class="doctag">@return</span> The view if found or null otherwise.
*/</span>
<span class="function"><span class="keyword">public</span> View <span class="title">findViewById</span><span class="params">(<span class="keyword">int</span> id)</span> </span>{
<span class="keyword">return</span> getWindow().findViewById(id);
}
</code></pre>
<p>又是到了window中,看下window中的方法 </p>
<pre><code class="java"><span class="function"><span class="keyword">public</span> View <span class="title">findViewById</span><span class="params">(<span class="keyword">int</span> id)</span> </span>{
<span class="keyword">return</span> getDecorView().findViewById(id);
}
</code></pre>
<p>是调用了getDecorView的findViewById,也就是调用了view的findViewById,我们来看下view类中 </p>
<pre><code class="java"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> View <span class="title">findViewById</span><span class="params">(<span class="keyword">int</span> id)</span> </span>{
<span class="keyword">if</span> (id < <span class="number">0</span>) {
<span class="keyword">return</span> <span class="keyword">null</span>;
}
<span class="keyword">return</span> findViewTraversal(id);
}
<span class="function"><span class="keyword">protected</span> View <span class="title">findViewTraversal</span><span class="params">(<span class="keyword">int</span> id)</span> </span>{
<span class="keyword">if</span> (id == mID) {
<span class="keyword">return</span> <span class="keyword">this</span>;
}
<span class="keyword">return</span> <span class="keyword">null</span>;
}
</code></pre>
<p>到这我们就疑惑了,直接判断了id是否为view的id,是的话就返回。怎么也应该有一个循环或者递归查找啊,什么都没有。<br>这时我们来看下,mID是怎么初始化的 </p>
<pre><code class="java"> ....
<span class="keyword">case</span> com.android.internal.R.styleable.View_id:
mID = a.getResourceId(attr, NO_ID);
<span class="keyword">break</span>;
...
</code></pre>
<p>喔,这个id就是我们在xml中设置的id。那会不会在ViewGroup中进行查找的呢?来看下 </p>
<pre><code class="java"><span class="function"><span class="keyword">protected</span> View <span class="title">findViewTraversal</span><span class="params">(<span class="keyword">int</span> id)</span> </span>{
<span class="keyword">if</span> (id == mID) {
<span class="keyword">return</span> <span class="keyword">this</span>;
}
<span class="keyword">final</span> View[] where = mChildren;
<span class="keyword">final</span> <span class="keyword">int</span> len = mChildrenCount;
<span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < len; i++) {
View v = where[i];
<span class="keyword">if</span> ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == <span class="number">0</span>) {
v = v.findViewById(id);
<span class="keyword">if</span> (v != <span class="keyword">null</span>) {
<span class="keyword">return</span> v;
}
}
}
<span class="keyword">return</span> <span class="keyword">null</span>;
}
</code></pre>
<p>果然, ViewGroup重写了View的findViewTraversal()方法,遍历了自己的child的findViewById方法,如果找到了返回View自身。<br>ok,到现在我们就理解了view是怎么findViewById的了。 </p>
<h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>上面我们介绍了,Activity setContentView和findViewById的流程,是不是又多了一层理解呢,接下来我们会介绍,LayoutInflater是怎么把资源文件inflate到view中的,还有view的加载机制。如果有漏掉的之后再进行补充</p>
]]></content>
<summary type="html">
<p>当我们给Activity设置布局时,都是直接调用setContentView来完成的,但具体Android是怎么把布局加载到window,又是怎么通过findViewById获取view对象的,我们可能并没有太关心,下面就结合源码来分析下这个过程。</p>
<h1 id="
</summary>
<category term="Android" scheme="http://ibat.xyz/categories/Android/"/>
<category term="Android" scheme="http://ibat.xyz/tags/Android/"/>
<category term="资源加载" scheme="http://ibat.xyz/tags/%E8%B5%84%E6%BA%90%E5%8A%A0%E8%BD%BD/"/>
</entry>
<entry>
<title>Drawable mutations解析</title>
<link href="http://ibat.xyz/2017/06/14/Drawable%20mutations%E8%A7%A3%E6%9E%90/"/>
<id>http://ibat.xyz/2017/06/14/Drawable mutations解析/</id>
<published>2017-06-14T15:19:06.000Z</published>
<updated>2017-07-24T06:04:23.000Z</updated>
<content type="html"><![CDATA[<h1 id="Drawable-mutations"><a href="#Drawable-mutations" class="headerlink" title="Drawable mutations"></a>Drawable mutations</h1><p>有没有遇到过这样一种情况,我们要加载同一资源到两个ImageView,但需要给其中一个资源改变颜色或者透明度。如下面的代码 </p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">ImageView imageView1 = (ImageView) view.findViewById(R.id.imageview);</div><div class="line"> ImageView imageView2 = (ImageView) view.findViewById(R.id.imageview2);</div><div class="line"></div><div class="line"> Drawable drawable = getResources().getDrawable(R.mipmap.ic_launcher);</div><div class="line"> drawable.setColorFilter(Color.RED, PorterDuff.Mode.SRC_ATOP);</div><div class="line">imageView.setImageDrawable(drawable);</div><div class="line"></div><div class="line">Drawable drawable1 = getResources().getDrawable(R.mipmap.ic_launcher);</div><div class="line">imageView2.setImageDrawable(drawable1);</div></pre></td></tr></table></figure>
<p>我们给imageView1设置ColorFilter改变一下图标的颜色,imageview2保持不变。这样会发生什么呢,看下面的图: </p>
<p><img src="/img/drawable_mutate1.png" alt="mutate(1)"> </p>
<p>这时候奇怪的事情发生了,两个ImageView都被改变了颜色。这是因为Drawable使用在Android系统中使用范围比较广,系统对此作了优化,同一资源的drawable共享一个状态,叫做ConstantState.例如,上面的R.mipmap.ic_launcher,每次新建一个drawable都是一个不同的drawable实例,但他们共享一个状态,这个状态中包含bitmap image,所以所有的R.mipmap.ic_launcher都共享一个bitmap,这就是两个ImageView都改变颜色原因。 </p>
<blockquote>
<p>Because drawables are used so extensively throughout the system, Android optimizes them when they are loaded from resources. For instance, every time you create a Button, a new drawable is loaded from the framework resources (android.R.drawable.btn_default). This means all buttons across all the apps use a different drawable instance as their background. However, all these drawables share a common state, called the “constant state.” The content of this state varies according to the type of drawable you are using, but it usually contains all the properties that can be defined by a resource. In the case of a button, the constant state contains a bitmap image. This way, all buttons across all applications share the same bitmap, which saves a lot of memory.(摘抄自<a href="https://android-developers.googleblog.com/2009/05/drawable-mutations.html" target="_blank" rel="external">Android Developers Blog</a>)。如下图所示 </p>
</blockquote>
<p><img src="/img/drawable_mutate2.png" alt="mutate(2)"></p>
<p>但有没有办法解决呢,Drawable提供了一个mutate方法,我们来看下mutate方法的注释 </p>
<blockquote>
<p>Make this drawable mutable. This operation cannot be reversed. A mutable drawable is guaranteed to not share its state with any other drawable. This is especially useful when you need to modify properties of drawables loaded from resources. By default, all drawables instances loaded from the same resource share a common state; if you modify the state of one instance, all the other instances will receive the same modification. Calling this method on a mutable Drawable will have no effect.</p>
</blockquote>
<p>mutate不知道怎么翻译合适,姑且叫做可变的吧。这个mutate方法使得这个drawable变成可变的,这个操作不可逆转。调用了mutate方法,使得该drawable不和其他drawable共享状态。<br>我们来看下这个mutate的实现,发现在Drawable源码中,mutate方法只是返回了他自身。那我们来看下drawable子类有没有对该方法重写。我们找到BitmapDrawable </p>
<pre><code class="java"> <span class="meta">@Override</span>
<span class="function"><span class="keyword">public</span> Drawable <span class="title">mutate</span><span class="params">()</span> </span>{
<span class="keyword">if</span> (!mMutated && <span class="keyword">super</span>.mutate() == <span class="keyword">this</span>) {
mBitmapState = <span class="keyword">new</span> BitmapState(mBitmapState);
mMutated = <span class="keyword">true</span>;
}
<span class="keyword">return</span> <span class="keyword">this</span>;
}
</code></pre>
<p>如果没有调用过mutate方法,会新建一个BitmapState,再将mMutated置为true。这样相当于做了一次状态的拷贝,就不会与其他drawable共享状态了。<br>接下来我们修改下上面的代码 </p>
<pre><code class="java">ImageView imageView1 = (ImageView) view.findViewById(R.id.imageview);
ImageView imageView2 = (ImageView) view.findViewById(R.id.imageview2);
Drawable drawable = getResources().getDrawable(R.mipmap.ic_launcher).mutate();
drawable.setColorFilter(Color.RED, PorterDuff.Mode.SRC_ATOP);
imageView.setImageDrawable(drawable);
Drawable drawable1 = getResources().getDrawable(R.mipmap.ic_launcher);
imageView2.setImageDrawable(drawable1);
</code></pre>
<p>运行效果: </p>
<p><img src="/img/drawable_mutate3.png" alt="mutate3"><br>这次两个ImageView不相同了,下一节我们来学习下资源的加载机制,看Resources类是怎么加载资源的。</p>
]]></content>
<summary type="html">
<h1 id="Drawable-mutations"><a href="#Drawable-mutations" class="headerlink" title="Drawable mutations"></a>Drawable mutations</h1><p>有没有遇到过
</summary>
<category term="Android" scheme="http://ibat.xyz/categories/Android/"/>
<category term="Android" scheme="http://ibat.xyz/tags/Android/"/>
<category term="Drawable" scheme="http://ibat.xyz/tags/Drawable/"/>
</entry>
<entry>
<title>Android动态加载插件资源</title>
<link href="http://ibat.xyz/2017/04/18/Android%E5%8A%A8%E6%80%81%E5%8A%A0%E8%BD%BD%E6%8F%92%E4%BB%B6%E8%B5%84%E6%BA%90/"/>
<id>http://ibat.xyz/2017/04/18/Android动态加载插件资源/</id>
<published>2017-04-18T08:30:28.000Z</published>
<updated>2017-04-20T08:11:42.000Z</updated>
<content type="html"><![CDATA[<h1 id="Android动态加载插件资源"><a href="#Android动态加载插件资源" class="headerlink" title="Android动态加载插件资源"></a>Android动态加载插件资源</h1><p>最近在看app的换肤功能。简单的来说就是动态读取插件apk中的资源,需要进行换肤的控件所用到的资源在主apk和插件apk中各维护了一份,且资源名称相同。<br>插件听起来高大上,但其实就是一个apk文件。所以我们所要做的,就是怎么样能让插件中的资源加载进本地,并且读取到。 </p>
<h1 id="Resource的创建"><a href="#Resource的创建" class="headerlink" title="Resource的创建"></a>Resource的创建</h1><p>在app内部加载资源使用的是context.getResources(),context中getResources()方法是一个抽象方法,具体的实现在ContextImpl类中。 </p>
<pre><code>Resources resources = packageInfo.getResources(mainThread);
</code></pre><p>参数packageInfo指向的是一个LoadedApk对象,这个LoadedApk对象描述的是当前正在启动的Activity组所属的Apk。 </p>
<p>进入到LoadedApk的getResources(mainThread)方法 </p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> Resources <span class="title">getResources</span><span class="params">(ActivityThread mainThread)</span> </span>{</div><div class="line"> <span class="keyword">if</span> (mResources == <span class="keyword">null</span>) {</div><div class="line"> mResources = mainThread.getTopLevelResources(mResDir, mSplitResDirs, mOverlayDirs,</div><div class="line"> mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, <span class="keyword">this</span>);</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> mResources;</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>LoadedApk类的成员函数getResources首先检查其成员变量mResources的值是否等于null。如果不等于的话,那么就会将它所指向的一个Resources对象返回给调用者,否则的话,就会调用参数mainThread的成员函数getTopLevelResources来获得这个Resources对象,然后再返回给调用者。 mainThread指向一个ActivityThread对象。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">ActivityThread</span> </span>{ </div><div class="line"> ...... </div><div class="line"> <span class="keyword">final</span> HashMap<ResourcesKey, WeakReference<Resources> > mActiveResources </div><div class="line"> = <span class="keyword">new</span> HashMap<ResourcesKey, WeakReference<Resources> >(); </div><div class="line"> </div><div class="line"> <span class="function">Resources <span class="title">getTopLevelResources</span><span class="params">(String resDir, CompatibilityInfo compInfo)</span> </span>{ </div><div class="line"> ResourcesKey key = <span class="keyword">new</span> ResourcesKey(resDir, compInfo.applicationScale); </div><div class="line"> Resources r; </div><div class="line"> <span class="keyword">synchronized</span> (mPackages) { </div><div class="line"> ...... </div><div class="line"> WeakReference<Resources> wr = mActiveResources.get(key); </div><div class="line"> r = wr != <span class="keyword">null</span> ? wr.get() : <span class="keyword">null</span>; </div><div class="line"> ...... </div><div class="line"> <span class="keyword">if</span> (r != <span class="keyword">null</span> && r.getAssets().isUpToDate()) { </div><div class="line"> ...... </div><div class="line"> <span class="keyword">return</span> r; </div><div class="line"> } </div><div class="line"> } </div><div class="line"> ...... </div><div class="line"> AssetManager assets = <span class="keyword">new</span> AssetManager(); </div><div class="line"> <span class="keyword">if</span> (assets.addAssetPath(resDir) == <span class="number">0</span>) { </div><div class="line"> <span class="keyword">return</span> <span class="keyword">null</span>; </div><div class="line"> } </div><div class="line"> ...... </div><div class="line"> r = <span class="keyword">new</span> Resources(assets, metrics, getConfiguration(), compInfo); </div><div class="line"> ...... </div><div class="line"> <span class="keyword">synchronized</span> (mPackages) { </div><div class="line"> WeakReference<Resources> wr = mActiveResources.get(key); </div><div class="line"> Resources existing = wr != <span class="keyword">null</span> ? wr.get() : <span class="keyword">null</span>; </div><div class="line"> <span class="keyword">if</span> (existing != <span class="keyword">null</span> && existing.getAssets().isUpToDate()) { </div><div class="line"> <span class="comment">// Someone else already created the resources while we were </span></div><div class="line"> <span class="comment">// unlocked; go ahead and use theirs. </span></div><div class="line"> r.getAssets().close(); </div><div class="line"> <span class="keyword">return</span> existing; </div><div class="line"> } </div><div class="line"> <span class="comment">// XXX need to remove entries when weak references go away </span></div><div class="line"> mActiveResources.put(key, <span class="keyword">new</span> WeakReference<Resources>(r)); </div><div class="line"> <span class="keyword">return</span> r; </div><div class="line"> } </div><div class="line"> } </div><div class="line">}</div></pre></td></tr></table></figure>
<p>在其中创建了AssertManager对象,assets.addAssetPath(resDir)这句话的意思是把资源目录里的资源都加载到AssetManager对象中 如果我们把一个未安装的apk的路径传给这个方法,那么apk中的资源就被加载到AssetManager对象里面了。但它是一个隐藏方法,需要反射调用。有了AssertManager对象就能创建Resources对象了。 </p>
<h1 id="AssetManager介绍"><a href="#AssetManager介绍" class="headerlink" title="AssetManager介绍"></a>AssetManager介绍</h1><blockquote>
<p>Provides access to an application’s raw asset files; see Resources for the way most applications will want to retrieve their resource data. This class presents a lower-level API that allows you to open and read raw files that have been bundled with the application as a simple stream of bytes. </p>
</blockquote>
<p>AssetManager提供了应用的原始资源,通过它可以让应用程序检索他们的资源数据。<br>在ResourcesImpl类中存在AssetManager的引用mAsset.举个例子看下Resources怎么通过AssetManager加载数据.看下Resources的getString()方法 </p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> String <span class="title">getString</span><span class="params">(@StringRes <span class="keyword">int</span> id)</span> <span class="keyword">throws</span> NotFoundException </span>{</div><div class="line"> <span class="keyword">return</span> getText(id).toString();</div><div class="line"> }</div><div class="line"> </div><div class="line"> </div><div class="line"><span class="function"><span class="keyword">public</span> CharSequence <span class="title">getText</span><span class="params">(@StringRes <span class="keyword">int</span> id)</span> <span class="keyword">throws</span> NotFoundException </span>{</div><div class="line"> CharSequence res = mResourcesImpl.getAssets().getResourceText(id);</div><div class="line"> <span class="keyword">if</span> (res != <span class="keyword">null</span>) {</div><div class="line"> <span class="keyword">return</span> res;</div><div class="line"> }</div><div class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> NotFoundException(<span class="string">"String resource ID #0x"</span></div><div class="line"> + Integer.toHexString(id));</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>Resources将资源id传给了AssetManager的getResourceText方法。从AssetManager中返回了资源数据。有兴趣大家可以深入研究一下,这里不做过多介绍。 接下来我们写一个小demo看看更换皮肤包的简单实现。 </p>
<h1 id="Demo"><a href="#Demo" class="headerlink" title="Demo"></a>Demo</h1><p>首先如果只加载本地皮肤包(带有皮肤资源的apk)的时候,我们将皮肤包放入assets文件夹内,再在初始化的时候加载进sdcard中。如果要下载皮肤包,则直接下载进sdcard指定目录中。我们现在只做本地皮肤包的更换。 </p>
<p>先进行初始化的操作: </p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//先定义全局的名称和存储路径</span></div><div class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String APK_NAME = <span class="string">"sample.apk"</span>;</div><div class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String APK_DIR = Environment.</div><div class="line"> getExternalStorageDirectory() + File.separator + APK_NAME;</div><div class="line"> </div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">(Context context)</span> </span>{</div><div class="line"></div><div class="line"> File pluginFile = <span class="keyword">new</span> File(APK_DIR);</div><div class="line"> <span class="keyword">if</span> (pluginFile.exists()) {</div><div class="line"> pluginFile.delete();</div><div class="line"> }</div><div class="line"></div><div class="line"> InputStream is = <span class="keyword">null</span>;</div><div class="line"> FileOutputStream fos = <span class="keyword">null</span>;</div><div class="line"></div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> is = context.getAssets().open(APK_NAME);</div><div class="line"> fos = <span class="keyword">new</span> FileOutputStream(APK_DIR);</div><div class="line"> <span class="keyword">int</span> bytes;</div><div class="line"> <span class="keyword">byte</span>[] byteArr = <span class="keyword">new</span> <span class="keyword">byte</span>[<span class="number">1024</span> * <span class="number">4</span>];</div><div class="line"> <span class="keyword">while</span> ((bytes = is.read(byteArr, <span class="number">0</span>, <span class="number">1024</span> * <span class="number">4</span>)) != -<span class="number">1</span>) {</div><div class="line"> fos.write(byteArr, <span class="number">0</span>, bytes);</div><div class="line"> }</div><div class="line"> PackageManager mPm = context.getPackageManager();</div><div class="line"> PackageInfo mInfo = mPm.getPackageArchiveInfo(APK_DIR, PackageManager.GET_ACTIVITIES);</div><div class="line"> mSkinPackageName = mInfo.packageName;</div><div class="line"></div><div class="line"> AssetManager assetManager = AssetManager.class.newInstance();</div><div class="line"> Method method = assetManager.getClass().getMethod(<span class="string">"addAssetPath"</span>, String.class);</div><div class="line"> method.invoke(assetManager, pluginFile.getAbsolutePath());</div><div class="line"></div><div class="line"> mSuperResources = context.getResources();</div><div class="line"> mResources = <span class="keyword">new</span> Resources(assetManager, mSuperResources.getDisplayMetrics(),</div><div class="line"> mSuperResources.getConfiguration());</div><div class="line"></div><div class="line"> } <span class="keyword">catch</span> (Exception e) {</div><div class="line"> e.printStackTrace();</div><div class="line"> } <span class="keyword">finally</span> {</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> <span class="keyword">if</span> (is != <span class="keyword">null</span>) {</div><div class="line"> is.close();</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (fos != <span class="keyword">null</span>) {</div><div class="line"> fos.close();</div><div class="line"> }</div><div class="line"> } <span class="keyword">catch</span> (IOException e) {</div><div class="line"> e.printStackTrace();</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>读取assets下的sample.apk,将其放进sdcard中。通过反射创建AssetManager,并调用addAssetPath方法,将apk的路径传入AssetManager中。再new 一个Resources对象,传入上步生成的AssetManager对象,这时就拿到了皮肤包apk的Resources对象。初始化完成。 </p>
<p>接下来在布局中放入一个TextView,动态替换TextView控件用到的资源。 </p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><TextView</div><div class="line"> android:id=<span class="string">"@+id/tv_name"</span></div><div class="line"> android:layout_width=<span class="string">"wrap_content"</span></div><div class="line"> android:layout_height=<span class="string">"wrap_content"</span></div><div class="line"> android:text=<span class="string">"@string/app_name"</span></div><div class="line"> android:textSize=<span class="string">"20sp"</span></div><div class="line"> android:layout_centerHorizontal=<span class="string">"true"</span></div><div class="line"> android:textColor=<span class="string">"@color/colorPrimaryDark"</span></div><div class="line"> android:background=<span class="string">"@mipmap/ic_launcher"</span></div><div class="line"> /></div></pre></td></tr></table></figure>
<p>代码实现</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line">btnLoad = (Button) findViewById(R.id.btn_load);</div><div class="line"> tvName = (TextView) findViewById(R.id.tv_name);</div><div class="line"> btnLoad.setOnClickListener(<span class="keyword">new</span> View.OnClickListener() {</div><div class="line"> <span class="meta">@Override</span></div><div 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>{</div><div class="line"></div><div class="line"> tvName.setBackground(ResourceManager.getInstance()</div><div class="line"> .loadMipmapResource(R.mipmap.ic_launcher));</div><div class="line"></div><div class="line"> tvName.setText(ResourceManager.getInstance()</div><div class="line"> .loadStringResource(R.string.app_name));</div><div class="line"></div><div class="line"> tvName.setTextColor(ResourceManager.getInstance()</div><div class="line"> .loadColorResource(R.color.colorPrimaryDark));</div><div class="line"></div><div class="line"> }</div><div class="line"> });</div></pre></td></tr></table></figure>
<p>ok,这时候我们看下ResourceManager.getInstance().loadMipmapResource(R.mipmap.ic_launcher)的实现;</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> Drawable <span class="title">loadMipmapResource</span><span class="params">(<span class="keyword">int</span> resId)</span></span>{</div><div class="line"> <span class="keyword">return</span> mResources.getDrawable(findTrueResId(resId,<span class="string">"mipmap"</span>));</div><div class="line">}</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * 找到插件app中rescource的真正id</div><div class="line"> * <span class="doctag">@param</span> resId 主app中的资源id</div><div class="line"> */</div><div class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">int</span> <span class="title">findTrueResId</span><span class="params">(<span class="keyword">int</span> resId,String defType)</span></span>{</div><div class="line"> String entryName = mSuperResources.getResourceEntryName(resId);</div><div class="line"> Log.e(TAG, <span class="string">"entryName "</span> + entryName);</div><div class="line"> String resourceName = mSuperResources.getResourceName(resId);</div><div class="line"> Log.e(TAG, <span class="string">"resourceName "</span> + resourceName);</div><div class="line"> <span class="keyword">int</span> trueResId = mResources.getIdentifier(entryName, defType,mSkinPackageName);</div><div class="line"> Log.e(TAG, <span class="string">"trueResId "</span> + trueResId);</div><div class="line"></div><div class="line"> <span class="keyword">return</span> trueResId;</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>上面的代码,mSuperResources是当前apk的Resources对象,通过getResourceEntryName(resId),拿到resId对应的名称.<br>还有一个方法,getResourceName,这个和getResourceEntryName的区别在于,getResourceName拿到的全名包括包名,getResourceEntryName拿到的是简短名称.这里我们使用getResourceEntryName方法。<br>调用皮肤包的Resources对象的getIdentifier方法,会返回资源在皮肤包中的真实id,将真实id拿到后,就可以调用getDrawable(trueId)来加载资源了。来看下打印出来的日志。 </p>
<pre><code>xyz.ibat.pluginsloader E/DONG: entryName ic_launcher
xyz.ibat.pluginsloader E/DONG: resourceName xyz.ibat.pluginsloader:mipmap/ic_launcher
xyz.ibat.pluginsloader E/DONG: trueResId 2130903047
</code></pre><p>加载string和color和上述方法原理相同。我们来看下最终效果。 </p>
<p>换肤前:</p>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="/img/Android动态加载资源1.png" alt="换肤前" title="">
</div>
<div class="image-caption">换肤前</div>
</figure>
<p>换肤后: </p>
<p><img src="/img/Android动态加载资源2.png" alt="换肤后"> </p>
<h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><p><a href="http://blog.csdn.net/luoshengyang/article/details/8791064" target="_blank" rel="external">Android应用程序资源管理器(Asset Manager)的创建过程分析</a><br><a href="http://blog.csdn.net/singwhatiwanna/article/details/24532419" target="_blank" rel="external">Android源码分析-资源加载机制</a></p>
]]></content>
<summary type="html">
<h1 id="Android动态加载插件资源"><a href="#Android动态加载插件资源" class="headerlink" title="Android动态加载插件资源"></a>Android动态加载插件资源</h1><p>最近在看app的换肤功能。简单的来说
</summary>
<category term="Android" scheme="http://ibat.xyz/categories/Android/"/>
<category term="Android" scheme="http://ibat.xyz/tags/Android/"/>
<category term="插件化" scheme="http://ibat.xyz/tags/%E6%8F%92%E4%BB%B6%E5%8C%96/"/>
</entry>
<entry>
<title>自定义自适应弹出位置的PopupWindow</title>
<link href="http://ibat.xyz/2017/04/12/%E8%87%AA%E5%AE%9A%E4%B9%89%E8%87%AA%E9%80%82%E5%BA%94%E5%BC%B9%E5%87%BA%E4%BD%8D%E7%BD%AE%E7%9A%84PopupWindow/"/>
<id>http://ibat.xyz/2017/04/12/自定义自适应弹出位置的PopupWindow/</id>
<published>2017-04-12T03:48:04.000Z</published>
<updated>2017-04-12T12:32:28.000Z</updated>
<content type="html"><![CDATA[<p>项目地址: <a href="https://github.com/JrDong/FitPopupWindow" target="_blank" rel="external">https://github.com/JrDong/FitPopupWindow</a><br>效果图:<br><img src="/img/popupwindow.gif" alt="效果图"></p>
<h1 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h1><p>项目是仿照网易新闻或者今日头条的不感兴趣功能。现在很多应用都加入了feed流,对自己不感兴趣的条目可以删除。<br>考虑到两个因素:1.每个item中叉号的位置并不是固定的,所以我们要根据点击的位置来判断弹框气泡的位置。2.list滑动时,当我想点击下面的item,则弹框应该向上弹出,反之亦然,所以要判断弹出的方向。<br>功能实现的话选择PopupWindow来实现。</p>
<h1 id="PopupWindow"><a href="#PopupWindow" class="headerlink" title="PopupWindow"></a>PopupWindow</h1><p>首先我们来自定义一个PopupWindow,先对PopupWindow进行初始化 </p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"></div><div class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">(Activity context, <span class="keyword">int</span> width, <span class="keyword">int</span> height)</span> </span>{</div><div class="line"> <span class="keyword">this</span>.context = context;</div><div class="line"> </div><div class="line"> setWidth(width);</div><div class="line"> setHeight(height);</div><div class="line"> <span class="comment">//点击空白处让PopupWindow消失</span></div><div class="line"> setBackgroundDrawable(<span class="keyword">new</span> ColorDrawable(<span class="number">0x00000000</span>));</div><div class="line"> setOutsideTouchable(<span class="keyword">true</span>);</div><div class="line"> <span class="comment">//PopupWindow弹出后,所有的触屏和物理按键都有PopupWindows处理</span></div><div class="line"> setFocusable(<span class="keyword">true</span>);</div><div class="line"> <span class="comment">//设置消失的监听</span></div><div class="line"> setOnDismissListener(<span class="keyword">this</span>);</div><div class="line"> <span class="comment">//设置动画</span></div><div class="line"> setAnimationStyle(R.style.popp_anim);</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>初始化好之后,就需要进行计算弹出的位置了。定义PopupWindow弹窗位置有三个方法:</p>
<p>showAsDropDown(View anchor):在某个控件正下方,无偏移 </p>
<p>showAsDropDown(View anchor, int xoff, int yoff):相对某个控件的位置,有偏移</p>
<p>showAtLocation(View parent, int gravity, int x, int y):第一个参数官方文档”a parent view to get the android.view.View.getWindowToken() token from“,这个parent的作用应该是调用其getWindowToken()方法获取窗口的Token,所以,只要是该窗口上的控件就可以了。gravity控制弹出位置,x,y分别控制偏移量。</p>
<p>接下来,计算偏移量: </p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * <span class="doctag">@param</span> anchorView 弹出window的view</div><div class="line"> * <span class="doctag">@param</span> contentView PopupWindow的内容布局</div><div class="line"> * <span class="doctag">@return</span> window显示的左上角的xOff, yOff坐标</div><div class="line"> */</div><div class="line"> <span class="keyword">protected</span> <span class="keyword">int</span>[] calculatePopWindowPos(<span class="keyword">final</span> View anchorView, <span class="keyword">final</span> View contentView) {</div><div class="line"> <span class="keyword">final</span> <span class="keyword">int</span> windowPos[] = <span class="keyword">new</span> <span class="keyword">int</span>[<span class="number">2</span>];</div><div class="line"> <span class="keyword">final</span> <span class="keyword">int</span> anchorLoc[] = <span class="keyword">new</span> <span class="keyword">int</span>[<span class="number">2</span>];</div><div class="line"> <span class="comment">// 获取锚点View在屏幕上的左上角坐标位置</span></div><div class="line"> anchorView.getLocationOnScreen(anchorLoc);</div><div class="line"> <span class="keyword">final</span> <span class="keyword">int</span> anchorHeight = anchorView.getHeight();</div><div class="line"> <span class="keyword">final</span> <span class="keyword">int</span> anchorWidth = anchorView.getWidth();</div><div class="line"> mXCoordinate = anchorLoc[<span class="number">0</span>];</div><div class="line"> <span class="comment">// 获取屏幕的高宽</span></div><div class="line"> <span class="keyword">final</span> <span class="keyword">int</span> screenHeight = ScreenUtils.getScreenHeight(anchorView.getContext());</div><div class="line"> <span class="keyword">final</span> <span class="keyword">int</span> screenWidth = ScreenUtils.getScreenWidth(anchorView.getContext());</div><div class="line"> contentView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);</div><div class="line"> <span class="comment">// 计算contentView的高宽</span></div><div class="line"> <span class="keyword">int</span> windowHeight = contentView.getMeasuredHeight();</div><div class="line"> mWindowWidth = mWindowWidth > <span class="number">0</span> ? mWindowWidth : contentView.getMeasuredWidth();</div><div class="line"></div><div class="line"> <span class="comment">// 判断需要向上弹出还是向下弹出,如果要改变弹出策略,改变此处即可</span></div><div class="line"> <span class="comment">// 目前是根据屏幕的一半进行判断</span></div><div class="line"> <span class="keyword">final</span> <span class="keyword">boolean</span> isNeedShowUp = (screenHeight - anchorLoc[<span class="number">1</span>] - anchorHeight < screenHeight / <span class="number">2</span>);</div><div class="line"></div><div class="line"> <span class="comment">// 判断需要向左弹出还是向右弹出</span></div><div class="line"> <span class="keyword">final</span> <span class="keyword">boolean</span> isNeedShowLeft = (anchorLoc[<span class="number">0</span>] < mWindowWidth / <span class="number">2</span>);</div><div class="line"> </div><div class="line"> <span class="comment">//分别设置水平竖直弹出位置</span></div><div class="line"> setHorizontal(isNeedShowLeft ? FitPopupWindowLayout.LEFT : FitPopupWindowLayout.RIGHT);</div><div class="line"> setVertical(isNeedShowUp ? FitPopupWindowLayout.UP : FitPopupWindowLayout.DOWN);</div><div class="line"> </div><div class="line"> windowPos[<span class="number">0</span>] = (screenWidth - mWindowWidth) / <span class="number">2</span>;</div><div class="line"></div><div class="line"> windowPos[<span class="number">1</span>] = isNeedShowUp ?</div><div class="line"> anchorLoc[<span class="number">1</span>] - windowHeight - PADDING - FitPopupWindowLayout.SHARP_HEIGHT</div><div class="line"> : anchorLoc[<span class="number">1</span>] + anchorHeight + PADDING;</div><div class="line"></div><div class="line"> <span class="keyword">return</span> windowPos;</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>设置View的方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div></pre></td><td class="code"><pre><div class="line">/**</div><div class="line"> * <span class="meta">@contentView</span> 内容布局</div><div class="line"> * <span class="meta">@anchorView</span> 目标view,比如叉号</div><div class="line"> */</div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setView</span><span class="params">(View contentView, View anchorView)</span> </span>{</div><div class="line"> <span class="keyword">this</span>.anchorView = anchorView;</div><div class="line"> <span class="comment">//计算偏移坐标</span></div><div class="line"> windowPos = calculatePopWindowPos(anchorView, contentView);</div><div class="line"> <span class="comment">//自定义带气泡的布局,最外层</span></div><div class="line"> mFitPopupWindowLayout = <span class="keyword">new</span> FitPopupWindowLayout(context);</div><div class="line"> RelativeLayout.LayoutParams layoutParams = <span class="keyword">new</span> RelativeLayout.LayoutParams(</div><div class="line"> ViewGroup.LayoutParams.MATCH_PARENT, getHeight() - FitPopupWindowLayout.SHARP_HEIGHT);</div><div class="line"> layoutParams.bottomMargin = FitPopupWindowLayout.SHARP_HEIGHT;</div><div class="line"></div><div class="line"> contentView.setLayoutParams(layoutParams);</div><div class="line"> <span class="comment">//给起泡布局设置方向,左上,右下等,并设置x方向上的偏移量</span></div><div class="line"> mFitPopupWindowLayout.setOrientation(getHorizontal(), getVertical(), getXCoordinate());</div><div class="line"> <span class="comment">//将内容布局添加到自定义的气泡布局中</span></div><div class="line"> mFitPopupWindowLayout.addView(contentView);</div><div class="line"> <span class="comment">//设置PopupWindow布局</span></div><div class="line"> setContentView(mFitPopupWindowLayout);</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>显示PopupWindow</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">show</span><span class="params">()</span> </span>{</div><div class="line"> showAtLocation(anchorView, Gravity.TOP | Gravity.END</div><div class="line"> , windowPos[<span class="number">0</span>], windowPos[<span class="number">1</span>]);</div><div class="line"> update();</div><div class="line"> <span class="comment">//弹出PopupWindow时让背景置灰,在onDismiss()回调中再将背景恢复</span></div><div class="line"> Window window = context.getWindow();</div><div class="line"> WindowManager.LayoutParams lp = window.getAttributes();</div><div class="line"> lp.alpha = <span class="number">0.7f</span>;</div><div class="line"> window.setAttributes(lp);</div><div class="line"></div><div class="line"> }</div></pre></td></tr></table></figure>
<p>接下来我们看下自定义气泡布局</p>
<p>气泡布局由两部分组成,一个是圆角矩形,一个是贝塞尔曲线画成的尖角。我们直接看下onDraw方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div></pre></td><td class="code"><pre><div class="line"></div><div class="line"><span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onDraw</span><span class="params">(Canvas canvas)</span> </span>{</div><div class="line"> <span class="comment">//添加圆角矩形</span></div><div class="line"> mPath.addRoundRect(<span class="keyword">new</span> RectF(<span class="number">0</span>, <span class="number">0</span>, getMeasuredWidth(), getMeasuredHeight() - SHARP_HEIGHT)</div><div class="line"> , RECT_CORNER, RECT_CORNER, Path.Direction.CW);</div><div class="line"> mPath.addPath(makeSharpPath());</div><div class="line"> canvas.drawPath(mPath, mPaint);</div><div class="line"> <span class="comment">//此处的mHorizontal和mVertical是上面PopupWindow计算出弹出位置后设置进来的</span></div><div class="line"> <span class="keyword">if</span> (mHorizontal == LEFT && mVertical == UP) {</div><div class="line"> setScaleX(<span class="number">1</span>);</div><div class="line"> setScaleY(<span class="number">1</span>);</div><div class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (mHorizontal == LEFT && mVertical == DOWN) {</div><div class="line"> setScaleX(<span class="number">1</span>);</div><div class="line"> setScaleY(-<span class="number">1</span>);</div><div class="line"> scaleChild(<span class="number">1</span>, -<span class="number">1</span>);</div><div class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (mHorizontal == RIGHT && mVertical == UP) {</div><div class="line"> setScaleX(-<span class="number">1</span>);</div><div class="line"> setScaleY(<span class="number">1</span>);</div><div class="line"> scaleChild(-<span class="number">1</span>, <span class="number">1</span>);</div><div class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (mHorizontal == RIGHT && mVertical == DOWN) {</div><div class="line"> setScaleX(-<span class="number">1</span>);</div><div class="line"> setScaleY(-<span class="number">1</span>);</div><div class="line"> scaleChild(-<span class="number">1</span>, -<span class="number">1</span>);</div><div class="line"> }</div><div class="line"></div><div class="line"> }</div><div class="line"> </div><div class="line"> <span class="function"><span class="keyword">private</span> Path <span class="title">makeSharpPath</span><span class="params">()</span> </span>{</div><div class="line"> mSharpPath.moveTo(mXoffset, getMeasuredHeight() - SHARP_HEIGHT);</div><div class="line"> mSharpPath.cubicTo(mXoffset, getMeasuredHeight(), mXoffset, getMeasuredHeight() - SHARP_HEIGHT,</div><div class="line"> SHARP_WIDTH + mXoffset, getMeasuredHeight() - SHARP_HEIGHT);</div><div class="line"> <span class="keyword">return</span> mSharpPath;</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>默认是按照左上角的位置画出的,也就是说先画出一个尖角在左上角的气泡view,<br>再根据偏移量用setScaleX,和 setScaleY进行旋转,但注意,旋转的话其中的子view也会跟着一起旋转,所以之后我们要再把其中的子view旋转回来。<br>mHorizontal和mVertical是上面PopupWindow计算出弹出位置后设置进来的。<br>这样一个带气泡的view就画好了。</p>
<p>我们再来整理一遍:<br>1.传入一个目标view,计算出PopupWindow的弹出方向,和x轴上的偏移量。<br>2.绘制一个气泡view,根据计算出来的方向和偏移量,画出气泡的方向。<br>3.传入一个内容布局,这个可以根据项目需求自定义。add到气泡ViewGroup中。<br>4.向上还是向下由showAtLocation方法决定,尖角的位置取决于通过目标view计算出来的方向和x轴偏移量,有了x轴的偏移量,就可以保证尖角在目标view的正下方。</p>
<p>以上就是自适应位置的全部思路,全部源码参考<a href="https://github.com/JrDong/FitPopupWindow" target="_blank" rel="external">https://github.com/JrDong/FitPopupWindow</a> </p>
]]></content>
<summary type="html">
<p>项目地址: <a href="https://github.com/JrDong/FitPopupWindow" target="_blank" rel="external">https://github.com/JrDong/FitPopupWindow</a><br>效
</summary>
<category term="Android" scheme="http://ibat.xyz/categories/Android/"/>
<category term="Android" scheme="http://ibat.xyz/tags/Android/"/>
<category term="PopupWindow" scheme="http://ibat.xyz/tags/PopupWindow/"/>
</entry>
<entry>
<title>分治法——快速排序,归并排序</title>
<link href="http://ibat.xyz/2017/03/12/%E5%88%86%E6%B2%BB%E6%B3%95%E2%80%94%E2%80%94%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F%EF%BC%8C%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F/"/>
<id>http://ibat.xyz/2017/03/12/分治法——快速排序,归并排序/</id>
<published>2017-03-12T01:32:21.000Z</published>
<updated>2017-03-12T09:08:28.000Z</updated>
<content type="html"><![CDATA[<h1 id="分治法"><a href="#分治法" class="headerlink" title="分治法"></a>分治法</h1><blockquote>
<p>分治法是一种很重要的算法,也就是“分而治之”的意思,就是把一个复杂的问题分解成两个或者多个相似的子问题,直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。</p>
</blockquote>
<p>比如二分搜索算法,排序算法中的快速排序和归并排序都属于分治法的一种。下面我们来看看归并排序和快速排序算法的实现。</p>
<h1 id="归并排序"><a href="#归并排序" class="headerlink" title="归并排序"></a>归并排序</h1><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="/img/Merge-sort-anim2.gif" alt="MergeSort2" title="">
</div>
<div class="image-caption">MergeSort2</div>
</figure>
<p>(维基百科)<br><strong>归并排序</strong>(Merge sort),是创建在归并操作上的一种有效的排序算法,效率为O(n log n)。最优时间复杂度,O(n),平均时间复杂度为O(n log n)。由上图我们可以了解到归并排序的过程。 </p>
<h2 id="实例分析"><a href="#实例分析" class="headerlink" title="实例分析"></a>实例分析</h2><p>以数组6 5 3 1 8 7 2 4为例。首先递归的将数组一分为2,并对子数组排序 </p>
<pre><code>[6, 5, 3, 1] [8, 7, 2, 4]
[6, 5] [3, 1] [8, 7] [2, 4]
[6], [5] [4], [3] [7], [8] [2], [4]
</code></pre><p>然后向上回溯,将两个数组合并成有序数组</p>
<pre><code>[6], [5] [4], [3] [7], [8] [2], [4]
[5, 6] [3, 4] [7, 8] [2, 4]
[3, 4, 5, 6] [2, 4, 7, 8]
[1, 2, 3, 4, 5, 6, 7, 8]
</code></pre><p>动图如下所示<br><img src="/img/Merge-sort-anim.gif" alt="MergeSort"><br>(维基百科)</p>
<h2 id="两个有序数组排序"><a href="#两个有序数组排序" class="headerlink" title="两个有序数组排序"></a>两个有序数组排序</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> *</div><div class="line"> * <span class="doctag">@param</span> a 有序数组a</div><div class="line"> * <span class="doctag">@param</span> b 有序数组b</div><div class="line"> * <span class="doctag">@param</span> result 结果数组</div><div class="line"> */</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">merge2</span><span class="params">(<span class="keyword">int</span>[] a,<span class="keyword">int</span> [] b, <span class="keyword">int</span>[] result)</span></span>{</div><div class="line"></div><div class="line"> <span class="keyword">int</span> i = <span class="number">0</span> , j = <span class="number">0</span> , k = <span class="number">0</span> ;</div><div class="line"> <span class="keyword">while</span> (i < a.length && j < b.length){</div><div class="line"> <span class="keyword">if</span> (a[i] < b[j]){</div><div class="line"> result[k++] = a[i++];</div><div class="line"> }<span class="keyword">else</span> {</div><div class="line"> result[k++] = b[j++];</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">while</span> (i < a.length){</div><div class="line"> result[k++] = a[i++];</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">while</span> (j < b.length){</div><div class="line"> result[k++] = b[j++];</div><div class="line"> }</div><div class="line"> print(result);</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>看会了两个有序数组的排序,则知道了如何实现归并排序</p>
<h2 id="Java代码实现"><a href="#Java代码实现" class="headerlink" title="Java代码实现"></a>Java代码实现</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">merge</span><span class="params">(<span class="keyword">int</span>[] arr, <span class="keyword">int</span>[] result, <span class="keyword">int</span> start, <span class="keyword">int</span> end)</span> </span>{</div><div class="line"></div><div class="line"> <span class="keyword">if</span> (start >= end) <span class="keyword">return</span>;</div><div class="line"></div><div class="line"> <span class="keyword">int</span> center = (start + end) / <span class="number">2</span>;</div><div class="line"> <span class="keyword">int</span> start1 = start, end1 = center;</div><div class="line"> <span class="keyword">int</span> start2 = center + <span class="number">1</span>, end2 = end;</div><div class="line"> merge(arr, result, start1, end1);</div><div class="line"> merge(arr, result, start2, end2);</div><div class="line"> <span class="keyword">int</span> k = start1;</div><div class="line"> <span class="keyword">while</span> (start1 <= end1 && start2 <= end2) {</div><div class="line"> <span class="keyword">if</span> (arr[start1] < arr[start2]) {</div><div class="line"> result[k++] = arr[start1++];</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> result[k++] = arr[start2++];</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">while</span> (start1 <= end1) {</div><div class="line"> result[k++] = arr[start1++];</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">while</span> (start2 <= end2) {</div><div class="line"> result[k++] = arr[start2++];</div><div class="line"> }</div><div class="line"> <span class="keyword">for</span> (k = start; k <= end; k++) {</div><div class="line"> arr[k] = result[k];</div><div class="line"> }</div><div class="line"> print(arr);</div><div class="line"></div><div class="line"> }</div></pre></td></tr></table></figure>
<h1 id="快速排序"><a href="#快速排序" class="headerlink" title="快速排序"></a>快速排序</h1><h2 id="简介-1"><a href="#简介-1" class="headerlink" title="简介"></a>简介</h2><figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="/img/Sorting_quicksort_anim.gif" alt="quickSort" title="">
</div>
<div class="image-caption">quickSort</div>
</figure>
<p>(使用快速排序法对一列数字进行排序的过程——维基百科) </p>
<p><strong>快速排序</strong>(Quicksort),是一种排序算法,最坏情况复杂度:Ο(n2),最优时间复杂度:Ο(n log n),平均时间复杂度:Ο(n log n)。快速排序的基本思想也是用了分治法的思想:找出一个元素X,在一趟排序中,使X左边的数都比X小,X右边的数都比X要大。然后再分别对X左边的数组和X右边的数组进行排序,直到数组不能分割为止。 </p>
<h2 id="具体操作"><a href="#具体操作" class="headerlink" title="具体操作"></a>具体操作</h2><p>ok,我们来看一下具体操作: </p>
<p>1.设置一个长度为n的数组A,定义两个变量i = 0,j = n - 1;<br>2.从数组中挑选出一个元素作为基准元素,复制给key;<br>3.从j开始从后向前搜索,j–,找到比key小的值,将A[j]与A[i]互换;<br>4.从i 开始向后搜索,i++,找到比key大的值,将A[i]与A[j]互换;<br>5.递归的,重复2,3,4步,直到i == j ; </p>
<p>举个栗子: </p>
<ul>
<li>存在一个数组A:6 2 7 3 8 9 ,创建i = 0 ; j = 5,选择一个基准元素 k = 6 </li>
<li>j 从右向左查找比k小的元素,发现,当 j = 3 时,发现元素3比k小,则另A[i] 与 A[j]交换,得到3 2 7 6 8 9; </li>
<li>i 从左向右进行查找,当 i = 2时,发现元素 7 比k大,则另A[i] 与A[j]进行交换,得到 3 2 6 7 8 9; </li>
<li>接着,再减小j,重复上面的循环。 </li>
<li>但是我们发现,在本例中,一次循环后j与i就相等了,他们的下标同时指向了2.这时候,我们就进行分组,将3 2分为一组,7 8 9分为一组继续上述的比较,最终得到排序好的数组。</li>
</ul>
<h2 id="Java代码实现-1"><a href="#Java代码实现-1" class="headerlink" title="Java代码实现"></a>Java代码实现</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">quickSort</span><span class="params">(<span class="keyword">int</span>[] arr, <span class="keyword">int</span> start, <span class="keyword">int</span> end)</span> </span>{</div><div class="line"> <span class="keyword">if</span> (start >= end)</div><div class="line"> <span class="keyword">return</span>;</div><div class="line"></div><div class="line"> <span class="keyword">int</span> mid = arr[end];</div><div class="line"> <span class="keyword">int</span> left = start;</div><div class="line"> <span class="keyword">int</span> right = end - <span class="number">1</span>;</div><div class="line"></div><div class="line"> <span class="keyword">while</span> (left < right) {</div><div class="line"> <span class="keyword">while</span> (arr[left] <= mid && left < right) {</div><div class="line"> left++;</div><div class="line"> }</div><div class="line"> <span class="keyword">while</span> (arr[right] >= mid && left < right) {</div><div class="line"> right--;</div><div class="line"> }</div><div class="line"> swap(arr, left, right);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">if</span> (arr[left] >= arr[end]) {</div><div class="line"> swap(arr, left, end);</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> left++;</div><div class="line"> }</div><div class="line"> quickSort(arr, start, left - <span class="number">1</span>);</div><div class="line"> quickSort(arr, left + <span class="number">1</span>, end);</div><div class="line"></div><div class="line"> print(arr);</div><div class="line"> }</div><div class="line"> </div><div class="line"> </div><div class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">swap</span><span class="params">(<span class="keyword">int</span>[] arr, <span class="keyword">int</span> x, <span class="keyword">int</span> y)</span> </span>{</div><div class="line"> <span class="keyword">int</span> temp = arr[x];</div><div class="line"> arr[x] = arr[y];</div><div class="line"> arr[y] = temp;</div><div class="line"> }</div></pre></td></tr></table></figure>
<h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>可以看出分治法的策略还是递归的去解决问题,基本分为三个步骤: </p>
<p>分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题; </p>
<p>解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题; </p>
<p>合并:将各个子问题的解合并为原问题的解。 </p>
]]></content>
<summary type="html">
<h1 id="分治法"><a href="#分治法" class="headerlink" title="分治法"></a>分治法</h1><blockquote>
<p>分治法是一种很重要的算法,也就是“分而治之”的意思,就是把一个复杂的问题分解成两个或者多个相似的子问题,直
</summary>
<category term="算法" scheme="http://ibat.xyz/categories/%E7%AE%97%E6%B3%95/"/>
</entry>
<entry>
<title>单例模式--双重检验锁真的线程安全吗</title>
<link href="http://ibat.xyz/2017/03/10/%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F--%E5%8F%8C%E9%87%8D%E6%A3%80%E9%AA%8C%E9%94%81%E7%9C%9F%E7%9A%84%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E5%90%97/"/>
<id>http://ibat.xyz/2017/03/10/单例模式--双重检验锁真的线程安全吗/</id>
<published>2017-03-10T14:56:48.000Z</published>
<updated>2017-03-10T17:19:57.000Z</updated>
<content type="html"><![CDATA[<h1 id="单例模式–双重检验锁真的线程安全吗"><a href="#单例模式–双重检验锁真的线程安全吗" class="headerlink" title="单例模式–双重检验锁真的线程安全吗"></a>单例模式–双重检验锁真的线程安全吗</h1><p>单例模式是我们最熟悉不过的一种设计模式,用来保证内存中只有一个对象的实例。虽然容易,但里面的坑也有很多,比如双重检验锁模式(double checked locking pattern)真的是线程安全的吗? </p>
<h2 id="起因"><a href="#起因" class="headerlink" title="起因"></a>起因</h2><p>在对项目进行PMD静态代码检测时,遇到了这样一个问题 </p>
<blockquote>
<p>Partially created objects can be returned by the Double Checked Locking pattern when used in Java. An optimizing JRE may assign a reference to the baz variable before it calls the constructor of the object the reference points to. </p>
<p>Note: With Java 5, you can make Double checked locking work, if you declare the variable to be volatile.</p>
</blockquote>
<p>大概意思是,使用双重检验锁模式,可能会返回一个部分初始化的对象。可能大家有些疑虑,什么是部分初始化的对象,我们下面继续分析 </p>
<h2 id="什么是双重检验锁模式"><a href="#什么是双重检验锁模式" class="headerlink" title="什么是双重检验锁模式"></a>什么是双重检验锁模式</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Singleton <span class="title">getSingleton</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">if</span> (instance == <span class="keyword">null</span>) { </div><div class="line"> <span class="keyword">synchronized</span> (Singleton.class) {</div><div class="line"> <span class="keyword">if</span> (instance == <span class="keyword">null</span>) { </div><div class="line"> instance = <span class="keyword">new</span> Singleton();</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> instance ;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>我们看到,在同步代码块的内部和外部都判断了instance == null,这时因为,可能会有多个线程同时进入到同步代码块外的if判断中,如果在同步代码块内部不进行判空的话,可能会初始化多个实例。 </p>
<h2 id="问题所在"><a href="#问题所在" class="headerlink" title="问题所在"></a>问题所在</h2><p>这种写法看似完美无缺,但它却是有问题的,或者说它并不担保一定完美无缺。主要原因在于instance = new Singleton();并不是原子性的操作。<br>创建一个对象可以分为三部: </p>
<pre><code>1.分配对象的内存空间
2.初始化对象
3.设置instance指向刚分配的内存地址
当instance指向分配地址时,instance不为空
</code></pre><p>但是,2、3部之间,可能会被重排序,造成创建对象顺序变为1-3-2.试想一个场景:<br>线程A第一次创建对象Singleton,对象创建顺序为1-3-2;<br>当给instance分配完内存后,这时来了一个线程B调用了getSingleton()方法<br>这时候进行instance == null的判断,发现instance并不为null。<br>但注意这时候instance并没有初始化对象,线程B则会将这个未初始化完成的对象返回。那B线程使用instance时就可能会出现问题,这就是双重检查锁问题所在。</p>
<h2 id="使用volatile"><a href="#使用volatile" class="headerlink" title="使用volatile"></a>使用volatile</h2><p>对于上述的问题,我们可以通过把instance声明为volatile型来解决 </p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Singleton</span></span>{</div><div class="line"> <span class="keyword">private</span> <span class="keyword">volatile</span> <span class="keyword">static</span> Singleton instance;</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Singleton <span class="title">getSingleton</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">if</span> (instance == <span class="keyword">null</span>) { </div><div class="line"> <span class="keyword">synchronized</span> (Singleton.class) {</div><div class="line"> <span class="keyword">if</span> (instance == <span class="keyword">null</span>) { </div><div class="line"> instance = <span class="keyword">new</span> Singleton();</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> instance ;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>但是必须在JDK5版本以上使用。</p>
<h2 id="静态内部类"><a href="#静态内部类" class="headerlink" title="静态内部类"></a>静态内部类</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Singleton</span> </span>{ </div><div class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">SingletonHolder</span> </span>{ </div><div class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> Singleton INSTANCE = <span class="keyword">new</span> Singleton(); </div><div class="line"> } </div><div class="line"> <span class="function"><span class="keyword">private</span> <span class="title">Singleton</span> <span class="params">()</span></span>{} </div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> Singleton <span class="title">getInstance</span><span class="params">()</span> </span>{ </div><div class="line"> <span class="keyword">return</span> SingletonHolder.INSTANCE; </div><div class="line"> } </div><div class="line">}</div></pre></td></tr></table></figure>
<p>这种写法是目前比较推荐的一种写法,采用静态内部类的方式,即实现了懒加载又不会出现线程安全问题。而且减少了synchronized的开销。</p>
<h2 id="Learn-more"><a href="#Learn-more" class="headerlink" title="Learn more"></a>Learn more</h2><p><a href="http://www.infoq.com/cn/articles/double-checked-locking-with-delay-initialization" target="_blank" rel="external">双重检查锁定与延迟初始化</a><br><a href="https://pmd.github.io/pmd-5.5.4/pmd-java/rules/java/basic.html" target="_blank" rel="external">PMD-DoubleCheckedLocking</a><br><a href="http://www.javaworld.com/article/2074979/java-concurrency/double-checked-locking--clever--but-broken.html" target="_blank" rel="external">Double-checked locking: Clever, but broken</a></p>
]]></content>
<summary type="html">
<h1 id="单例模式–双重检验锁真的线程安全吗"><a href="#单例模式–双重检验锁真的线程安全吗" class="headerlink" title="单例模式–双重检验锁真的线程安全吗"></a>单例模式–双重检验锁真的线程安全吗</h1><p>单例模式是我们最熟悉
</summary>
<category term="Android" scheme="http://ibat.xyz/categories/Android/"/>
</entry>
<entry>
<title>浅谈HashMap中的hash算法</title>
<link href="http://ibat.xyz/2017/02/16/%E6%B5%85%E8%81%8AHashMap%E4%B8%AD%E7%9A%84hash%E7%AE%97%E6%B3%95/"/>
<id>http://ibat.xyz/2017/02/16/浅聊HashMap中的hash算法/</id>
<published>2017-02-16T14:19:15.000Z</published>
<updated>2017-03-05T08:40:39.000Z</updated>
<content type="html"><![CDATA[<h2 id="浅谈HashMap中的hash算法"><a href="#浅谈HashMap中的hash算法" class="headerlink" title="浅谈HashMap中的hash算法"></a>浅谈HashMap中的hash算法</h2><p>HashMap是我们常见的一种数据结构,实现Map接口,用来存储键值对,允许null键/值、非同步、不保证有序(比如插入的顺序)。那HashMap中最核心的部分就是哈希函数,又称散列函数。也就是说,哈希函数是通过把key的hash值映射到数组中的一个位置来进行访问。比如: </p>
<pre><code>存在一组哈希值 10,13,7,5,4,20
存在一个长度为10的数组 arrays
定义一个hash函数 int index = h % arrays.length;
10 % 10 = 0 那么 哈希值为10的对象放在数组索引为0的位置上;
13 % 10 = 3 那么 哈希值为13的对象放在数组索引为3的位置上;
......
20 % 10 = 0 那么 哈希值为13的对象放在数组索引为0的位置上;
</code></pre><p>这时候大家看出了一个问题,哈希值为10的对象和哈希值为20的对象,放在了一个索引上。发生了碰撞,那么怎么解决这样碰撞呢,有很多种方式,这里不展开叙述。HashMap中维护了一个链表组成的数组。如果冲突的话就添加到链表中,下面来看下hashmap中的hash算法,以Java8源码为例。</p>
<pre><code>static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
</code></pre><p>其中,key.hashCode()是Key自带的hashCode()方法,返回一个int类型的散列值。我们大家知道,32位带符号的int表值范围从-2147483648到2147483648。这样只要hash函数松散的话,一般是很难发生碰撞的,因为HashMap的初始容量只有16。但是这样的散列值我们是不能直接拿来用的。用之前需要对数组的长度取模运算。得到余数才是索引值。我们来看下HashMap中怎么实现的。 </p>
<pre><code>int index = hash & (arrays.length-1);
</code></pre><p>那么这也就明白了为什么HashMap的数组长度是2的整数幂。比如以初始长度为16为例,16-1 = 15,15的二进制数位00000000 00000000 00001111。可以看出一个基数二进制最后一位必然位1,当与一个hash值进行与运算时,最后一位可能是0也可能是1。但偶数与一个hash值进行与运算最后一位必然为0,造成有些位置永远映射不上值。<br>但是这时,又出现了一个问题,即使散列函数很松散,但只取最后几位碰撞也会很严重。这时候hash算法的价值就体现出来了,<br><img src="/img/hash算法1.png" alt="扰动函数"><br>hashCode右移16位,正好是32bit的一半。与自己本身做异或操作(相同为0,不同为1)。就是为了混合哈希值的高位和地位,增加低位的随机性。并且混合后的值也变相保持了高位的特征。</p>
<p>HashMap中用到的编码思想确实很值得我们学习。HashMap在Java1.8后又进行了优化,比如引入红黑树的数据结构和扩容的优化等。有机会我们再结合Java1.8聊聊,HashMap get()和put()实现原理,装载因子,resize()方法还有红黑树等。</p>
]]></content>
<summary type="html">
<h2 id="浅谈HashMap中的hash算法"><a href="#浅谈HashMap中的hash算法" class="headerlink" title="浅谈HashMap中的hash算法"></a>浅谈HashMap中的hash算法</h2><p>HashMap是我们
</summary>
<category term="Android" scheme="http://ibat.xyz/categories/Android/"/>
</entry>
<entry>
<title>TextView进阶用法</title>
<link href="http://ibat.xyz/2017/02/06/TextView%E8%BF%9B%E9%98%B6%E7%94%A8%E6%B3%95/"/>
<id>http://ibat.xyz/2017/02/06/TextView进阶用法/</id>
<published>2017-02-06T15:30:15.000Z</published>
<updated>2017-02-07T14:04:04.000Z</updated>
<content type="html"><![CDATA[<h1 id="TextView进阶用法"><a href="#TextView进阶用法" class="headerlink" title="TextView进阶用法"></a>TextView进阶用法</h1><p>在Android中TextView是我们最熟悉不过的控件了,但是它的进阶用法你知道多少呢?下面就来聊一聊TextView的进阶用法。</p>
<h3 id="1-显示多种颜色"><a href="#1-显示多种颜色" class="headerlink" title="1.显示多种颜色"></a>1.显示多种颜色</h3><p>在做项目中经常会遇到一行字显示多种颜色的需求。用两个或多个TextView也可以实现,但这样比较费事,而且可能还要多一级布局嵌套。接下来我们需要用到SpannableStringBuilder这个类。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"></div><div class="line">TextView textView = (TextView) findViewById(R.id.textview);</div><div class="line"></div><div class="line">String text = <span class="string">"TextView进阶用法"</span>;</div><div class="line"></div><div class="line">SpannableStringBuilder ssb = <span class="keyword">new</span> SpannableStringBuilder(text);</div><div class="line">ssb.setSpan(<span class="keyword">new</span> ForegroundColorSpan(Color.RED),<span class="number">0</span>,<span class="number">8</span>, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);</div><div class="line">ssb.setSpan(<span class="keyword">new</span> ForegroundColorSpan(Color.GREEN),<span class="number">8</span>,text.length(),Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);</div><div class="line"></div><div class="line">textView.setText(ssb);</div></pre></td></tr></table></figure>
<p>效果如下:<br><img src="/img/TextView进阶用法-1.png" alt="textview多种颜色"><br>SpannableStringBuilder类实现了CharSequence接口,所以可以直接通过setText()设置值,它更像一个StringBuilder,同时也有append()方法。这样的话无论要指定多少颜色,都可以通过setSpan()来根据字符串的索引长度来解决。<br>注意: </p>
<pre><code>1.参数start为闭区间,end为开区间。即本例中的0,8实际代表从0~7的长度。
2.int flags:取值有如下四个
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE:前后都不包括,即在指定范围的前面和后面插入新字符都不会应用新样式
Spannable.SPAN_EXCLUSIVE_INCLUSIVE:前面不包括,后面包括。即仅在范围字符的后面插入新字符时会应用新样式
Spannable.SPAN_INCLUSIVE_EXCLUSIVE:前面包括,后面不包括。
Spannable.SPAN_INCLUSIVE_INCLUSIVE:前后都包括。
大家可以自己试下取不同flags时的效果,可以用EditText试验。
</code></pre><h3 id="2-显示不同大小"><a href="#2-显示不同大小" class="headerlink" title="2.显示不同大小"></a>2.显示不同大小</h3><p>将ForegroundColorSpan改为AbsoluteSizeSpan。 </p>
<pre><code>ssb.setSpan(new AbsoluteSizeSpan(80),0,8,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
</code></pre><h3 id="3-删除线(StrikethroughSpan)"><a href="#3-删除线(StrikethroughSpan)" class="headerlink" title="3.删除线(StrikethroughSpan)"></a>3.删除线(StrikethroughSpan)</h3><pre><code>ssb.setSpan(new StrikethroughSpan(),0,8,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
</code></pre><h3 id="4-下划线(UnderlineSpan)"><a href="#4-下划线(UnderlineSpan)" class="headerlink" title="4.下划线(UnderlineSpan)"></a>4.下划线(UnderlineSpan)</h3><pre><code>ssb.setSpan(new UnderlineSpan(),0,8,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
</code></pre><h3 id="5-加粗斜体(StyleSpan)"><a href="#5-加粗斜体(StyleSpan)" class="headerlink" title="5.加粗斜体(StyleSpan)"></a>5.加粗斜体(StyleSpan)</h3><pre><code>粗体:
ssb.setSpan(new StyleSpan(Typeface.BOLD),0,8,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
斜体:
ssb.setSpan(new StyleSpan(Typeface.ITALIC),0,8,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
</code></pre><h3 id="6-设置点击事件"><a href="#6-设置点击事件" class="headerlink" title="6.设置点击事件"></a>6.设置点击事件</h3><pre><code>ssb.setSpan(new ClickableSpan() {
@Override
public void onClick(View widget) {
Toast.makeText(getApplicationContext(),"被点击了",Toast.LENGTH_SHORT).show();
}
},0,8,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
//须调用此方法,不然点击无效
textView.setMovementMethod(LinkMovementMethod.getInstance());
</code></pre><p>最终效果如下:<br><img src="/img/TextView进阶用法-2.png" alt="textview多种颜色"> </p>
<p>当然,如果你想及设置点击又设置颜色或下划线的话,ClickableSpan也提供了一个回调方法来设置颜色下划线等属性。</p>
<pre><code>@Override
public void updateDrawState(TextPaint ds) {
ds.setColor(Color.RED);
ds.setUnderlineText(true);
}
</code></pre><h3 id="附上完整代码"><a href="#附上完整代码" class="headerlink" title="附上完整代码"></a>附上完整代码</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div></pre></td><td class="code"><pre><div class="line">TextView textView = (TextView) findViewById(R.id.textview);</div><div class="line"></div><div class="line"> String text = <span class="string">"TextView进阶用法"</span>;</div><div class="line"></div><div class="line"> SpannableStringBuilder ssb = <span class="keyword">new</span> SpannableStringBuilder(text);</div><div class="line"> ssb.setSpan(<span class="keyword">new</span> ForegroundColorSpan(Color.RED),<span class="number">0</span>,<span class="number">8</span>, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);</div><div class="line"> ssb.setSpan(<span class="keyword">new</span> ForegroundColorSpan(Color.GREEN),<span class="number">8</span>,text.length(),Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);</div><div class="line"> ssb.setSpan(<span class="keyword">new</span> AbsoluteSizeSpan(<span class="number">80</span>),<span class="number">0</span>,<span class="number">8</span>,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);</div><div class="line"> ssb.setSpan(<span class="keyword">new</span> StrikethroughSpan(),<span class="number">0</span>,<span class="number">8</span>,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);</div><div class="line"> ssb.setSpan(<span class="keyword">new</span> UnderlineSpan(),<span class="number">0</span>,<span class="number">8</span>,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);</div><div class="line"> ssb.setSpan(<span class="keyword">new</span> StyleSpan(Typeface.BOLD),<span class="number">0</span>,<span class="number">8</span>,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);</div><div class="line"> ssb.setSpan(<span class="keyword">new</span> StyleSpan(Typeface.ITALIC),<span class="number">0</span>,<span class="number">8</span>,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);</div><div class="line"></div><div class="line"> ssb.setSpan(<span class="keyword">new</span> ClickableSpan() {</div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onClick</span><span class="params">(View widget)</span> </span>{</div><div class="line"> Toast.makeText(getApplicationContext(),<span class="string">"被点击了"</span>,Toast.LENGTH_SHORT).show();</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">updateDrawState</span><span class="params">(TextPaint ds)</span> </span>{</div><div class="line"> ds.setColor(Color.RED);</div><div class="line"> ds.setUnderlineText(<span class="keyword">true</span>);</div><div class="line"> }</div><div class="line"></div><div class="line"> },<span class="number">0</span>,<span class="number">8</span>,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);</div><div class="line"></div><div class="line"> <span class="comment">//须调用此方法,不然点击无效</span></div><div class="line"> textView.setMovementMethod(LinkMovementMethod.getInstance());</div><div class="line"> textView.setText(ssb);</div></pre></td></tr></table></figure>
<p>这样就不用担心一段话要写多个TextView的问题了。一个TextView统统搞定!</p>
]]></content>
<summary type="html">
<h1 id="TextView进阶用法"><a href="#TextView进阶用法" class="headerlink" title="TextView进阶用法"></a>TextView进阶用法</h1><p>在Android中TextView是我们最熟悉不过的控件了,
</summary>
<category term="Android" scheme="http://ibat.xyz/categories/Android/"/>
</entry>
<entry>
<title>2017年,我的小目标</title>
<link href="http://ibat.xyz/2017/02/05/2017%E5%B9%B4%EF%BC%8C%E6%88%91%E7%9A%84%E5%B0%8F%E7%9B%AE%E6%A0%87/"/>
<id>http://ibat.xyz/2017/02/05/2017年,我的小目标/</id>
<published>2017-02-05T13:34:21.000Z</published>
<updated>2017-03-05T07:06:53.000Z</updated>
<content type="html"><![CDATA[<p>2016,一路平淡的走过。仔细想想,虽然做了很多项目,也涉猎了几本技术书籍,和一些用到的新技术,但并没有更深入的了解,更没有一个模块化的知识体系。所以是时候给自己定个目标了,让2017年更加充实。 </p>
<ul>
<li><p>Java</p>
<ul>
<li>Java编程思想,目前看了一小半,今年争取把这本神书看完</li>
<li>Effective Java </li>
</ul>
</li>
<li><p>数据结构与算法</p>
<ul>
<li><a href="http://open.163.com/special/opencourse/algorithms.html" target="_blank" rel="external">麻省理工学院公开课:算法导论</a></li>
<li>数据结构与算法-Java语言描述</li>
</ul>
</li>
<li><p>Android </p>
<ul>
<li>将现有知识再进行深度扩展,了解更低层的一些东西。</li>
<li>插件化开发,热更新原理及实践。</li>
<li>坚持看鸿洋和郭霖的技术公众号,积累技术干货。</li>
<li>掌握Android技术新动向</li>
</ul>
</li>
<li><p>其他</p>
<ul>
<li>至少1500个英语单词</li>
<li>看两本以上非技术书籍</li>
<li>GitHub上有一个上百Star的项目</li>
<li>每个月至少两篇技术博客</li>
<li>了解下机器学习人工智能方向的知识,这个可能是以后的热点。 </li>
</ul>
</li>
</ul>
<p>先暂时写这么多吧,一年后看看自己的成果。每做完一件事更新下状态,并标注日期。激励下自己,每天进步一点点。(Ps.地铁上是一段可以很好利用的时间,与其看新闻,不如背单词或者看看技术文章)。</p>
]]></content>
<summary type="html">
<p>2016,一路平淡的走过。仔细想想,虽然做了很多项目,也涉猎了几本技术书籍,和一些用到的新技术,但并没有更深入的了解,更没有一个模块化的知识体系。所以是时候给自己定个目标了,让2017年更加充实。 </p>
<ul>
<li><p>Java</p>
<ul>
<li>J
</summary>
<category term="学习笔记" scheme="http://ibat.xyz/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
</entry>
<entry>
<title>理解RSA算法</title>
<link href="http://ibat.xyz/2016/08/23/%E7%90%86%E8%A7%A3RSA%E7%AE%97%E6%B3%95/"/>
<id>http://ibat.xyz/2016/08/23/理解RSA算法/</id>
<published>2016-08-23T13:34:21.000Z</published>
<updated>2019-01-17T02:58:46.860Z</updated>
<content type="html"><![CDATA[<h1 id="理解RSA算法"><a href="#理解RSA算法" class="headerlink" title="理解RSA算法"></a>理解RSA算法</h1><blockquote>
<p>本文是看完阮一峰的”RSA算法原理”后所做的笔记,有兴趣的同学可以移步至:<a href="http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html" target="_blank" rel="external">RSA算法原理–阮一峰</a></p>
<h2 id="一-简介"><a href="#一-简介" class="headerlink" title="一.简介"></a>一.简介</h2><p><strong>非对称加密算法:非对称加密需要两个密钥,公钥和私钥.</strong><br>1.乙方生成两把密钥(公钥和私钥).公钥是公开的,任何人都可以获得,私钥是保密的<br>2.甲方获取乙方的公钥,然后用它对信息加密<br>3.乙方得到加密后的信息,用私钥解密 </p>
</blockquote>
<p>如果公钥加密的信息只有私钥解得开,只要私钥不泄露,通信就是安全的.</p>
<h2 id="二-欧拉函数"><a href="#二-欧拉函数" class="headerlink" title="二.欧拉函数"></a>二.欧拉函数</h2><p><a href="https://zh.wikipedia.org/wiki/%E6%AC%A7%E6%8B%89%E5%87%BD%E6%95%B0" target="_blank" rel="external">欧拉函数</a>,在数论中,对正整数n,欧拉函数φ(n)是小于或等于n的正整数中与n互质的数的数目.例如:φ(8) = 4,因为1,3,5,7均与8互质. </p>
<p>通式:<br><img src="/img/rsatongshi.jpeg" alt="欧拉函数公式(1)"><br>其中p1,p2….pn为x的所有质因数,x为不为0的整数.注意:<strong>每种质因数只一个</strong>。<br>比如:12 = 2×2×3 那么 φ(12)=12<em>(1-1/2)</em>(1-1/3)=4 </p>
<h3 id="第一种情况"><a href="#第一种情况" class="headerlink" title="第一种情况"></a>第一种情况</h3><p>如果n=1,则 φ(1) = 1 。因为1与任何数(包括自身)都构成互质关系。</p>
<h3 id="第二种情况"><a href="#第二种情况" class="headerlink" title="第二种情况"></a>第二种情况</h3><p>如果n是质数,则 φ(n)=n-1 。因为质数与小于它的每一个数,都构成互质关系。比如5与1、2、3、4都构成互质关系。</p>
<h3 id="第三种情况"><a href="#第三种情况" class="headerlink" title="第三种情况"></a>第三种情况</h3><p>如果n是质数的某一个次方,即 n = p^k (p为质数,k为大于等于1的整数),则<br><img src="/img/rsa3.jpeg" alt="欧拉函数公式(2)"><br>这是因为只有当一个数不包含质数p,才可能与n互质。而包含质数p的数一共有p^(k-1)个,即1×p、2×p、3×p、…、p^(k-1)×p,把它们去除,剩下的就是与n互质的数。</p>
<h3 id="第四种情况"><a href="#第四种情况" class="headerlink" title="第四种情况"></a>第四种情况</h3><p>如果n可以分解成两个互质的整数之积<br><strong>n = p1 × p2</strong> 则 <strong>φ(n) = φ(p1p2) = φ(p1)φ(p2)</strong></p>
<h2 id="三-欧拉定理"><a href="#三-欧拉定理" class="headerlink" title="三.欧拉定理"></a>三.欧拉定理</h2><p>如果两个正整数a和n互质,则n的欧拉函数 φ(n) 可以让下面的等式成立:<br><img src="/img/ouladingli.jpeg" alt="欧拉定理"><br>也就是说,a的φ(n)次方被n除的余数为1。或者说,a的φ(n)次方减去1,可以被n整除。比如,3和7互质,而7的欧拉函数φ(7)等于6,所以3的6次方(729)减去1,可以被7整除(728/7=104)。<br>欧拉定理有一个特殊情况。<br>假设正整数a与质数p互质,因为质数p的φ(p)等于p-1,则欧拉定理可以写成<br><img src="/img/ouladingli2.jpeg" alt="欧拉定理"> </p>
<h2 id="四-模反元素"><a href="#四-模反元素" class="headerlink" title="四.模反元素"></a>四.模反元素</h2><p>如果两个正整数a和n互质,那么一定可以找到整数b,使得 ab-1 被n整除,或者说ab被n除的余数是1。</p>
<blockquote>
<p>###<strong>ab ≡ 1( mod n )</strong> </p>
</blockquote>
<p>这时,b就叫做a的”模反元素”。<br>比如,3和11互质,那么3的模反元素就是4,因为 (3 × 4)-1 可以被11整除。显然,模反元素不止一个, 4加减11的整数倍都是3的模反元素 {…,-18,-7,4,15,26,…},即如果b是a的模反元素,则 b+kn 都是a的模反元素。</p>
<h2 id="五-RSA算法原理"><a href="#五-RSA算法原理" class="headerlink" title="五.RSA算法原理"></a>五.RSA算法原理</h2><h3 id="1-密钥生成步骤"><a href="#1-密钥生成步骤" class="headerlink" title="1.密钥生成步骤"></a>1.密钥生成步骤</h3><p>比如,老张和老王是两名地下工作者,老张要向老王传达一个机密的文件.这时老张想到了RSA算法.<br><strong>(1) 随机选择两个不相等的质数p,q.</strong><br>这时,老张选择了61和53.<br><strong>(2) 计算p和q的乘积n.</strong> </p>
<pre><code>n = p * q = 61 * 53 = 3233.
</code></pre><p>n的长度就是密钥长度。3233写成二进制是110010100001,一共有12位,所以这个密钥就是12位。实际应用中,RSA密钥一般是1024位,重要场合则为2048位。<br><strong>(3) 计算n的欧拉函数φ(n).</strong><br>根据上面所介绍的欧拉定理第四种情况:</p>
<pre><code>φ(n) = φ(3233) = φ(61) * φ(53) = 60 * 52 = 3120;
</code></pre><p><strong>(4) 随机选择一个整数e,条件是1< e < φ(n),且e与φ(n) 互质.</strong><br>这时,老张从1-3120之间,随机选择了17.(实际应用中,常常选择65537).<br><strong>(5) 计算e对于φ(n)的模反元素d</strong><br>所谓”模反元素”就是指有一个整数d,可以使得ed被φ(n)除的余数为1。</p>
<pre><code>ed ≡ 1 (mod φ(n))
</code></pre><p>这个公式等价于 </p>