-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
1874 lines (1087 loc) · 157 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
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
<!doctype html>
<html class="theme-next pisces use-motion" lang="zh-CN">
<head>
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
<link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />
<link href="//fonts.googleapis.com/css?family=Lato:300,300italic,400,400italic,700,700italic&subset=latin,latin-ext" rel="stylesheet" type="text/css">
<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />
<link href="/css/main.css?v=5.1.0" rel="stylesheet" type="text/css" />
<meta name="keywords" content="Hexo, NexT, Android" />
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico?v=5.1.0" />
<meta property="og:type" content="website">
<meta property="og:title" content="Android背锅侠">
<meta property="og:url" content="http://yoursite.com/index.html">
<meta property="og:site_name" content="Android背锅侠">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Android背锅侠">
<script type="text/javascript" id="hexo.configurations">
var NexT = window.NexT || {};
var CONFIG = {
root: '/',
scheme: 'Pisces',
sidebar: {"position":"left","display":"post"},
fancybox: true,
motion: true,
duoshuo: {
userId: '0',
author: 'Author'
},
algolia: {
applicationID: '',
apiKey: '',
indexName: '',
hits: {"per_page":10},
labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
}
};
</script>
<link rel="canonical" href="http://yoursite.com/"/>
<title> Android背锅侠 </title>
</head>
<body itemscope itemtype="http://schema.org/WebPage" lang="zh-CN">
<div class="container one-collumn sidebar-position-left
page-home
">
<div class="headband"></div>
<header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
<div class="header-inner"><div class="site-meta ">
<div class="custom-logo-site-title">
<a href="/" class="brand" rel="start">
<span class="logo-line-before"><i></i></span>
<span class="site-title">Android背锅侠</span>
<span class="logo-line-after"><i></i></span>
</a>
</div>
<p class="site-subtitle"></p>
</div>
<div class="site-nav-toggle">
<button>
<span class="btn-bar"></span>
<span class="btn-bar"></span>
<span class="btn-bar"></span>
</button>
</div>
<nav class="site-nav">
<ul id="menu" class="menu">
<li class="menu-item menu-item-home">
<a href="/" rel="section">
<i class="menu-item-icon fa fa-fw fa-home"></i> <br />
Startseite
</a>
</li>
<li class="menu-item menu-item-archives">
<a href="/archives" rel="section">
<i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
Archiv
</a>
</li>
<li class="menu-item menu-item-tags">
<a href="/tags" rel="section">
<i class="menu-item-icon fa fa-fw fa-tags"></i> <br />
Tags
</a>
</li>
</ul>
</nav>
</div>
</header>
<main id="main" class="main">
<div class="main-inner">
<div class="content-wrap">
<div id="content" class="content">
<section id="posts" class="posts-expand">
<article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
<link itemprop="mainEntityOfPage" href="http://yoursite.com/2017/02/04/RecyclerView的下拉刷新与上拉加载更多/">
<span style="display:none" itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="XiaoFeng">
<meta itemprop="description" content="">
<meta itemprop="image" content="/images/avatar.gif">
</span>
<span style="display:none" itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="Android背锅侠">
<span style="display:none" itemprop="logo" itemscope itemtype="http://schema.org/ImageObject">
<img style="display:none;" itemprop="url image" alt="Android背锅侠" src="">
</span>
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
<a class="post-title-link" href="/2017/02/04/RecyclerView的下拉刷新与上拉加载更多/" itemprop="url">
RecyclerView的下拉刷新与上拉加载更多
</a>
</h1>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">Veröffentlicht am</span>
<time title="Post created" itemprop="dateCreated datePublished" datetime="2017-02-04T00:46:28+08:00">
2017-02-04
</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<p><strong>下拉刷新</strong>和<strong>上拉加载更多</strong>,从设计层面上来说,就不是同一个层面上的,从Google官方推出的下拉刷新控件SwipeRefreshLayout就可以看出。<br>一般来说,<strong>下拉刷新</strong>是以包裹整个列表控件的容器来实现的,而<strong>上拉加载更多</strong>是以列表控件的一部分扩展(footerView)来实现的。</p>
<h4 id="下拉刷新"><a href="#下拉刷新" class="headerlink" title="下拉刷新"></a>下拉刷新</h4><ol>
<li>Google官方的SwipeRefreshLayout<br>xml中控件初始化:<figure class="highlight stylus"><table><tr><td class="code"><pre><div class="line"><android<span class="selector-class">.support</span><span class="selector-class">.v4</span><span class="selector-class">.widget</span><span class="selector-class">.SwipeRefreshLayout</span></div><div class="line"> android:layout_width=<span class="string">"match_parent"</span></div><div class="line"> android:layout_height=<span class="string">"match_parent"</span>></div><div class="line"></div><div class="line"> <android<span class="selector-class">.support</span><span class="selector-class">.v7</span><span class="selector-class">.widget</span><span class="selector-class">.RecyclerView</span></div><div class="line"> android:id=<span class="string">"@+id/recyclerView"</span></div><div class="line"> android:layout_width=<span class="string">"match_parent"</span></div><div class="line"> android:layout_height=<span class="string">"match_parent"</span>/></div><div class="line"></div><div class="line"></android<span class="selector-class">.support</span><span class="selector-class">.v4</span><span class="selector-class">.widget</span><span class="selector-class">.SwipeRefreshLayout</span>></div></pre></td></tr></table></figure>
</li>
</ol>
<p>java类中实现<br><figure class="highlight processing"><table><tr><td class="code"><pre><div class="line"><span class="comment">// 设置下拉出现小圆圈是否是缩放出现,出现的位置,最大的下拉位置</span></div><div class="line">mySwipeRefreshLayout.setProgressViewOffset(<span class="keyword">true</span>, <span class="number">50</span>, <span class="number">200</span>);</div><div class="line"></div><div class="line"><span class="comment">// 设置下拉圆圈的大小,两个值 LARGE, DEFAULT</span></div><div class="line">mySwipeRefreshLayout.setSize(SwipeRefreshLayout.LARGE);</div><div class="line"></div><div class="line"><span class="comment">// 设置下拉圆圈上的颜色,蓝色、绿色、橙色、红色</span></div><div class="line">mySwipeRefreshLayout.setColorSchemeResources(</div><div class="line"> android.R.<span class="built_in">color</span>.holo_blue_bright,</div><div class="line"> android.R.<span class="built_in">color</span>.holo_green_light,</div><div class="line"> android.R.<span class="built_in">color</span>.holo_orange_light,</div><div class="line"> android.R.<span class="built_in">color</span>.holo_red_light);</div><div class="line"></div><div class="line"><span class="comment">// 通过 setEnabled(false) 禁用下拉刷新</span></div><div class="line">mySwipeRefreshLayout.setEnabled(<span class="keyword">false</span>);</div><div class="line"></div><div class="line"><span class="comment">// 设定下拉圆圈的背景</span></div><div class="line">mSwipeLayout.setProgressBackgroundColor(R.<span class="built_in">color</span>.<span class="built_in">red</span>);</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line"> * 设置手势下拉刷新的监听</div><div class="line"> */</div><div class="line">mySwipeRefreshLayout.setOnRefreshListener(</div><div class="line"> <span class="keyword">new</span> SwipeRefreshLayout.OnRefreshListener() {</div><div class="line"> @Override</div><div class="line"> <span class="keyword">public</span> <span class="keyword">void</span> onRefresh() {</div><div class="line"> <span class="comment">// 刷新动画开始后回调到此方法</span></div><div class="line"> }</div><div class="line"> }</div><div class="line">);</div></pre></td></tr></table></figure></p>
<ol>
<li>使用第三方下拉刷新控件,可以更好地支持自定义下拉样式。<br>这里推荐一个比较好的第三方控件:<a href="https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh" target="_blank" rel="external">android-Ultra-Pull-To-Refresh</a><figure class="highlight maxima"><table><tr><td class="code"><pre><div class="line"><span class="built_in">compile</span> '<span class="keyword">in</span>.srain.<span class="built_in">cube</span>:ultra-ptr:<span class="number">1.0</span>.10'</div></pre></td></tr></table></figure>
</li>
</ol>
<p>xml中控件初始化:<br><figure class="highlight stylus"><table><tr><td class="code"><pre><div class="line"><<span class="keyword">in</span><span class="selector-class">.srain</span><span class="selector-class">.cube</span><span class="selector-class">.views</span><span class="selector-class">.ptr</span><span class="selector-class">.PtrFrameLayout</span></div><div class="line"> xmlns:app=<span class="string">"http://schemas.android.com/apk/res-auto"</span></div><div class="line"> android:id=<span class="string">"@+id/refreshView"</span></div><div class="line"> android:layout_width=<span class="string">"match_parent"</span></div><div class="line"> android:layout_height=<span class="string">"match_parent"</span></div><div class="line"> app:ptr_duration_to_close=<span class="string">"200"</span></div><div class="line"> app:ptr_duration_to_close_header=<span class="string">"1000"</span></div><div class="line"> app:ptr_keep_header_when_refresh=<span class="string">"true"</span></div><div class="line"> app:ptr_pull_to_fresh=<span class="string">"false"</span></div><div class="line"> app:ptr_ratio_of_header_height_to_refresh=<span class="string">"1.2"</span></div><div class="line"> app:ptr_resistance=<span class="string">"1.7"</span>></div><div class="line"></div><div class="line"> <com<span class="selector-class">.zdf</span><span class="selector-class">.zhihu</span><span class="selector-class">.view</span><span class="selector-class">.widget</span><span class="selector-class">.LoadMoreRecyclerView</span></div><div class="line"> android:id=<span class="string">"@+id/recyclerView"</span></div><div class="line"> android:layout_width=<span class="string">"match_parent"</span></div><div class="line"> android:layout_height=<span class="string">"wrap_content"</span>></div><div class="line"></div><div class="line"> </com<span class="selector-class">.zdf</span><span class="selector-class">.zhihu</span><span class="selector-class">.view</span><span class="selector-class">.widget</span><span class="selector-class">.LoadMoreRecyclerView</span>></div><div class="line"></div><div class="line"></<span class="keyword">in</span><span class="selector-class">.srain</span><span class="selector-class">.cube</span><span class="selector-class">.views</span><span class="selector-class">.ptr</span><span class="selector-class">.PtrFrameLayout</span>></div></pre></td></tr></table></figure></p>
<p>java类中实现:<br><figure class="highlight aspectj"><table><tr><td class="code"><pre><div class="line"><span class="keyword">private</span> <span class="function"><span class="keyword">void</span> <span class="title">initRefreshView</span><span class="params">()</span> </span>{</div><div class="line"> <span class="comment">// 这里可以完全自定义各种样式,只要实现PtrUIHandler,</span></div><div class="line"> <span class="comment">// 具体请参考官方文档也源码</span></div><div class="line"> MaterialHeader header = <span class="keyword">new</span> MaterialHeader(getContext());</div><div class="line"> refreshView.setHeaderView(header);</div><div class="line"> refreshView.addPtrUIHandler(header);</div><div class="line"> refreshView.setPtrHandler(<span class="keyword">new</span> PtrDefaultHandler() {</div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">void</span> <span class="title">onRefreshBegin</span><span class="params">(PtrFrameLayout ptrFrameLayout)</span> </span>{</div><div class="line"> page = <span class="number">0</span>;</div><div class="line"> requestTopMovies();</div><div class="line"> }</div><div class="line"> });</div><div class="line">}</div><div class="line"></div><div class="line"><span class="meta">@Override</span></div><div class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">void</span> <span class="title">autoRefresh</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">handler</span>.postDelayed(<span class="keyword">new</span> Runnable() {</div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</div><div class="line"> refreshView.autoRefresh();</div><div class="line"> }</div><div class="line"> }, <span class="number">100</span>);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="meta">@Override</span></div><div class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">void</span> <span class="title">refreshComplete</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">handler</span>.post(<span class="keyword">new</span> Runnable() {</div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</div><div class="line"> refreshView.refreshComplete();</div><div class="line"> }</div><div class="line"> });</div><div class="line">}</div></pre></td></tr></table></figure></p>
<h4 id="上拉加载更多"><a href="#上拉加载更多" class="headerlink" title="上拉加载更多"></a>上拉加载更多</h4><p>以列表控件的一部分扩展实现,这里以RecyclerView为例,新建一个类LoadMoreRecyclerView,继承至RecyclerView,主要的功能就是实现加载更多逻辑和显示控件:<br><figure class="highlight java"><table><tr><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * 集成了上拉加载更多的RecyclerView</div><div class="line"> *</div><div class="line"> * Created by XiaoFeng on 2017/2/3.</div><div class="line"> */</div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">LoadMoreRecyclerView</span> <span class="keyword">extends</span> <span class="title">RecyclerView</span> </span>{</div><div class="line"></div><div class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">int</span> STATE_NONE = <span class="number">0x00</span>;</div><div class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">int</span> STATE_LOADING = <span class="number">0x01</span>;</div><div class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">int</span> STATE_FAILURE = <span class="number">0x02</span>;</div><div class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">int</span> STATE_COMPLETE = <span class="number">0x03</span>;</div><div class="line"></div><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> <span class="keyword">int</span> VISIBLE_THRESHOLD = <span class="number">1</span>;</div><div class="line"></div><div class="line"> <span class="keyword">private</span> <span class="keyword">int</span> state;</div><div class="line"> <span class="keyword">private</span> <span class="keyword">boolean</span> loadMoreEnabled = <span class="keyword">true</span>;</div><div class="line"> <span class="keyword">private</span> OnLoadMoreListener listener;</div><div class="line"> <span class="keyword">private</span> SuperAdapter superAdapter;</div><div class="line"> <span class="keyword">private</span> LoadMoreView loadMoreView;</div><div class="line"> <span class="keyword">private</span> LinearLayoutManager layoutManager;</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="title">LoadMoreRecyclerView</span><span class="params">(Context context)</span> </span>{</div><div class="line"> <span class="keyword">this</span>(context, <span class="keyword">null</span>);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="title">LoadMoreRecyclerView</span><span class="params">(Context context, @Nullable AttributeSet attrs)</span> </span>{</div><div class="line"> <span class="keyword">this</span>(context, attrs, <span class="number">0</span>);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="title">LoadMoreRecyclerView</span><span class="params">(Context context, @Nullable AttributeSet attrs, <span class="keyword">int</span> defStyle)</span> </span>{</div><div class="line"> <span class="keyword">super</span>(context, attrs, defStyle);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">initLoadMore</span><span class="params">(@NonNull OnLoadMoreListener listener)</span> </span>{</div><div class="line"> <span class="keyword">this</span>.listener = listener;</div><div class="line"></div><div class="line"> <span class="keyword">if</span> (getAdapter() <span class="keyword">instanceof</span> SuperAdapter) {</div><div class="line"> superAdapter = (SuperAdapter) getAdapter();</div><div class="line"> }</div><div class="line"></div><div class="line"> loadMoreView = <span class="keyword">new</span> LoadMoreView(getContext());</div><div class="line"> layoutManager = (LinearLayoutManager) getLayoutManager();</div><div class="line"></div><div class="line"> addOnScrollListener(<span class="keyword">new</span> OnScrollListener() {</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">onScrollStateChanged</span><span class="params">(RecyclerView recyclerView, <span class="keyword">int</span> newState)</span> </span>{</div><div class="line"> <span class="keyword">super</span>.onScrollStateChanged(recyclerView, newState);</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">onScrolled</span><span class="params">(RecyclerView recyclerView, <span class="keyword">int</span> dx, <span class="keyword">int</span> dy)</span> </span>{</div><div class="line"> <span class="keyword">super</span>.onScrolled(recyclerView, dx, dy);</div><div class="line"></div><div class="line"> <span class="keyword">if</span> (canLoadMore() && dy >= <span class="number">0</span>) {</div><div class="line"> <span class="keyword">int</span> totalItemCount = layoutManager.getItemCount();</div><div class="line"> <span class="keyword">int</span> lastVisibleItem = layoutManager.findLastVisibleItemPosition();</div><div class="line"> <span class="keyword">if</span> (lastVisibleItem + VISIBLE_THRESHOLD >= totalItemCount) {</div><div class="line"> onLoadMore(recyclerView);</div><div class="line"> }</div><div class="line"> }</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">public</span> <span class="keyword">void</span> <span class="title">setLoadMoreEnabled</span><span class="params">(<span class="keyword">boolean</span> enabled)</span> </span>{</div><div class="line"> <span class="keyword">this</span>.loadMoreEnabled = enabled;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">canLoadMore</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">return</span> state != STATE_LOADING && loadMoreEnabled;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">onLoadMore</span><span class="params">(RecyclerView recyclerView)</span> </span>{</div><div class="line"> state = STATE_LOADING;</div><div class="line"></div><div class="line"> <span class="keyword">if</span> (loadMoreView != <span class="keyword">null</span>) {</div><div class="line"> loadMoreView.loading();</div><div class="line"> }</div><div class="line"></div><div class="line"> recyclerView.post(<span class="keyword">new</span> Runnable() {</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">run</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">if</span> (state == STATE_LOADING) {</div><div class="line"> <span class="keyword">if</span> (superAdapter != <span class="keyword">null</span> && loadMoreView != <span class="keyword">null</span>) {</div><div class="line"> superAdapter.addFooterView(loadMoreView);</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> });</div><div class="line"></div><div class="line"> <span class="keyword">if</span> (listener != <span class="keyword">null</span>) {</div><div class="line"> listener.onLoadMore();</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">loadMoreFailed</span><span class="params">()</span> </span>{</div><div class="line"> state = STATE_FAILURE;</div><div class="line"></div><div class="line"> <span class="keyword">if</span> (loadMoreView != <span class="keyword">null</span>) {</div><div class="line"> loadMoreView.failure();</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">if</span> (superAdapter != <span class="keyword">null</span>) {</div><div class="line"> superAdapter.removeFooterView();</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">loadMoreComplete</span><span class="params">()</span> </span>{</div><div class="line"> state = STATE_COMPLETE;</div><div class="line"></div><div class="line"> <span class="keyword">if</span> (loadMoreView != <span class="keyword">null</span>) {</div><div class="line"> loadMoreView.complete();</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">if</span> (superAdapter != <span class="keyword">null</span>) {</div><div class="line"> superAdapter.removeFooterView();</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>上面出现的SuperAdapter是使用了一个第三方封装的<strong><a href="https://github.com/byteam/SuperAdapter" target="_blank" rel="external">万能Adapter</a></strong>,同时支持ListView和RecyclerView,个人感觉比<a href="https://github.com/hongyangAndroid/baseAdapter" target="_blank" rel="external">鸿洋大神封装的万能Adapter</a>要好一些,但是基本原理都是一样的。原理可以看他们各自的文章介绍,已经写的很详细了,这里就不多说了。</p>
<blockquote>
<p><a href="http://www.jianshu.com/p/d6a76fd3ea5b" target="_blank" rel="external">http://www.jianshu.com/p/d6a76fd3ea5b</a><br><a href="http://blog.csdn.net/lmj623565791/article/details/38902805" target="_blank" rel="external">http://blog.csdn.net/lmj623565791/article/details/38902805</a><br><a href="http://blog.csdn.net/lmj623565791/article/details/51118836" target="_blank" rel="external">http://blog.csdn.net/lmj623565791/article/details/51118836</a></p>
</blockquote>
<h4 id="关于分页加载"><a href="#关于分页加载" class="headerlink" title="关于分页加载"></a>关于分页加载</h4><p>伴随着<strong>上拉加载更多</strong>功能的,往往是<strong>分页加载</strong>,关于分页加载的页数是在列表控件内部自动控制?还是在列表控件外部自己控制?我个人认为应该放在列表控件外部自己来控制页数,因为这个页数不光是在上拉加载更多时会使用到,在下拉刷新的时候也会使用到,并且对页数需要清零。<br>这里提供一种在列表控件外部控制页数的实现逻辑:<br><figure class="highlight aspectj"><table><tr><td class="code"><pre><div class="line"><span class="comment">// 分页页数</span></div><div class="line"><span class="keyword">private</span> <span class="keyword">int</span> page = <span class="number">0</span>;</div><div class="line"></div><div class="line"><span class="comment">// 初始化下拉刷新控件</span></div><div class="line"><span class="keyword">private</span> <span class="function"><span class="keyword">void</span> <span class="title">initRefreshView</span><span class="params">()</span> </span>{</div><div class="line"> MaterialHeader header = <span class="keyword">new</span> MaterialHeader(getContext());</div><div class="line"> refreshView.setHeaderView(header);</div><div class="line"> refreshView.addPtrUIHandler(header);</div><div class="line"> refreshView.setPtrHandler(<span class="keyword">new</span> PtrDefaultHandler() {</div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">void</span> <span class="title">onRefreshBegin</span><span class="params">(PtrFrameLayout ptrFrameLayout)</span> </span>{</div><div class="line"> page = <span class="number">0</span>;</div><div class="line"> requestTopMovies();</div><div class="line"> }</div><div class="line"> });</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">// 初始化RecyclerView,包含上拉加载更多</span></div><div class="line"><span class="keyword">private</span> <span class="function"><span class="keyword">void</span> <span class="title">initRecyclerView</span><span class="params">()</span> </span>{</div><div class="line"> recyclerView.setLayoutManager(<span class="keyword">new</span> LinearLayoutManager(getContext()));</div><div class="line"> adapter = <span class="keyword">new</span> SubjectAdapter(getContext());</div><div class="line"> recyclerView.setAdapter(adapter);</div><div class="line"> recyclerView.initLoadMore(<span class="keyword">new</span> OnLoadMoreListener() {</div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">void</span> <span class="title">onLoadMore</span><span class="params">()</span> </span>{</div><div class="line"> page++;</div><div class="line"> requestTopMovies();</div><div class="line"> }</div><div class="line"> });</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">// 具有分页操作的网络请求</span></div><div class="line"><span class="keyword">private</span> <span class="function"><span class="keyword">void</span> <span class="title">requestTopMovies</span><span class="params">()</span> </span>{</div><div class="line"> Subscriber subscriber = <span class="keyword">new</span> RxCallback<List<Movie>>() {</div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">void</span> <span class="title">onSuccess</span><span class="params">(List<Movie> movieList)</span> </span>{</div><div class="line"> <span class="keyword">if</span> (adapter != <span class="keyword">null</span>) {</div><div class="line"> <span class="keyword">if</span> (page == <span class="number">0</span>) {</div><div class="line"> adapter.clear();</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> recyclerView.loadMoreComplete();</div><div class="line"> }</div><div class="line"> adapter.addAll(movieList);</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">void</span> <span class="title">onFinished</span><span class="params">()</span> </span>{</div><div class="line"> refreshComplete();</div><div class="line"> }</div><div class="line"> };</div><div class="line"> RxRetrofitClient.getInstance().requestTop250Movies(subscriber, page, <span class="number">10</span>);</div><div class="line">}</div></pre></td></tr></table></figure></p>
<blockquote>
<p>参考:<br><a href="https://github.com/hanks-zyh/SwipeRefreshLayout" target="_blank" rel="external">https://github.com/hanks-zyh/SwipeRefreshLayout</a><br><a href="http://www.jianshu.com/p/d6a76fd3ea5b" target="_blank" rel="external">http://www.jianshu.com/p/d6a76fd3ea5b</a><br><a href="http://blog.csdn.net/lmj623565791/article/details/38902805" target="_blank" rel="external">http://blog.csdn.net/lmj623565791/article/details/38902805</a><br><a href="http://blog.csdn.net/lmj623565791/article/details/51118836" target="_blank" rel="external">http://blog.csdn.net/lmj623565791/article/details/51118836</a></p>
</blockquote>
</div>
<div>
</div>
<div>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
<link itemprop="mainEntityOfPage" href="http://yoursite.com/2017/01/26/RxJava-Retrofit2-OkHttp3封装及踩坑-续/">
<span style="display:none" itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="XiaoFeng">
<meta itemprop="description" content="">
<meta itemprop="image" content="/images/avatar.gif">
</span>
<span style="display:none" itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="Android背锅侠">
<span style="display:none" itemprop="logo" itemscope itemtype="http://schema.org/ImageObject">
<img style="display:none;" itemprop="url image" alt="Android背锅侠" src="">
</span>
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
<a class="post-title-link" href="/2017/01/26/RxJava-Retrofit2-OkHttp3封装及踩坑-续/" itemprop="url">
RxJava + Retrofit2 + OkHttp3 封装及踩坑(续)
</a>
</h1>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">Veröffentlicht am</span>
<time title="Post created" itemprop="dateCreated datePublished" datetime="2017-01-26T21:04:43+08:00">
2017-01-26
</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<p><a href="http://www.jianshu.com/p/ba917e1029f2" target="_blank" rel="external">前一篇文章</a>(也是我在简书上的第一篇技术文章^.^)讲了Android三剑客的基础用法和简单封装,有一些封装只是一笔带过,还有些用法被遗漏没讲到的,所以在这篇里统一做下查漏补缺。</p>
<h4 id="0x00-先做一下纠正:"><a href="#0x00-先做一下纠正:" class="headerlink" title="0x00 先做一下纠正:"></a>0x00 先做一下纠正:</h4><p>https和失败重连,OkHttp默认是支持的,并不用手动去设置(在OkHttpClient.Builder中已默认设置),所以OkHttpClient.Builder的初始化可以简化为:<br><figure class="highlight haxe"><table><tr><td class="code"><pre><div class="line"><span class="comment">// 创建OkHttpClient</span></div><div class="line">OkHttpClient.Builder builder = <span class="keyword">new</span> <span class="type">OkHttpClient</span>.Builder()</div><div class="line"> <span class="comment">// 超时设置</span></div><div class="line"> .connectTimeout(DEFAULT_CONNECT_TIMEOUT, TimeUnit.SECONDS)</div><div class="line"> .readTimeout(DEFAULT_READ_TIMEOUT, TimeUnit.SECONDS)</div><div class="line"> .writeTimeout(DEFAULT_WRITE_TIMEOUT, TimeUnit.SECONDS)</div><div class="line"> <span class="comment">// cookie管理</span></div><div class="line"> .cookieJar(<span class="keyword">new</span> <span class="type">PersistentCookieJar</span>(<span class="keyword">new</span> <span class="type">SetCookieCache</span>(), <span class="keyword">new</span> <span class="type">SharedPrefsCookiePersistor</span>(App.getInstance())));</div></pre></td></tr></table></figure></p>
<h4 id="0x01-Cookie持久化管理"><a href="#0x01-Cookie持久化管理" class="headerlink" title="0x01 Cookie持久化管理"></a>0x01 Cookie持久化管理</h4><p>这部分主要参考了<a href="https://gold.xitu.io/entry/572ed42ddf0eea0063186e1f" target="_blank" rel="external">这篇文章</a>。</p>
<ul>
<li>不带持久化<figure class="highlight dart"><table><tr><td class="code"><pre><div class="line">builder.cookieJar(<span class="keyword">new</span> CookieJar() {</div><div class="line"> private <span class="keyword">final</span> HashMap<HttpUrl, <span class="built_in">List</span><Cookie>> cookieStore = <span class="keyword">new</span> HashMap<>();</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> public <span class="keyword">void</span> saveFromResponse(HttpUrl url, <span class="built_in">List</span><Cookie> cookies) {</div><div class="line"> cookieStore.put(url, cookies);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> public <span class="built_in">List</span><Cookie> loadForRequest(HttpUrl url) {</div><div class="line"> <span class="built_in">List</span><Cookie> cookies = cookieStore.<span class="keyword">get</span>(url);</div><div class="line"> <span class="keyword">return</span> cookies != <span class="keyword">null</span> ? cookies : <span class="keyword">new</span> ArrayList<Cookie>();</div><div class="line"> }</div><div class="line"> });</div></pre></td></tr></table></figure>
</li>
</ul>
<p>这种简单的实现,每次重启App都会需要重新登录,不可取。</p>
<ul>
<li>带持久化<figure class="highlight haxe"><table><tr><td class="code"><pre><div class="line">CookieHandler cookieHandler = <span class="keyword">new</span> <span class="type">CookieManager</span>(</div><div class="line"> <span class="keyword">new</span> <span class="type">PersistentCookieStore</span>(context), CookiePolicy.ACCEPT_ALL);</div><div class="line">builder.cookieJar(<span class="keyword">new</span> <span class="type">JavaNetCookieJar</span>(cookieHandler));</div></pre></td></tr></table></figure>
</li>
</ul>
<p>这里出现了两个类:<strong>JavaNetCookieJar</strong>和<strong>PersistentCookieStore</strong></p>
<ul>
<li><p><strong>JavaNetCookieJar</strong>就是对CookieJar的封装实现,里面实现了对Cookie持久化存储和获取的调用逻辑,OkHttp已经帮我们实现了这个类,需要引入下面这个包:</p>
<figure class="highlight gradle"><table><tr><td class="code"><pre><div class="line"><span class="keyword">compile</span> <span class="string">'com.squareup.okhttp3:okhttp-urlconnection:3.5.0'</span></div></pre></td></tr></table></figure>
</li>
<li><p><strong>PersistentCookieStore</strong>是具体实现Cookie持久化的类,使用的是SharedPreferences,具体代码实现可参考<a href="https://gist.github.com/franmontiel/ed12a2295566b7076161" target="_blank" rel="external">这篇</a>。<br>当然,如果你想通过数据库实现持久化,也可以自己封装一个类似的类去实现。</p>
</li>
</ul>
<ul>
<li>再介绍一个封装了Cookie持久化的第三方库(推荐)<figure class="highlight haxe"><table><tr><td class="code"><pre><div class="line">ClearableCookieJar cookieJar = <span class="keyword">new</span> <span class="type">PersistentCookieJar</span>(</div><div class="line"> <span class="keyword">new</span> <span class="type">SetCookieCache</span>(), <span class="keyword">new</span> <span class="type">SharedPrefsCookiePersistor</span>(context));</div><div class="line">builder.cookieJar(cookieJar);</div></pre></td></tr></table></figure>
</li>
</ul>
<p>需要引入下面这个包:<br><figure class="highlight ada"><table><tr><td class="code"><pre><div class="line">compile <span class="symbol">'com.github.franmontiel</span>:PersistentCookieJar:v1.<span class="number">0.0</span></div></pre></td></tr></table></figure></p>
<h4 id="0x02-拦截器"><a href="#0x02-拦截器" class="headerlink" title="0x02. 拦截器"></a>0x02. 拦截器</h4><ul>
<li><p>addInterceptor和addNetworkInterceptor的区别<br>前一篇文章有同学问到两者的区别,okHttp官方对<a href="https://github.com/square/okhttp/wiki/Interceptors" target="_blank" rel="external">拦截器</a>做了解释,并给了一张图,感觉挺一目了然的。<br><img src="https://raw.githubusercontent.com/wiki/square/okhttp/[email protected]" alt="Paste_Image.png"><br>两种拦截器简单来说就是调用时机的区别,应用拦截器调用时机较早,也就是进入chain.proceed的递归较早,相应的完成递归得到response会较晚;而网络拦截器则相反,request请求调用时机较晚,会较早完成chain.proceed递归调用,得到response的时机较早。<br>简单来说就是应用拦截器较上层,而网络拦截器较底层,所有拦截器就是一个由浅入深的递归调用。具体还是得看源码。</p>
</li>
<li><p>Http Header<br>可以通过这个拦截器为Request添加全局统一的Header。</p>
<figure class="highlight aspectj"><table><tr><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * 网络请求公共头信息插入器</div><div class="line"> *</div><div class="line"> * Created by XiaoFeng on 17/1/18.</div><div class="line"> */</div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HttpHeaderInterceptor</span> <span class="keyword">implements</span> <span class="title">Interceptor</span> </span>{</div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="keyword">public</span> <span class="function">Response <span class="title">intercept</span><span class="params">(Chain chain)</span> <span class="keyword">throws</span> IOException </span>{</div><div class="line"> Request original = chain.request();</div><div class="line"> Request request = original.newBuilder()</div><div class="line"> .header(<span class="string">"User-Agent"</span>, <span class="string">"Android, xxx"</span>)</div><div class="line"> .header(<span class="string">"Accept"</span>, <span class="string">"application/json"</span>)</div><div class="line"> .header(<span class="string">"Content-type"</span>, <span class="string">"application/json"</span>)</div><div class="line"> .method(original.method(), original.body())</div><div class="line"> .build();</div><div class="line"> <span class="function"><span class="keyword">return</span> chain.<span class="title">proceed</span><span class="params">(request)</span></span>;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
</li>
</ul>
<ol>
<li><p>公共参数<br>主要参考<a href="https://gold.xitu.io/entry/5825300b2f301e005c47fac5" target="_blank" rel="external">这篇</a></p>
<figure class="highlight vbscript"><table><tr><td class="code"><pre><div class="line">/**</div><div class="line"> * 网络请求公共参数插入器</div><div class="line"> * <p></div><div class="line"> * Created by XiaoFeng <span class="keyword">on</span> <span class="number">2017</span>/<span class="number">1</span>/<span class="number">25.</span></div><div class="line"> */</div><div class="line"><span class="keyword">public</span> <span class="keyword">class</span> CommonParamsInterceptor implements Interceptor {</div><div class="line"> @Override</div><div class="line"> <span class="keyword">public</span> <span class="built_in">Response</span> intercept(Chain chain) throws IOException {</div><div class="line"> <span class="built_in">Request</span> <span class="built_in">request</span> = chain.<span class="built_in">request</span>();</div><div class="line"></div><div class="line"> <span class="keyword">if</span> (<span class="built_in">request</span>.method().equals(<span class="string">"GET"</span>)) {</div><div class="line"> HttpUrl httpUrl = <span class="built_in">request</span>.url().newBuilder()</div><div class="line"> .addQueryParameter(<span class="string">"version"</span>, <span class="string">"xxx"</span>)</div><div class="line"> .addQueryParameter(<span class="string">"device"</span>, <span class="string">"Android"</span>)</div><div class="line"> .addQueryParameter(<span class="string">"timestamp"</span>, <span class="built_in">String</span>.valueOf(System.currentTimeMillis()))</div><div class="line"> .build();</div><div class="line"> <span class="built_in">request</span> = <span class="built_in">request</span>.newBuilder().url(httpUrl).build();</div><div class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">request</span>.method().equals(<span class="string">"POST"</span>)) {</div><div class="line"> <span class="keyword">if</span> (<span class="built_in">request</span>.body() instanceof FormBody) {</div><div class="line"> FormBody.Builder bodyBuilder = <span class="keyword">new</span> FormBody.Builder();</div><div class="line"> FormBody formBody = (FormBody) <span class="built_in">request</span>.body();</div><div class="line"></div><div class="line"> <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i < formBody.size(); i++) {</div><div class="line"> bodyBuilder.addEncoded(formBody.encodedName(i), formBody.encodedValue(i));</div><div class="line"> }</div><div class="line"></div><div class="line"> formBody = bodyBuilder</div><div class="line"> .addEncoded(<span class="string">"version"</span>, <span class="string">"xxx"</span>)</div><div class="line"> .addEncoded(<span class="string">"device"</span>, <span class="string">"Android"</span>)</div><div class="line"> .addEncoded(<span class="string">"timestamp"</span>, <span class="built_in">String</span>.valueOf(System.currentTimeMillis()))</div><div class="line"> .build();</div><div class="line"></div><div class="line"> <span class="built_in">request</span> = <span class="built_in">request</span>.newBuilder().post(formBody).build();</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> return chain.proceed(<span class="built_in">request</span>);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
</li>
<li><p>缓存策略</p>
<figure class="highlight haxe"><table><tr><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * 网络请求缓存策略插入器</div><div class="line"> *</div><div class="line"> * Created by XiaoFeng on 17/1/17.</div><div class="line"> */</div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HttpCacheInterceptor</span> <span class="keyword"><span class="keyword">implements</span> <span class="type">Interceptor</span></span> </span>{</div><div class="line"> @Override</div><div class="line"> <span class="keyword">public</span> Response intercept(Chain chain) throws IOException {</div><div class="line"> Request request = chain.request();</div><div class="line"> <span class="comment">// 无网络时,始终使用本地Cache</span></div><div class="line"> <span class="keyword">if</span> (!NetworkUtil.isNetworkConnected()) {</div><div class="line"> request = request.<span class="keyword">new</span><span class="type">Builder</span>()</div><div class="line"> .cacheControl(CacheControl.FORCE_CACHE)</div><div class="line"> .build();</div><div class="line"> }</div><div class="line"></div><div class="line"> Response response = chain.proceed(request);</div><div class="line"> <span class="keyword">if</span> (NetworkUtil.isNetworkConnected()) {</div><div class="line"> <span class="comment">// 有网络时,设置缓存过期时间0个小时</span></div><div class="line"> int maxAge = <span class="number">0</span>;</div><div class="line"> response.<span class="keyword">new</span><span class="type">Builder</span>()</div><div class="line"> .header(<span class="string">"Cache-Control"</span>, <span class="string">"public, max-age="</span> + maxAge)</div><div class="line"> .removeHeader(<span class="string">"Pragma"</span>) <span class="comment">// 清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效</span></div><div class="line"> .build();</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> <span class="comment">// 无网络时,设置缓存过期超时时间为4周</span></div><div class="line"> int maxStale = <span class="number">60</span> * <span class="number">60</span> * <span class="number">24</span> * <span class="number">28</span>;</div><div class="line"> response.<span class="keyword">new</span><span class="type">Builder</span>()</div><div class="line"> .header(<span class="string">"Cache-Control"</span>, <span class="string">"public, only-if-cached, max-stale="</span> + maxStale)</div><div class="line"> .removeHeader(<span class="string">"Pragma"</span>)</div><div class="line"> .build();</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> response;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
</li>
<li><p>调试工具<br>使用的是Facebook推出的一个集成到Chrome中的调试工具,需要引入下面两个库:</p>
<figure class="highlight gradle"><table><tr><td class="code"><pre><div class="line"><span class="keyword">compile</span> <span class="string">'com.facebook.stetho:stetho:1.4.1'</span></div><div class="line"><span class="keyword">compile</span> <span class="string">'com.facebook.stetho:stetho-okhttp3:1.4.1'</span></div></pre></td></tr></table></figure>
</li>
</ol>
<p>在Application中初始化就可以用了<br><figure class="highlight kotlin"><table><tr><td class="code"><pre><div class="line">Stetho.initializeWithDefaults(<span class="keyword">this</span>);</div></pre></td></tr></table></figure></p>
<p><strong>如何调试?</strong></p>
<ul>
<li>打开Chrome浏览器</li>
<li>地址栏输入<code>chrome://inspect</code></li>
<li>进入页面后,在左边的DevTools -> Devices -> Remote Target下,可以找到你连接的手机设备,点开后就会出现调试页面了,后面就自己研究吧,不光可以调试网络请求,还可以查看手机中的数据库和SharePreference等持久化文件,而且不用root,很强大!</li>
</ul>
<h4 id="0x03-FastJson解析库封装"><a href="#0x03-FastJson解析库封装" class="headerlink" title="0x03. FastJson解析库封装"></a>0x03. FastJson解析库封装</h4><p>网上很多介绍retrofit的文章,对网络请求返回的结果,使用的都是默认的Gson库,虽然可以满足大部分人的需求,但是有些对性能要求高一点的人,还是习惯使用FastJson库做解析,这里就讲讲如何把默认的Gson库替换成FastJson库。 </p>
<p>首先,默认Gson库的设置是这样的:<br><figure class="highlight pony"><table><tr><td class="code"><pre><div class="line"><span class="type">Retrofit</span> retrofit = <span class="function"><span class="keyword">new</span> <span class="title">Retrofit</span>.<span class="title">Builder</span>()</span></div><div class="line"> .<span class="title">client</span>(builder.build())</div><div class="line"> .<span class="title">addConverterFactory</span>(<span class="type">GsonConverterFactory</span>.create())</div><div class="line"> .<span class="title">addCallAdapterFactory</span>(<span class="type">RxJavaCallAdapterFactory</span>.create())</div><div class="line"> .<span class="title">baseUrl</span>(<span class="type">BASE_URL</span>)</div><div class="line"> .<span class="title">build</span>();</div></pre></td></tr></table></figure></p>
<p>用FastJson库替换后是这样的:<br><figure class="highlight pony"><table><tr><td class="code"><pre><div class="line"><span class="type">Retrofit</span> retrofit = <span class="function"><span class="keyword">new</span> <span class="title">Retrofit</span>.<span class="title">Builder</span>()</span></div><div class="line"> .<span class="title">client</span>(builder.build())</div><div class="line"> .<span class="title">addConverterFactory</span>(<span class="type">FastJsonConvertFactory</span>.create())</div><div class="line"> .<span class="title">addCallAdapterFactory</span>(<span class="type">RxJavaCallAdapterFactory</span>.create())</div><div class="line"> .<span class="title">baseUrl</span>(<span class="type">BASE_URL</span>)</div><div class="line"> .<span class="title">build</span>();</div></pre></td></tr></table></figure></p>
<p>是不是很像,没错,就是把ConverterFactory替换了一下而已。</p>
<p>至于FastJsonConvertFactory的实现,其实就是仿造GsonConverterFactory的源码来写的,并不复杂。</p>
<p>主要有三个类:</p>
<ol>
<li><p>工厂类:FastJsonConvertFactory,里面就是分别创建了Request和Response的转换类。</p>
<figure class="highlight scala"><table><tr><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> *</div><div class="line"> * Created by XiaoFeng on 2017/1/17.</div><div class="line"> */</div><div class="line">public <span class="class"><span class="keyword">class</span> <span class="title">FastJsonConvertFactory</span> <span class="keyword">extends</span> <span class="title">Converter</span>.<span class="title">Factory</span> </span>{</div><div class="line"> public static <span class="type">FastJsonConvertFactory</span> create() {</div><div class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="type">FastJsonConvertFactory</span>();</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> public <span class="type">Converter</span><?, <span class="type">RequestBody</span>> requestBodyConverter(<span class="type">Type</span> <span class="class"><span class="keyword">type</span>, <span class="title">Annotation</span>[] <span class="title">parameterAnnotations</span>, <span class="title">Annotation</span>[] <span class="title">methodAnnotations</span>, <span class="title">Retrofit</span> <span class="title">retrofit</span>) </span>{</div><div class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="type">FastJsonRequestConverter</span><>();</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> public <span class="type">Converter</span><<span class="type">ResponseBody</span>, ?> responseBodyConverter(<span class="type">Type</span> <span class="class"><span class="keyword">type</span>, <span class="title">Annotation</span>[] <span class="title">annotations</span>, <span class="title">Retrofit</span> <span class="title">retrofit</span>) </span>{</div><div class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="type">FastJsonResponseConverter</span><>(<span class="class"><span class="keyword">type</span>)</span>;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
</li>
<li><p>Request转换类:FastJsonRequestConverter</p>
<figure class="highlight java"><table><tr><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> *</div><div class="line"> * Created by XiaoFeng on 2017/1/17.</div><div class="line"> */</div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">FastJsonRequestConverter</span><<span class="title">T</span>> <span class="keyword">implements</span> <span class="title">Converter</span><<span class="title">T</span>, <span class="title">RequestBody</span>> </span>{</div><div class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> MediaType MEDIA_TYPE = MediaType.parse(<span class="string">"application/json; charset=UTF-8"</span>);</div><div class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> Charset UTF_8 = Charset.forName(<span class="string">"UTF-8"</span>);</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> RequestBody <span class="title">convert</span><span class="params">(T value)</span> <span class="keyword">throws</span> IOException </span>{</div><div class="line"> <span class="keyword">return</span> RequestBody.create(MEDIA_TYPE, JSON.toJSONBytes(value));</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
</li>
<li><p>Response转换类:FastJsonResponseConverter</p>
<figure class="highlight scala"><table><tr><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> *</div><div class="line"> * Created by XiaoFeng on 2017/1/17.</div><div class="line"> */</div><div class="line">public <span class="class"><span class="keyword">class</span> <span class="title">FastJsonResponseConverter<T></span> <span class="title">implements</span> <span class="title">Converter<ResponseBody</span>, <span class="title">T></span> </span>{</div><div class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">Type</span> <span class="class"><span class="keyword">type</span></span>;</div><div class="line"></div><div class="line"> public <span class="type">FastJsonResponseConverter</span>(<span class="type">Type</span> <span class="class"><span class="keyword">type</span>) </span>{</div><div class="line"> <span class="keyword">this</span>.<span class="keyword">type</span> = <span class="class"><span class="keyword">type</span></span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> public <span class="type">T</span> convert(<span class="type">ResponseBody</span> value) <span class="keyword">throws</span> <span class="type">IOException</span> {</div><div class="line"> <span class="type">BufferedSource</span> buffer = <span class="type">Okio</span>.buffer(value.source());</div><div class="line"> <span class="type">String</span> s = buffer.readUtf8();</div><div class="line"> buffer.close();</div><div class="line"> <span class="keyword">return</span> <span class="type">JSON</span>.parseObject(s, <span class="class"><span class="keyword">type</span>)</span>;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
</li>
</ol>
<p>是不是很简单,如果想再换成别的第三方json解析库,照着这个写就可以了。</p>
<h4 id="0x04-生命周期"><a href="#0x04-生命周期" class="headerlink" title="0x04. 生命周期"></a>0x04. 生命周期</h4><p><a href="http://www.jianshu.com/p/ba917e1029f2" target="_blank" rel="external">上一篇</a>中还有同学提到RxJava的生命周期管理,防止内存泄漏,这个可以直接使用<a href="https://github.com/trello/RxLifecycle" target="_blank" rel="external">第三方库</a>,参考<a href="https://gold.xitu.io/entry/58290ea2570c35005878ce8f" target="_blank" rel="external">这篇</a>。<br>一般引用下面两个库就够了:<br><figure class="highlight gradle"><table><tr><td class="code"><pre><div class="line"><span class="keyword">compile</span> <span class="string">'com.trello:rxlifecycle:1.0'</span></div><div class="line"><span class="keyword">compile</span> <span class="string">'com.trello:rxlifecycle-components:1.0'</span></div></pre></td></tr></table></figure></p>
<p>有两种使用方式:<br><strong>1. 自动取消订阅,使用bindToLifecycle。</strong><br>需要继承至RxActivity或者RxFragment等基类。<br><figure class="highlight aspectj"><table><tr><td class="code"><pre><div class="line"><span class="meta">@Override</span></div><div class="line"><span class="keyword">protected</span> <span class="function"><span class="keyword">void</span> <span class="title">onStart</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">super</span>.onStart();</div><div class="line"> <span class="comment">// Using automatic unsubscription, this should determine that the correct time to</span></div><div class="line"> <span class="comment">// unsubscribe is onStop (the opposite of onStart).</span></div><div class="line"> Observable.interval(<span class="number">1</span>, TimeUnit.SECONDS)</div><div class="line"> .doOnUnsubscribe(<span class="keyword">new</span> Action0() {</div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">void</span> <span class="title">call</span><span class="params">()</span> </span>{</div><div class="line"> Log.i(TAG, <span class="string">"Unsubscribing subscription from onStart()"</span>);</div><div class="line"> }</div><div class="line"> })</div><div class="line"> <span class="comment">// 因为bindToLifecycle是在onStart的时候调用,所以在onStop的时候自动取消订阅</span></div><div class="line"> .compose(<span class="keyword">this</span>.<Long>bindToLifecycle())</div><div class="line"> .subscribe(<span class="keyword">new</span> Action1<Long>() {</div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">void</span> <span class="title">call</span><span class="params">(Long num)</span> </span>{</div><div class="line"> Log.i(TAG, <span class="string">"Started in onStart(), running until in onStop(): "</span> + num);</div><div class="line"> }</div><div class="line"> });</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>从下面这段核心函数可以看清<strong>自动取消订阅的规则</strong>,就是在哪个生命周期内调用bindToLifecycle,就在与其对应的结束生命周期函数调用时自动取消订阅。<br><figure class="highlight groovy"><table><tr><td class="code"><pre><div class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> Func1<ActivityEvent, ActivityEvent> ACTIVITY_LIFECYCLE =</div><div class="line"> <span class="keyword">new</span> Func1<ActivityEvent, ActivityEvent>() {</div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="keyword">public</span> ActivityEvent call(ActivityEvent lastEvent) {</div><div class="line"> <span class="keyword">switch</span> (lastEvent) {</div><div class="line"> <span class="keyword">case</span> <span class="string">CREATE:</span></div><div class="line"> <span class="keyword">return</span> ActivityEvent.DESTROY;</div><div class="line"> <span class="keyword">case</span> <span class="string">START:</span></div><div class="line"> <span class="keyword">return</span> ActivityEvent.STOP;</div><div class="line"> <span class="keyword">case</span> <span class="string">RESUME:</span></div><div class="line"> <span class="keyword">return</span> ActivityEvent.PAUSE;</div><div class="line"> <span class="keyword">case</span> <span class="string">PAUSE:</span></div><div class="line"> <span class="keyword">return</span> ActivityEvent.STOP;</div><div class="line"> <span class="keyword">case</span> <span class="string">STOP:</span></div><div class="line"> <span class="keyword">return</span> ActivityEvent.DESTROY;</div><div class="line"> <span class="keyword">case</span> <span class="string">DESTROY:</span></div><div class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> OutsideLifecycleException(<span class="string">"Cannot bind to Activity lifecycle when outside of it."</span>);</div><div class="line"><span class="symbol"> default:</span></div><div class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> UnsupportedOperationException(<span class="string">"Binding to "</span> + lastEvent + <span class="string">" not yet implemented"</span>);</div><div class="line"> }</div><div class="line"> }</div><div class="line"> };</div></pre></td></tr></table></figure></p>
<p><strong>2. 手动取消订阅,使用bindUntilEvent。</strong><br>需要继承至RxActivity或者RxFragment等基类。<br><figure class="highlight aspectj"><table><tr><td class="code"><pre><div class="line"><span class="meta">@Override</span></div><div class="line"><span class="keyword">protected</span> <span class="function"><span class="keyword">void</span> <span class="title">onResume</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">super</span>.onResume();</div><div class="line"> <span class="comment">// `this.<Long>` is necessary if you're compiling on JDK7 or below.</span></div><div class="line"> <span class="comment">// If you're using JDK8+, then you can safely remove it.</span></div><div class="line"> Observable.interval(<span class="number">1</span>, TimeUnit.SECONDS)</div><div class="line"> .doOnUnsubscribe(<span class="keyword">new</span> Action0() {</div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">void</span> <span class="title">call</span><span class="params">()</span> </span>{</div><div class="line"> Log.i(TAG, <span class="string">"Unsubscribing subscription from onResume()"</span>);</div><div class="line"> }</div><div class="line"> })</div><div class="line"> <span class="comment">// 手动设置在Activity onDestroy的时候取消订阅</span></div><div class="line"> .compose(<span class="keyword">this</span>.<Long>bindUntilEvent(ActivityEvent.DESTROY))</div><div class="line"> .subscribe(<span class="keyword">new</span> Action1<Long>() {</div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">void</span> <span class="title">call</span><span class="params">(Long num)</span> </span>{</div><div class="line"> Log.i(TAG, <span class="string">"Started in onResume(), running until in onDestroy(): "</span> + num);</div><div class="line"> }</div><div class="line"> });</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p><strong>3. 自定义RxActivity/RxFragment</strong><br>直接继承RxActivity/RxFragment有时会碰到问题,因为有可能本身已经有一个基类需要继承,java不能多继承。不过不要慌,我们可以自定义一个自己的基类,实现方式参考RxActivity。<br><figure class="highlight java"><table><tr><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">RxActivity</span> <span class="keyword">extends</span> <span class="title">Activity</span> <span class="keyword">implements</span> <span class="title">LifecycleProvider</span><<span class="title">activityevent</span>> </span>{</div><div class="line"> <span class="keyword">private</span> <span class="keyword">final</span> BehaviorSubject<activityevent> lifecycleSubject = BehaviorSubject.create();</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="title">RxActivity</span><span class="params">()</span> </span>{</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@NonNull</span></div><div class="line"> <span class="meta">@CheckResult</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> Observable<activityevent> <span class="title">lifecycle</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.lifecycleSubject.asObservable();</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@NonNull</span></div><div class="line"> <span class="meta">@CheckResult</span></div><div class="line"> <span class="keyword">public</span> <span class="keyword">final</span> <t> <span class="function">LifecycleTransformer<t> <span class="title">bindUntilEvent</span><span class="params">(@NonNull ActivityEvent event)</span> </span>{</div><div class="line"> <span class="keyword">return</span> RxLifecycle.bindUntilEvent(<span class="keyword">this</span>.lifecycleSubject, event);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@NonNull</span></div><div class="line"> <span class="meta">@CheckResult</span></div><div class="line"> <span class="keyword">public</span> <span class="keyword">final</span> <t> <span class="function">LifecycleTransformer<t> <span class="title">bindToLifecycle</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">return</span> RxLifecycleAndroid.bindActivity(<span class="keyword">this</span>.lifecycleSubject);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@CallSuper</span></div><div class="line"> <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(@Nullable Bundle savedInstanceState)</span> </span>{</div><div class="line"> <span class="keyword">super</span>.onCreate(savedInstanceState);</div><div class="line"> <span class="keyword">this</span>.lifecycleSubject.onNext(ActivityEvent.CREATE);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@CallSuper</span></div><div class="line"> <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onStart</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">super</span>.onStart();</div><div class="line"> <span class="keyword">this</span>.lifecycleSubject.onNext(ActivityEvent.START);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@CallSuper</span></div><div class="line"> <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onResume</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">super</span>.onResume();</div><div class="line"> <span class="keyword">this</span>.lifecycleSubject.onNext(ActivityEvent.RESUME);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@CallSuper</span></div><div class="line"> <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onPause</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">this</span>.lifecycleSubject.onNext(ActivityEvent.PAUSE);</div><div class="line"> <span class="keyword">super</span>.onPause();</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@CallSuper</span></div><div class="line"> <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onStop</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">this</span>.lifecycleSubject.onNext(ActivityEvent.STOP);</div><div class="line"> <span class="keyword">super</span>.onStop();</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@CallSuper</span></div><div class="line"> <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onDestroy</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">this</span>.lifecycleSubject.onNext(ActivityEvent.DESTROY);</div><div class="line"> <span class="keyword">super</span>.onDestroy();</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p><strong>突然发现写文章真是一个知识梳理,自我学习的好方法,比没有目的性的看很多技术文章有用很多倍,极力推荐有能力的同学都去尝试写属于自己的技术博客。^ ^</strong></p>
<blockquote>
<p>参考:<br><a href="https://gold.xitu.io/entry/572ed42ddf0eea0063186e1f" target="_blank" rel="external">https://gold.xitu.io/entry/572ed42ddf0eea0063186e1f</a><br><a href="https://gist.github.com/franmontiel/ed12a2295566b7076161" target="_blank" rel="external">https://gist.github.com/franmontiel/ed12a2295566b7076161</a><br><a href="https://gold.xitu.io/entry/5825300b2f301e005c47fac5" target="_blank" rel="external">https://gold.xitu.io/entry/5825300b2f301e005c47fac5</a><br><a href="http://www.codexiu.cn/android/blog/39432/" target="_blank" rel="external">http://www.codexiu.cn/android/blog/39432/</a><br><a href="http://androidxx.ren/forum.php?mod=viewthread&tid=19" target="_blank" rel="external">http://androidxx.ren/forum.php?mod=viewthread&tid=19</a><br><a href="https://gold.xitu.io/entry/58290ea2570c35005878ce8f" target="_blank" rel="external">https://gold.xitu.io/entry/58290ea2570c35005878ce8f</a></p>
</blockquote>
</div>
<div>
</div>
<div>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
<link itemprop="mainEntityOfPage" href="http://yoursite.com/2017/01/21/RxJava-Retrofit2-OkHttp3封装及踩坑/">
<span style="display:none" itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="XiaoFeng">
<meta itemprop="description" content="">
<meta itemprop="image" content="/images/avatar.gif">
</span>
<span style="display:none" itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="Android背锅侠">
<span style="display:none" itemprop="logo" itemscope itemtype="http://schema.org/ImageObject">
<img style="display:none;" itemprop="url image" alt="Android背锅侠" src="">
</span>
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
<a class="post-title-link" href="/2017/01/21/RxJava-Retrofit2-OkHttp3封装及踩坑/" itemprop="url">
RxJava + Retrofit2 + OkHttp3 封装及踩坑
</a>
</h1>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">Veröffentlicht am</span>
<time title="Post created" itemprop="dateCreated datePublished" datetime="2017-01-21T17:14:03+08:00">
2017-01-21
</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<p>网上对这三个开源库的组合框架代码已经一搜一大把了,但是都很零碎,需要搜索很多东拼西凑才能写一套完整的复合自己需求的框架。这篇文章就是我自己在封装过程遇到的各种问题的记录和总结,免得很多人重复踩坑。。</p>
<h4 id="一、封装后的效果"><a href="#一、封装后的效果" class="headerlink" title="一、封装后的效果"></a>一、封装后的效果</h4><figure class="highlight aspectj"><table><tr><td class="code"><pre><div class="line"><span class="keyword">private</span> <span class="function"><span class="keyword">void</span> <span class="title">requestTopMovies</span><span class="params">(<span class="keyword">int</span> page)</span> </span>{</div><div class="line"> showWaitingDialog();</div><div class="line"></div><div class="line"> Subscriber subscriber = <span class="keyword">new</span> RxCallback<List<Movie>>() {</div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">void</span> <span class="title">onSuccess</span><span class="params">(List<Movie> movieList)</span> </span>{</div><div class="line"> <span class="keyword">if</span> (adapter != <span class="keyword">null</span>) {</div><div class="line"> adapter.updateData(movieList);</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">void</span> <span class="title">onFinished</span><span class="params">()</span> </span>{</div><div class="line"> dismissWaitingDialog();</div><div class="line"> }</div><div class="line"> };</div><div class="line"> </div><div class="line"> RxRetrofitClient.getInstance().requestTop250Movies(subscriber, page, <span class="number">10</span>);</div><div class="line">}</div></pre></td></tr></table></figure>
<h4 id="二、封装过程"><a href="#二、封装过程" class="headerlink" title="二、封装过程"></a>二、封装过程</h4><h6 id="1-全局Client管理类封装-RxRetrofitClient(单例)"><a href="#1-全局Client管理类封装-RxRetrofitClient(单例)" class="headerlink" title="1. 全局Client管理类封装 RxRetrofitClient(单例)"></a>1. 全局Client管理类封装 RxRetrofitClient(单例)</h6><p>在这里初始化各种网络设置<br><figure class="highlight pony"><table><tr><td class="code"><pre><div class="line"><span class="comment">/// RxRetrofitClient.java</span></div><div class="line"></div><div class="line">private void initClient() {</div><div class="line"> <span class="comment">// 创建OkHttpClient</span></div><div class="line"> <span class="type">OkHttpClient</span>.<span class="type">Builder</span> builder = <span class="function"><span class="keyword">new</span> <span class="title">OkHttpClient</span>.<span class="title">Builder</span>()</span></div><div class="line"> <span class="comment">// 超时设置</span></div><div class="line"> .<span class="title">connectTimeout</span>(<span class="type">DEFAULT_CONNECT_TIMEOUT</span>, <span class="type">TimeUnit</span>.<span class="type">SECONDS</span>)</div><div class="line"> .<span class="title">readTimeout</span>(<span class="type">DEFAULT_READ_TIMEOUT</span>, <span class="type">TimeUnit</span>.<span class="type">SECONDS</span>)</div><div class="line"> .<span class="title">writeTimeout</span>(<span class="type">DEFAULT_WRITE_TIMEOUT</span>, <span class="type">TimeUnit</span>.<span class="type">SECONDS</span>)</div><div class="line"> <span class="comment">// 错误重连</span></div><div class="line"> .<span class="title">retryOnConnectionFailure</span>(true)</div><div class="line"> <span class="comment">// 支持HTTPS</span></div><div class="line"> .<span class="title">connectionSpecs</span>(<span class="type">Arrays</span>.asList(<span class="type">ConnectionSpec</span>.<span class="type">CLEARTEXT</span>, <span class="type">ConnectionSpec</span>.<span class="type">MODERN_TLS</span>)) <span class="comment">//明文Http与比较新的Https</span></div><div class="line"> <span class="comment">// cookie管理</span></div><div class="line"> .<span class="title">cookieJar</span>(new <span class="type">PersistentCookieJar</span>(new <span class="type">SetCookieCache</span>(), <span class="title">new</span> <span class="title">SharedPrefsCookiePersistor</span>(<span class="type">App</span>.getInstance())));</div><div class="line"></div><div class="line"> <span class="comment">// 添加各种插入器</span></div><div class="line"> <span class="title">addInterceptor</span>(builder);</div><div class="line"></div><div class="line"> <span class="comment">// 创建Retrofit实例</span></div><div class="line"> <span class="title">Retrofit</span> <span class="title">doubanRetrofit</span> = <span class="title">new</span> <span class="title">Retrofit</span>.<span class="title">Builder</span>()</div><div class="line"> .<span class="title">client</span>(builder.build())</div><div class="line"> .<span class="title">addConverterFactory</span>(<span class="type">GsonConverterFactory</span>.create())</div><div class="line"> <span class="comment">// .addConverterFactory(FastJsonConvertFactory.create())</span></div><div class="line"> .<span class="title">addCallAdapterFactory</span>(<span class="type">RxJavaCallAdapterFactory</span>.create())</div><div class="line"> .<span class="title">baseUrl</span>(<span class="type">BASE_URL_DOUBAN</span>)</div><div class="line"> .<span class="title">build</span>();</div><div class="line"></div><div class="line"> <span class="comment">// 创建API接口类</span></div><div class="line"> <span class="title">doubanApi</span> = <span class="title">doubanRetrofit</span>.<span class="title">create</span>(<span class="type">IDoubanApi</span>.class);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="title">private</span> <span class="title">void</span> <span class="title">addInterceptor</span>(<span class="type">OkHttpClient</span>.<span class="type">Builder</span> builder) {</div><div class="line"> <span class="comment">// 添加Header</span></div><div class="line"> <span class="title">builder</span>.<span class="title">addInterceptor</span>(new <span class="type">HttpHeaderInterceptor</span>());</div><div class="line"></div><div class="line"> <span class="comment">// 添加缓存控制策略</span></div><div class="line"> <span class="title">File</span> <span class="title">cacheDir</span> = <span class="title">App</span>.<span class="title">getInstance</span>().<span class="title">getExternalCacheDir</span>();</div><div class="line"> <span class="title">Cache</span> <span class="title">cache</span> = <span class="title">new</span> <span class="title">Cache</span>(cacheDir, <span class="type">DEFAULT_CACHE_SIZE</span>);</div><div class="line"> <span class="title">builder</span>.<span class="title">cache</span>(cache).<span class="title">addInterceptor</span>(new <span class="type">HttpCacheInterceptor</span>());</div><div class="line"></div><div class="line"> <span class="comment">// 添加http log</span></div><div class="line"> <span class="title">HttpLoggingInterceptor</span> <span class="title">logger</span> = <span class="title">new</span> <span class="title">HttpLoggingInterceptor</span>();</div><div class="line"> <span class="title">logger</span>.<span class="title">setLevel</span>(<span class="type">HttpLoggingInterceptor</span>.<span class="type">Level</span>.<span class="type">BODY</span>);</div><div class="line"> <span class="title">builder</span>.<span class="title">addInterceptor</span>(logger);</div><div class="line"></div><div class="line"> <span class="comment">// 添加调试工具</span></div><div class="line"> <span class="title">builder</span>.<span class="title">networkInterceptors</span>().<span class="title">add</span>(new <span class="type">StethoInterceptor</span>());</div><div class="line">}</div></pre></td></tr></table></figure></p>
<h6 id="2-Get和Post请求封装"><a href="#2-Get和Post请求封装" class="headerlink" title="2. Get和Post请求封装"></a>2. Get和Post请求封装</h6><figure class="highlight less"><table><tr><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * 豆瓣 Retrofit API</div><div class="line"> *</div><div class="line"> * Created by XiaoFeng on 16/12/20.</div><div class="line"> */</div><div class="line"></div><div class="line"><span class="selector-tag">public</span> <span class="selector-tag">interface</span> <span class="selector-tag">IDoubanApi</span> {</div><div class="line"></div><div class="line"> <span class="variable">@GET</span>(<span class="string">"top250"</span>)</div><div class="line"> Observable<DoubanResult<List<Movie>>> getTopMovies(<span class="variable">@Query</span>(<span class="string">"start"</span>) int start, <span class="variable">@Query</span>(<span class="string">"count"</span>) int count);</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * Json格式的Post请求(application/json)</div><div class="line"> */</div><div class="line"> <span class="variable">@POST</span>(<span class="string">"account/update_user_info"</span>)</div><div class="line"> Observable<RESTResult<String>> updateUserInfo(<span class="variable">@Body</span> RequestBody body);</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * Form格式的Post请求(application/x-www-form-urlencoded)</div><div class="line"> */</div><div class="line"> <span class="variable">@FormUrlEncoded</span></div><div class="line"> <span class="variable">@POST</span>(<span class="string">"account/update_user_info"</span>)</div><div class="line"> Observable<RESTResult<String>> updateUserInfo(<span class="variable">@FieldMap</span> Map<String, String> params);</div><div class="line"></div><div class="line">}</div></pre></td></tr></table></figure>
<figure class="highlight lasso"><table><tr><td class="code"><pre><div class="line"><span class="comment">/// RxRetrofitClient.java</span></div><div class="line"></div><div class="line"><span class="comment">/**</span></div><div class="line"> * 获取豆瓣电影Top250的列表数据</div><div class="line"> *</div><div class="line"> * @param subscriber 由调用者传过来的观察者对象</div><div class="line"> * @param page 页码</div><div class="line"> * @param count 每页个数</div><div class="line"> */</div><div class="line"><span class="keyword">public</span> <span class="literal">void</span> requestTop250Movies(Subscriber<<span class="built_in">List</span><Movie>> subscriber, int page, int count) {</div><div class="line"> doubanApi.getTopMovies(page * count, count)</div><div class="line"> .<span class="built_in">map</span>(RxUtil.<<span class="built_in">List</span><Movie>>handleDoubanResult())</div><div class="line"> .compose(RxUtil.<<span class="built_in">List</span><Movie>>normalSchedulers())</div><div class="line"> .subscribe(subscriber);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">/**</span></div><div class="line"> * 修改用户信息</div><div class="line"> * Json格式</div><div class="line"> *</div><div class="line"> * @param subscriber</div><div class="line"> * @param params</div><div class="line"> */</div><div class="line"><span class="keyword">public</span> <span class="literal">void</span> updateUserInfo(Subscriber<<span class="built_in">String</span>> subscriber, <span class="built_in">Map</span><<span class="built_in">String</span>, Object> <span class="keyword">params</span>) {</div><div class="line"> xzApi.updateUserInfo(toRequestBody(<span class="keyword">params</span>))</div><div class="line"> .<span class="built_in">map</span>(RxUtil.<<span class="built_in">String</span>>handleRESTFulResult())</div><div class="line"> .compose(RxUtil.<<span class="built_in">String</span>>normalSchedulers())</div><div class="line"> .subscribe(subscriber);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">/**</span></div><div class="line"> * 修改用户信息</div><div class="line"> * Form格式</div><div class="line"> *</div><div class="line"> * @param subscriber</div><div class="line"> * @param params</div><div class="line"> */</div><div class="line"><span class="keyword">public</span> <span class="literal">void</span> updateUserInfo(Subscriber<<span class="built_in">String</span>> subscriber, <span class="built_in">Map</span><<span class="built_in">String</span>, Object> <span class="keyword">params</span>) {</div><div class="line"> xzApi.updateUserInfo(<span class="keyword">params</span>)</div><div class="line"> .<span class="built_in">map</span>(RxUtil.<<span class="built_in">String</span>>handleRESTFulResult())</div><div class="line"> .compose(RxUtil.<<span class="built_in">String</span>>normalSchedulers())</div><div class="line"> .subscribe(subscriber);</div><div class="line">}</div></pre></td></tr></table></figure>
<blockquote>
<p>这里有几个 <strong>坑</strong></p>
<ol>
<li>写Form格式的Post请求时,需要添加 <strong>@FormUrlEncoded</strong> 注解,否则编译器会报错</li>
<li>写Json格式的Post请求时,不使用@FieldMap注解,而是使用 <strong>@Body</strong> 注解,并声明 <strong>RequestBody</strong> 类型变量</li>
</ol>
</blockquote>
<h6 id="3-RequestBody封装"><a href="#3-RequestBody封装" class="headerlink" title="3. RequestBody封装"></a>3. RequestBody封装</h6><figure class="highlight lasso"><table><tr><td class="code"><pre><div class="line"><span class="keyword">private</span> RequestBody toRequestBody(<span class="built_in">Map</span> <span class="keyword">params</span>) {</div><div class="line"> <span class="keyword">return</span> RequestBody.create(JSON, toJsonStr(<span class="keyword">params</span>));</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">private</span> <span class="built_in">String</span> toJsonStr(<span class="built_in">Map</span> <span class="keyword">params</span>) {</div><div class="line"> <span class="keyword">return</span> <span class="literal">new</span> JSONObject(<span class="keyword">params</span>).toString();</div><div class="line">}</div></pre></td></tr></table></figure>
<h6 id="4-对请求结果统一封装-RESTResult"><a href="#4-对请求结果统一封装-RESTResult" class="headerlink" title="4. 对请求结果统一封装 RESTResult"></a>4. 对请求结果统一封装 RESTResult</h6><figure class="highlight java"><table><tr><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * RESTFul 返回值封装类</div><div class="line"> *</div><div class="line"> * Created by XiaoFeng on 16/12/21.</div><div class="line"> */</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RESTResult</span><<span class="title">T</span>> </span>{</div><div class="line"></div><div class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> FAILURE = <span class="number">0</span>;</div><div class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> SUCCESS = <span class="number">1</span>;</div><div class="line"></div><div class="line"> <span class="meta">@SerializedName</span>(<span class="string">"res"</span>)</div><div class="line"> <span class="keyword">private</span> <span class="keyword">int</span> res;</div><div class="line"></div><div class="line"> <span class="meta">@SerializedName</span>(<span class="string">"msg"</span>)</div><div class="line"> <span class="keyword">private</span> String msg;</div><div class="line"></div><div class="line"> <span class="meta">@SerializedName</span>(<span class="string">"data"</span>)</div><div class="line"> T data;</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getRes</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">return</span> res;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setRes</span><span class="params">(<span class="keyword">int</span> res)</span> </span>{</div><div class="line"> <span class="keyword">this</span>.res = res;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">getMsg</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">return</span> msg;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setMsg</span><span class="params">(String msg)</span> </span>{</div><div class="line"> <span class="keyword">this</span>.msg = msg;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> T <span class="title">getData</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">return</span> data;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setData</span><span class="params">(T data)</span> </span>{</div><div class="line"> <span class="keyword">this</span>.data = data;</div><div class="line"> }</div><div class="line"></div><div class="line">}</div></pre></td></tr></table></figure>
<h6 id="5-对请求结果进行转换和预处理(map)"><a href="#5-对请求结果进行转换和预处理(map)" class="headerlink" title="5. 对请求结果进行转换和预处理(map)"></a>5. 对请求结果进行转换和预处理(map)</h6><figure class="highlight aspectj"><table><tr><td class="code"><pre><div class="line"><span class="comment">/// RxUtil.java</span></div><div class="line"></div><div class="line"><span class="comment">/**</span></div><div class="line"> * 对RESTful返回结果做预处理,对逻辑错误抛出异常</div><div class="line"> *</div><div class="line"> * <span class="doctag">@param</span> <T></div><div class="line"> * <span class="doctag">@return</span></div><div class="line"> */</div><div class="line"><span class="keyword">public</span> <span class="keyword">static</span> <T> Func1<RESTResult<T>, T> handleRESTFulResult() {</div><div class="line"> <span class="keyword">return</span> <span class="keyword">new</span> Func1<RESTResult<T>, T>() {</div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="keyword">public</span> <span class="function">T <span class="title">call</span><span class="params">(RESTResult<T> restResult)</span> </span>{</div><div class="line"> <span class="keyword">if</span> (restResult.getRes() != RESTResult.SUCCESS) {</div><div class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> ApiException(restResult.getRes(), restResult.getMsg());</div><div class="line"> }</div><div class="line"> <span class="function"><span class="keyword">return</span> restResult.<span class="title">getData</span><span class="params">()</span></span>;</div><div class="line"> }</div><div class="line"> };</div><div class="line">}</div></pre></td></tr></table></figure>
<h6 id="6-rxjava线程切换封装(compose)"><a href="#6-rxjava线程切换封装(compose)" class="headerlink" title="6. rxjava线程切换封装(compose)"></a>6. rxjava线程切换封装(compose)</h6><figure class="highlight java"><table><tr><td class="code"><pre><div class="line"><span class="comment">/// RxUtil.java</span></div><div class="line"></div><div class="line"><span class="comment">/**</span></div><div class="line"> * 普通线程切换: IO -> Main</div><div class="line"> *</div><div class="line"> * <span class="doctag">@param</span> <T></div><div class="line"> * <span class="doctag">@return</span></div><div class="line"> */</div><div class="line"><span class="keyword">public</span> <span class="keyword">static</span> <T> Observable.<span class="function">Transformer<T, T> <span class="title">normalSchedulers</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">return</span> <span class="keyword">new</span> Observable.Transformer<T, T>() {</div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> Observable<T> <span class="title">call</span><span class="params">(Observable<T> source)</span> </span>{</div><div class="line"> <span class="keyword">return</span> source.subscribeOn(Schedulers.io())</div><div class="line"> .observeOn(AndroidSchedulers.mainThread());</div><div class="line"> }</div><div class="line"> };</div><div class="line">}</div></pre></td></tr></table></figure>
<blockquote>
<p>这里有一个 <strong>坑</strong> :<br>我把handleRESTFulResult和normalSchedulers封装到了单独的帮助类RxUtil中,这样在使用map或者compose做转换时,需要<strong>显式写明返回类型</strong>,不然编译器会报错,这点在很多网上查找的资料中都没有提及</p>
<blockquote>
<p>.map(RxUtil.<strong><string></string></strong>handleRESTFulResult())<br>.compose(RxUtil.<strong><string></string></strong>normalSchedulers())</p>
</blockquote>
</blockquote>
<h6 id="7-对服务器返回的逻辑错误进行统一拦截和封装,抛出异常"><a href="#7-对服务器返回的逻辑错误进行统一拦截和封装,抛出异常" class="headerlink" title="7. 对服务器返回的逻辑错误进行统一拦截和封装,抛出异常"></a>7. 对服务器返回的逻辑错误进行统一拦截和封装,抛出异常</h6><figure class="highlight java"><table><tr><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * 对服务器返回的逻辑错误值进行封装</div><div class="line"> *</div><div class="line"> * Created by XiaoFeng on 16/12/21.</div><div class="line"> */</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ApiException</span> <span class="keyword">extends</span> <span class="title">RuntimeException</span> </span>{</div><div class="line"></div><div class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> USER_NOT_EXIST = <span class="number">100</span>;</div><div class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> WRONG_PASSWORD = <span class="number">101</span>;</div><div class="line"></div><div class="line"> <span class="keyword">private</span> <span class="keyword">int</span> errorCode;</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="title">ApiException</span><span class="params">(String detailMessage)</span> </span>{</div><div class="line"> <span class="keyword">super</span>(detailMessage);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="title">ApiException</span><span class="params">(<span class="keyword">int</span> resultCode)</span> </span>{</div><div class="line"> <span class="keyword">this</span>(resultCode, toApiExceptionMessage(resultCode));</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="title">ApiException</span><span class="params">(<span class="keyword">int</span> resultCode, String detailMessage)</span> </span>{</div><div class="line"> <span class="keyword">super</span>(detailMessage);</div><div class="line"> <span class="keyword">this</span>.errorCode = resultCode;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getErrorCode</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">return</span> errorCode;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * 映射服务器返回的自定义错误码,</div><div class="line"> * (此时的http状态码在[200, 300) 之间)</div><div class="line"> *</div><div class="line"> * <span class="doctag">@param</span> resultCode</div><div class="line"> * <span class="doctag">@return</span></div><div class="line"> */</div><div class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> String <span class="title">toApiExceptionMessage</span><span class="params">(<span class="keyword">int</span> resultCode)</span> </span>{</div><div class="line"> String message;</div><div class="line"> <span class="keyword">switch</span> (resultCode) {</div><div class="line"> <span class="keyword">case</span> USER_NOT_EXIST:</div><div class="line"> message = <span class="string">"该用户不存在"</span>;</div><div class="line"> <span class="keyword">break</span>;</div><div class="line"> <span class="keyword">case</span> WRONG_PASSWORD:</div><div class="line"> message = <span class="string">"密码错误"</span>;</div><div class="line"> <span class="keyword">break</span>;</div><div class="line"> <span class="keyword">default</span>:</div><div class="line"> message = <span class="string">"未知错误"</span>;</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> message;</div><div class="line"> }</div><div class="line"></div><div class="line">}</div></pre></td></tr></table></figure>
<h6 id="8-对Subscriber的封装,同时对onError异常再次封装"><a href="#8-对Subscriber的封装,同时对onError异常再次封装" class="headerlink" title="8. 对Subscriber的封装,同时对onError异常再次封装"></a>8. 对Subscriber的封装,同时对onError异常再次封装</h6><figure class="highlight java"><table><tr><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * 暴露给最上层的网络请求回调处理类</div><div class="line"> *</div><div class="line"> * Created by XiaoFeng on 16/12/28.</div><div class="line"> */</div><div class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">RxCallback</span><<span class="title">T</span>> <span class="keyword">extends</span> <span class="title">Subscriber</span><<span class="title">T</span>> </span>{</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * 成功返回结果时被调用</div><div class="line"> *</div><div class="line"> * <span class="doctag">@param</span> t</div><div class="line"> */</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">onSuccess</span><span class="params">(T t)</span></span>;</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * 成功或失败到最后都会调用</div><div class="line"> */</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">onFinished</span><span class="params">()</span></span>;</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">onCompleted</span><span class="params">()</span> </span>{</div><div class="line"> onFinished();</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">onError</span><span class="params">(Throwable e)</span> </span>{</div><div class="line"> String errorMsg;</div><div class="line"> <span class="keyword">if</span> (e <span class="keyword">instanceof</span> IOException) {</div><div class="line"> <span class="comment">/** 没有网络 */</span></div><div class="line"> errorMsg = <span class="string">"Please check your network status"</span>;</div><div class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (e <span class="keyword">instanceof</span> HttpException) {</div><div class="line"> <span class="comment">/** 网络异常,http 请求失败,即 http 状态码不在 [200, 300) 之间, such as: "server internal error". */</span></div><div class="line"> errorMsg = ((HttpException) e).response().message();</div><div class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (e <span class="keyword">instanceof</span> ApiException) {</div><div class="line"> <span class="comment">/** 网络正常,http 请求成功,服务器返回逻辑错误 */</span></div><div class="line"> errorMsg = e.getMessage();</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> <span class="comment">/** 其他未知错误 */</span></div><div class="line"> errorMsg = !TextUtils.isEmpty(e.getMessage()) ? e.getMessage() : <span class="string">"unknown error"</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> Toast.makeText(App.getInstance(), errorMsg, Toast.LENGTH_SHORT).show();</div><div class="line"></div><div class="line"> onFinished();</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">onNext</span><span class="params">(T t)</span> </span>{</div><div class="line"> onSuccess(t);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<blockquote>
<p>参考:<br><a href="https://gank.io/post/56e80c2c677659311bed9841" target="_blank" rel="external">https://gank.io/post/56e80c2c677659311bed9841</a><br><a href="http://tech.glowing.com/cn/glow-android-performance-optimization/" target="_blank" rel="external">http://tech.glowing.com/cn/glow-android-performance-optimization/</a><br><a href="http://www.jianshu.com/p/f3f0eccbcd6f" target="_blank" rel="external">http://www.jianshu.com/p/f3f0eccbcd6f</a><br><a href="http://wuxiaolong.me/2016/06/18/retrofits/" target="_blank" rel="external">http://wuxiaolong.me/2016/06/18/retrofits/</a><br><a href="http://stackoverflow.com/questions/35243785/rxjava-static-generic-utility-method-with-transformer" target="_blank" rel="external">http://stackoverflow.com/questions/35243785/rxjava-static-generic-utility-method-with-transformer</a></p>
</blockquote>
</div>
<div>
</div>
<div>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
<link itemprop="mainEntityOfPage" href="http://yoursite.com/2017/01/21/Android编码规范/">
<span style="display:none" itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="XiaoFeng">
<meta itemprop="description" content="">
<meta itemprop="image" content="/images/avatar.gif">
</span>
<span style="display:none" itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="Android背锅侠">
<span style="display:none" itemprop="logo" itemscope itemtype="http://schema.org/ImageObject">
<img style="display:none;" itemprop="url image" alt="Android背锅侠" src="">
</span>
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
<a class="post-title-link" href="/2017/01/21/Android编码规范/" itemprop="url">
Android 编码规范
</a>
</h1>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">Veröffentlicht am</span>
<time title="Post created" itemprop="dateCreated datePublished" datetime="2017-01-21T17:08:21+08:00">
2017-01-21
</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h1 id="Android-编码规范"><a href="#Android-编码规范" class="headerlink" title="Android 编码规范"></a>Android 编码规范</h1><blockquote>
<p><strong>任何一个傻瓜都能写出计算机可以理解的代码。唯有写出人类容易理解的代码,才是优秀的程序员。 – Martin Flower</strong></p>
</blockquote>
<p><br><br>团队协作项目,为了保持大家的代码一致性,进行一些代码格式的规范。</p>
<h2 id="1-基本要求"><a href="#1-基本要求" class="headerlink" title="1. 基本要求"></a>1. 基本要求</h2><ul>
<li>代码缩进使用4个空格,不是Tab键。</li>
<li>统一使用UTF-8编码,避免乱码问题。</li>
<li>除了注释,代码中不出现中文。</li>
<li>每个类写上必要的注释,类的说明,作者,修改时间,联系方式(可选)。</li>
<li>为了让他人可以容易看懂你的代码,请在关键地方做好注释。</li>
<li>变量和函数名命名使用“驼峰命名法”,资源相关文件命名使用“下划线命名法”。</li>
<li>代码中用到的<strong>字符串</strong>、<strong>颜色值</strong>和<strong>控件尺寸</strong>等资源,都统一写到对应的xml文件中(strings.xml, colors.xml, values.xml),方便代码维护和今后的国际化支持。</li>
<li><strong>强烈建议使用Android Studio或者Intellij IDEA。</strong></li>
<li><strong>使用 Ctrl+Shift+F 来格式化代码, 使用 Ctrl+Shift+O 来格式化 Import 包。</strong></li>
</ul>
<h2 id="2-项目架构"><a href="#2-项目架构" class="headerlink" title="2. 项目架构"></a>2. 项目架构</h2><ul>
<li>基本遵循<strong>MVP架构</strong>(Model/View/Presenter),把数据源,用户界面和业务逻辑各个层次解耦,增加代码可读性和维护性,也增强了代码的扩展性。</li>
<li>详细代码可参考<a href="https://github.com/googlesamples/android-architecture" target="_blank" rel="external">Google官方MVP架构示例</a>。</li>
</ul>
<h2 id="3-分包管理"><a href="#3-分包管理" class="headerlink" title="3. 分包管理"></a>3. 分包管理</h2><ul>
<li>按照组件类型来分包,而不是按业务模块来分包。业务有可能会变,但组件类型是基本不变的。另外,新加入的开发人员,对业务不熟悉,但对组件是很清楚的,理解快,入手也快。</li>
<li>对于稍微大一点的项目,可以先按模块分包,在子模块中再按照组件类型分包。</li>
<li><p>分包格式也可参考<a href="https://github.com/googlesamples/android-architecture" target="_blank" rel="external">Google官方MVP架构示例</a>。</p>
<pre><code>base:存放基础类的包,里面的类以Base为前缀,例如BaseActivity;
activity:存放activity的包,每个activity命名以Activity结尾,例如MainActivity;
fragment:存放fragment的包,每个fragment命名以Fragment结尾,例如ChatFragment;
receiver:存放receiver的包;
service:存放service的包;
adapter:存放adapter的包,每个adapter命名以Adapter结尾,例如EventItemAdapter;
common:存放一些公共常量,例如后端接口、SharedPreferenceKey、IntentExtra等;
utils:存放工具类的包,比如常见的工具类:LogUtils、DateUtils;
entity:存放实体类的包;
widget:存放自定义View的包;
</code></pre></li>
</ul>
<h2 id="4-命名规范"><a href="#4-命名规范" class="headerlink" title="4. 命名规范"></a>4. 命名规范</h2><h4 id="包命名"><a href="#包命名" class="headerlink" title="包命名"></a>包命名</h4><ul>
<li>采用反域名命名规则,包名全部小写,连续的单词只是简单地连接起来,不使用下划线。</li>
<li>一级包名为com,二级包名为xxx(可以是公司域名或者个人命名),三级包名根据应用进行命名,四级包名为模块名或层级名。如com.isa.crm.activity。</li>
</ul>
<h4 id="类命名"><a href="#类命名" class="headerlink" title="类命名"></a>类命名</h4><ul>
<li>大驼峰命名。除常见的缩写单词以外,不使用缩写,缩写的单词每个字母都大写。</li>
<li>公共的工具类建议以Utils、Manager为后缀,如LogUtils。接口命名遵循以上原则,以I为前缀,或以able或ible为后缀。</li>
</ul>
<h4 id="变量命名"><a href="#变量命名" class="headerlink" title="变量命名"></a>变量命名</h4><ul>
<li><p><strong>成员变量命名</strong><br>小驼峰命名。不推荐使用谷歌的前面加m的编码风格(如果使用团队中使用m,则统一使用)。</p>
</li>
<li><p><strong>临时变量命名</strong><br>小驼峰命名。</p>
</li>
<li><p><strong>常量命名</strong><br> 单词每个字母均大写。单词之间下划线连接。</p>
</li>
</ul>
<h4 id="方法命名"><a href="#方法命名" class="headerlink" title="方法命名"></a>方法命名</h4><p>小驼峰命名。使用动词或动名词。</p>
<h4 id="资源文件命名"><a href="#资源文件命名" class="headerlink" title="资源文件命名"></a>资源文件命名</h4><ul>
<li><p><strong>布局文件命名</strong><br> {组件_功能},下划线命名,全部小写。如:activity_login</p>
</li>
<li><p><strong>控件id命名</strong><br> {控件_范围_功能}, 下划线命名,全部小写。如:et_login_password</p>
</li>
<li><p><strong>字符串id命名</strong><br> {控件类型_功能}, 如:btn<em>login<br> 页面标题,命名格式为:title</em>{页面}<br> 按钮文字,命名格式为:btn<em>{按钮事件}<br> 标签文字,命名格式为:label</em>{标签文字}<br> 选项卡文字,命名格式为:tab<em>{选项卡文字}<br> 消息框文字,命名格式为:toast</em>{消息}<br> 编辑框的提示文字,命名格式为:hint<em>{提示信息}<br> 图片的描述文字,命名格式为:desc</em>{图片文字}<br> 对话框的文字,命名格式为:dialog_{文字}</p>
</li>
<li><p><strong>图片资源命名</strong><br> 图标资源以ic为前缀,例如ic_chat,指聊天图标;<br> 背景图片以bg为前缀,例如bg_login,指的是登录页的背景图;<br> 按钮图片以btn为前缀,例如btn_login,指的是登录按钮的图片,不过这只有一种状态,需要加上状态的可以在后面添加,例如btn_login_pressed,表示登录按钮按下的图片;<br> 当使用shape和selector文件为背景或者按钮时,命名参照以上说明;</p>
</li>
</ul>
<ul>
<li><strong>动画文件命名</strong><br> {动画类型_动画方向},下划线命名法,全部小写。如:fade_in</li>
</ul>
<h4 id="常用控件缩写"><a href="#常用控件缩写" class="headerlink" title="常用控件缩写"></a>常用控件缩写</h4><table>
<thead>
<tr>
<th>控件</th>
<th>缩写</th>
</tr>
</thead>
<tbody>
<tr>
<td>Linearlayout</td>
<td>ll</td>
</tr>
<tr>
<td>RelativeLayout</td>
<td>rl</td>
</tr>
<tr>
<td>TextView</td>
<td>tv</td>
</tr>
<tr>
<td>EditText</td>
<td>et</td>
</tr>
<tr>
<td>Button</td>
<td>btn</td>
</tr>
<tr>
<td>ImageButton</td>
<td>imgBtn</td>
</tr>
<tr>
<td>ImageView</td>
<td>iv</td>
</tr>
<tr>
<td>CheckBox</td>
<td>chb</td>
</tr>
<tr>
<td>ListView</td>
<td>lv</td>
</tr>
<tr>
<td>GridView</td>
<td>gv</td>
</tr>
<tr>
<td>ScrollView</td>
<td>sv</td>
</tr>
<tr>
<td>WebView</td>
<td>wv</td>
</tr>
<tr>
<td>ProgressBar</td>
<td>proBar</td>
</tr>
<tr>
<td>RadioButton</td>
<td>rb</td>
</tr>
<tr>
<td>Spinner</td>
<td>spn</td>
</tr>
</tbody>
</table>
<h2 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h2><ol>
<li><p>其他内容参见谷歌编码规范<br>中文<a href="http://blog.sina.com.cn/s/blog_48d491300100zwzg.html#use-todo-comments" target="_blank" rel="external">http://blog.sina.com.cn/s/blog_48d491300100zwzg.html#use-todo-comments</a><br>英文<a href="http://source.android.com/source/code-style.html" target="_blank" rel="external">http://source.android.com/source/code-style.html</a></p>
</li>
<li><p>其他未写明的欢迎补充,现有的有疑惑的欢迎提问,有缺点的欢迎质疑讨论修改。</p>
</li>
</ol>
<p><br><br><br><br></p>
<h1 id="新浪微博Android-SDK-Java代码规范"><a href="#新浪微博Android-SDK-Java代码规范" class="headerlink" title="新浪微博Android SDK Java代码规范"></a>新浪微博Android SDK Java代码规范</h1><p>代码规范的重要性不言而喻,但是一般公司都不太重视代码规范。在代码走读的时候,由于没有统一的规范,导致每个人都认为自己写的是对的。</p>
<p>在查看新浪微博Android SDK代码的时候,发现了个<a href="https://github.com/sinaweibosdk/weibo_android_sdk/tree/master/demo-src/WeiboSDK/src/com/sina/weibo/sdk/codestyle" target="_blank" rel="external">codestyle包</a>。一个短小精悍的CodingRuler类却包含了比较全面的Java代码规范。</p>
<p>贴出来分享一下。当然,如果想要更详细的,写以查看 <a href="http://source.android.com/source/code-style.html" target="_blank" rel="external">Google Java Style</a></p>
<ol>
<li><p><strong>weibo_android_sdk/demo-src/WeiboSDK/src/com/sina/weibo/sdk/codestyle/CodingRuler.java</strong></p>
<pre><code>/*
* 文件名(可选),如 CodingRuler.java
*
* 版本信息(可选),如:@version 1.0.0
*
* 版权申明(开源代码一般都需要添加),如:Copyright (C) 2010-2013 SINA Corporation.
*/
package com.sina.weibo.sdk.codestyle;
/**
* 类的大体描述放在这里。
*
* <p>
* <b>NOTE:以下部分为一个简要的编码规范,更多规范请参考 ORACLE 官方文档。</b><br>
* 地址:http://www.oracle.com/technetwork/java/codeconventions-150003.pdf<br>
* 另外,请使用 UTF-8 格式来查看代码,避免出现中文乱码。<br>
* <b>至于注释应该使用中文还是英文,请自己行决定,根据公司或项目的要求而定,推荐使用英文。</b><br>
* </p>
* <h3>1. 整理代码</h3>
* <ul>
* <li>1.1. Java 代码中不允许出现在警告,无法消除的警告要用 @SuppressWarnings。
* <li>1.2. 去掉无用的包、方法、变量等,减少僵尸代码。
* <li>1.3. 使用 Lint 工具来查看并消除警告和错误。
* <li>1.4. 使用 Ctrl+Shift+F 来格式化代码,然后再进行调整。
* <li>1.5. 使用 Ctrl+Shift+O 来格式化 Import 包。
* </ul>
*
* <h3>2. 命名规则</h3>
* <h3>2.1. 基本原则</h3>
* <ul>
* <li>2.1.1. 变量,方法,类命名要表义,严格禁止使用 name1, name2 等命名。
* <li>2.1.2. 命名不能太长,适当使用简写或缩写。(最好不要超过 25 个字母)
* <li>2.1.3. 方法名以小写字母开始,以后每个单词首字母大写。
* <li>2.1.4. 避免使用相似或者仅在大小写上有区别的名字。
* <li>2.1.5. 避免使用数字,但可用 2 代替 to,用 4 代替 for 等,如 go2Clean。
* </ul>
*
* <h3>2.2. 类、接口</h3>
* <ul>
* <li>2.2.1. 所有单词首字母都大写。使用能确切反应该类、接口含义、功能等的词。一般采用名词。
* <li>2.2.2. 接口带 I 前缀,或able, ible, er等后缀。如ISeriable。
* </ul>
*
* <h3>2.3. 字段、常量</h3>
* <ul>
* <li>2.3.1. 成员变量以 m 开头,静态变量以 s 开头,如 mUserName, sInstance。
* <li>2.3.2. 常量全部大写,在词与词之前用下划线连接,如 MAX_NUMBER。
* <li>2.3.3. 代码中禁止使用硬编码,把一些数字或字符串定义成常用量。
* <li>2.3.4. 对于废弃不用的函数,为了保持兼容性,通常添加 @Deprecated,如 {@link #doSomething()}
* </ul>
*
* <h3>3. 注释</h3>
* 请参考 {@link #SampleCode} 类的注释。