-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
339 lines (202 loc) · 137 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>沈文兴</title>
<link href="/atom.xml" rel="self"/>
<link href="https://shenwenxing.com/"/>
<updated>2018-01-03T03:04:49.129Z</updated>
<id>https://shenwenxing.com/</id>
<author>
<name>shenwenxing</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>芳华</title>
<link href="https://shenwenxing.com/youth.html"/>
<id>https://shenwenxing.com/youth.html</id>
<published>2017-12-31T02:56:07.000Z</published>
<updated>2018-01-03T03:04:49.129Z</updated>
<content type="html"><![CDATA[<p>看完电影芳华后,觉得相当不错,遂萌生了找原著出来看的想法,于是找到了这本书,下面的内容部分出自芳华这本书,部分出自电影芳华。</p><p>《芳华》描述的是70年代的中国,那个时代的国家远没有现在这样强大,国内外形式都非常微妙,国家一直处于备战状态,文工团担负起了军队文艺宣传的特殊使命,故事就发生在文工团中,透过文工团,可以管中窥豹,触摸到那个特殊的时代。</p><a id="more"></a><p>文工团作为一个小团体,里面也由不同的阶层构成,特权阶级以郝淑文为代表,底层阶级的代表是书中的主人公,刘峰和何小曼(电影中改名为何小萍),刘峰的父亲是木匠,小曼的亲生父亲在迫害中过世了,母亲改嫁到一个革命家庭中,自从母亲改嫁后,小曼就再也不知道幸福是何物了。处于中间阶层的是萧穗子和林丁丁,她们俩没有郝淑文那么好的背景,但是比刘峰和何小曼条件要好很多。来自不同阶层的他们最终也走向了不同的人生,这个接下来陈述。</p><p>首先我想讲的是刘峰,前面已经提到,刘峰来自木匠家庭,典型的无产阶级,刘峰在文工团有个很有名的称号–“活雷锋”,电影中对“活雷锋”这一形象进行了包装,显得表面上受文工团的人尊重,书中的描写更直接,“活雷锋”只是一个做各种小事和杂事的人。刘峰对林丁丁的感情过于卑微,加上林丁丁个人的择偶标准根本就没有把刘峰这一类人划进去,刘峰对丁丁的爱情注定会以悲剧结尾。刘峰个人本来有更好的机会离开文工团,然而为了自己单恋着的女人放弃了,结合丁丁本人以及后面的结局来看,真替他感到可惜。书中还描述了刘峰同一个妓女同居长达两年之久,最后帮助了那个性工作者走向了正常的生活,电影中为了保持主人公光亮的形象,对这一剧情进行了删减。刘峰作为一个抗战的英雄,晚年的时候,过得并不是特别好,谋生用的小车也被联防办给克扣了,很难想象,如果没有郝淑文的出现,刘峰要怎么赎回自己的车,曾经的“活雷锋”,战斗英雄,晚景如此凄凉,不禁令人扼腕叹息。</p><p>何小曼童年的不幸伴随着亲生父亲的去世和母亲的改嫁拉开了序幕,在第二个家庭生活的院子里,大家都把她看做拖油瓶,小曼的母亲对她也谈不上好,记忆中最后一次母亲抱着她是小曼自己故意洗冷水生病发烧不止的时候。母亲答应给她的红毛线衣最终也给了妹妹,小曼将红毛衣拆了,染成黑色,并织成了新的毛衣。小曼渴望这逃离这个将她排斥在外的家庭,当她加入文工团的时候,她以为自己实现了梦想,然而在文工团,小曼的处境并没有得到太大的改善,她同样受到了大部分人的孤立,一直活在别人的笑话中。</p><p>现在想谈一谈郝淑文,开篇对郝淑文的描述非常的到位,“郝淑文是那个提高了我们集体平均体重的丰满女兵,一米六九,还没碰到她就能感觉到她青春体温的冲击波”。七十年代的时候,国内物质条件还不是很好,郝淑文作为某空军部首长的女儿,在物质条件上显然没有受到任何的掣肘。书中的郝淑文比电影中要坏一些,勾引萧穗子的男朋友,并在和他睡了之后,策反萧穗子男朋友举报萧穗子,孤立萧穗子。在举报萧穗子后,郝淑文又离开了萧穗子的男朋友。书中郝淑文跟一个军二流子结了婚,并在军二流子事业成功后,两人离婚,在北京过着单身生活。电影中郝淑文在刘峰车被扣押的时候,及时出现,并骂了联防办的人,我想着郝淑文是空军首长的女儿,可能会借此机会教训一下联防办的人,然而电影中并没有向这方面发展,可能这就是现实吧,现实会更冰冷。</p><p>最后想讲一下政委这个人,政委权衡利弊,对小曼的故意装病暂时姑息,并利用小曼的虚荣心,完成了对骑兵慰问演出,成功阻止了一场潜在的动乱。在利用完小曼之后,将其下放到战前医院,整个过程做得滴水不漏,手段令人无话可说,好生厉害,只是不免让人心寒,可能对于某些在位者和高层来说,底下的都是任人操控的棋子吧。</p><p>读完整本书,突然想到一个词,叫危机感,文工团那么大的组织,说解散也就解散了,在完成了它的历史使命后,不可避免的走向了灭亡。对于我们来说,任何时候都不要认为当前的状态可以高枕无忧,时刻考虑到可能会出现的危机,为自己的未来做好准备。</p><p>一千个人眼中有一千个哈姆雷特,可能每个人对书中的解读都不一样,文中是我个人的解读,肯定会存在误解和不足或者过于解读,但是不管怎样,《芳华》书和电影都值得一看。</p>]]></content>
<summary type="html">
<p>看完电影芳华后,觉得相当不错,遂萌生了找原著出来看的想法,于是找到了这本书,下面的内容部分出自芳华这本书,部分出自电影芳华。</p>
<p>《芳华》描述的是70年代的中国,那个时代的国家远没有现在这样强大,国内外形式都非常微妙,国家一直处于备战状态,文工团担负起了军队文艺宣传的特殊使命,故事就发生在文工团中,透过文工团,可以管中窥豹,触摸到那个特殊的时代。</p>
</summary>
<category term="读书笔记" scheme="https://shenwenxing.com/categories/reading/"/>
<category term="小说" scheme="https://shenwenxing.com/tags/novel/"/>
</entry>
<entry>
<title>一本书读懂财报</title>
<link href="https://shenwenxing.com/report-financial-report.html"/>
<id>https://shenwenxing.com/report-financial-report.html</id>
<published>2017-12-04T08:16:25.000Z</published>
<updated>2017-12-04T08:38:30.000Z</updated>
<content type="html"><![CDATA[<p>在读《穷查理宝典》的时候,书中反复强调投资过程中读懂公司财务报表的重要性,从网上查了一下非会计专业的人读懂财报的入门书,不少人推荐了《一本书读懂财报》这本书,作者是清华大学会计学主任肖星老师。读完后,对公司的财务报表有了一定的认识,整本书通俗易懂,适合初学者入门。</p><p>要理解财务报表,得清楚什么是财务报表,以及财务报表有什么作用,我们能用财务报表做些什么。财务报表是财报中很重的一部分,本书中重点介绍了财务报表,它一共包含三张表,分别是资产负债表、利润表和现金流表。</p><a id="more"></a><p>资产负债表由资产、负债以及所有者权益构成,如图(1)所示,通俗的讲,负债和所有者权益表示企业的钱是从哪儿来的,资产表示钱都花在了哪些地方,资产=负债+所有者权益,这是会计中的恒等式。资产分为流动资产和非流动资产,变现能力快速的称为流动资产,否则称为非流动资产,比如货币资金、应收账款、存货都属于流动资产,而厂房、生产机器等属于非流动资产。同理,负债也分为流动负债和非流动负债。</p><p><img src="https://note.youdao.com/yws/api/personal/file/WEB9d90f4e4eb19c41f1af68d831915c9eb?method=download&shareKey=e86f135a0b523d1e8ae19c108c90dd55" alt="资产负债表"></p><center> 图(1) 资产负债表 </center><p>利润表反映了企业究竟有没有赚到钱,利润表中第一个项目是营业收入,企业卖出的产品称为营业收入,卖出的产品是营业成本。营业收入减去营业成本就得到了整个企业的毛利润,毛利润扣掉营业税金以及营业费用,管理费用等就得到了营业利润。营业利润+营业外收入-营业外支出+补贴收入+汇兑损益=利润总额。利润总额-所得税=净利润,而净利润就是企业所赚到的钱。利润表不光反应了企业是否赚到钱,还反应了钱是从哪些地方赚来的,以及长时间内是否能赚到钱。利润表和资产负债表一起反应了企业能活成什么样子。</p><p><img src="https://note.youdao.com/yws/api/personal/file/WEB515648bc3a6dfb2a18b7552ad7fe9a49?method=download&shareKey=c885317212bbdb188f3dcb79ab182e86" alt="利润表"></p><center> 图(2)利润表 </center><p>最后一张表是现金流表,现金流表跟企业的经济活动息息相关,企业的经济活动分为三种,融资、投资和经营。所以现金流表也分为三部分,融资现金流、投资现金流、经营现金流。现金流表向我们展示了资产负债表上货币资金变化的原因。</p><p>有了这三张表,我们就可以从不同的维度去了解企业的状况,在第一个维度上,现金流表描述了一个企业是否能生存下去,即风险的角度;在第二个维度上,资产负债表和利润表反应了企业有多少家底,有多少收益,即收益的视角。要了解一家企业,这两个维度缺一不可。当我们更关注风险的时候,需要分析现金流表,当我们更关注利润的时候,需要分析利润表。</p><p>了解财务报表后,还需要了解一些基本的财务分析方法,才能真正的认识财务报表,财务分析中最常用的是同型分析,同型分析就是算出报表中每一项所占的百分比,根据不同项的比重分析企业的状况。除了同型分析外,还有比率分析等方法。</p><p>对公司财务报表进行分析,就能了解企业的状况,也可以找出企业存在的潜在风险,实际操作过程中,需要合理的选取报表和分析方法。无论是投资还是管理公司,财务报表都是非常重要的辅助手段。</p>]]></content>
<summary type="html">
<p>在读《穷查理宝典》的时候,书中反复强调投资过程中读懂公司财务报表的重要性,从网上查了一下非会计专业的人读懂财报的入门书,不少人推荐了《一本书读懂财报》这本书,作者是清华大学会计学主任肖星老师。读完后,对公司的财务报表有了一定的认识,整本书通俗易懂,适合初学者入门。</p>
<p>要理解财务报表,得清楚什么是财务报表,以及财务报表有什么作用,我们能用财务报表做些什么。财务报表是财报中很重的一部分,本书中重点介绍了财务报表,它一共包含三张表,分别是资产负债表、利润表和现金流表。</p>
</summary>
<category term="读书笔记" scheme="https://shenwenxing.com/categories/reading/"/>
<category term="金融" scheme="https://shenwenxing.com/tags/finance/"/>
</entry>
<entry>
<title>学习之道</title>
<link href="https://shenwenxing.com/learing-how-to-learn.html"/>
<id>https://shenwenxing.com/learing-how-to-learn.html</id>
<published>2017-10-30T13:32:27.000Z</published>
<updated>2017-10-31T02:14:36.000Z</updated>
<content type="html"><![CDATA[<p>学习对于每个人来说都会,但不是所有人的学习效率都非常高,工作后用于学习的时间锐减,个人觉得提高学习的效率非常重要。于是在知乎上检索了关于学习的书,很多答主都推荐了这本书,通读了一遍该书,有不少收获。</p><p>书中谈到和学习相关的三个方面,思维模式,记忆,学习的小技巧。学习主要依靠大脑,所以弄清楚大脑的运作模式有助于我们学习。记忆毫无疑问也是学习中重要的一部分,书中花了很大篇幅介绍怎样去记忆以及遗忘做斗争。最后一部分包含一些学习的小tips来帮助我们更好的学习,部分小技巧自己在之前的学习中有用到过。下面分别介绍这三个方面。</p><a id="more"></a><p>大脑的思维模式分为专注思维和发散思维两种模式,精神十分集中的时候,大脑处于专注思维模式,其他情况下,大脑均处于发散思维模式,这两种模式并没有好坏之分,专注模式能在短时间内获取大量的信息,是学习新技能的过程中必不可少的,但是当大脑处于专注模式的时候,容易陷入定式思维,很难找到解决问题的方法。发散思维和深度思索和创造力相关,有助于将旧的知识和新的知识连接起来,我们要善于利用自己的发散思维。从专注思维切换到发散思维非常简单,只需要切换到其他的事情即可,比如睡觉,洗澡,散步等,以前读书的时候,很多难题的解法都是在上洗手间或者睡觉的时候想到的,这也印证了发散思维确实有利于解决困难的问题。想要发散思维在睡觉的时候任然发挥作用,需要在睡前把要解决的问题仔细想清楚。</p><p>整个学习的过程可以说是一个与遗忘不断做斗争的过程。记忆为分工作记忆和长期记忆,工作记忆相当于计算机内存,特点是数据比较新,容易丢失,长期记忆类似于计算机的硬盘,特点是存储时间长,检索需要消耗时间,学习整个过程可以认为是对工作记忆进行加工,最终存储在永久记忆中,以便在合适的时机去应用。书中提到一个概念,叫记忆组块,表示多个知识点集合在一起形成一个单元,这样的记忆单元有助于我们长期记忆,形成记忆组块分为三个步骤,首先要专注,专注才能接受新的信息,其次要理解,理解后才更容易记忆,最后要了解背景,知道在什么场合可以用。<br>为了更快的记忆,可以尝试下面的小技巧:<br>(1)编故事,越新奇的故事容易记住。<br>(2)少划线,划线会给自己一种错觉,我已经记住了,或者让自己推迟到以后去记忆。<br>(3)动手写下来,最好写在记事本上,最简单粗暴的方法就是抄书。<br>(4)读给自己听,有时候读得多了,也就记住了。<br>(5)进行有氧运动。<br>(6)尝试在不同的地点去记忆相同的内容,这样记忆会更深刻。<br>短时间记住后,还需要持续和遗忘作斗争,可以使用下面的方法:<br>(1)回顾之前的内容,刚学的东西最好24小时内进行回顾,可以参考记忆曲线。下次拿起书本的时候也是比较好的回忆时机。<br>(2)提取出主要内容,然后刻意的去练习。<br>(3)进行自我测试,把忘记的内容重新记忆。</p><p>最后一部分关于学习技巧的内容比较多,可以分成三部分,一是怎样学习新的知识,二是怎样克服拖延,三是解题的小技巧。<br>学习新知识的时候,要学会先宏观的将要学的内容扫一遍,掌握新知识的整体框架,可以看看目录,根据目录把知识框架画出来,记住自己有疑问的地方。接着开始学习,学习的过程中不要害怕别人比你学的快,以最终能理解和使用为目的,这样有助于减轻学习过程中的思想包袱。学习的时候可以穿插着学习,会有助于激活发散思维,比如先学一段数学,然后学一段时间英语等。学习后,要不停的运用刚学的知识点,有助于加深对知识点的理解。还有一个能加深理解的方式就是教别人,学然后和知不足,教然后知困说的就是这个道理。最后,最重要的一点,学习也要注意休息,保证充足的睡眠,不然前面所有的技巧都是徒劳。<br>拖延是阻碍我们学习的敌人,每个人或多或少的都会有拖延症,当你遇到一个很难解决的难题的时候,短时间内大脑如果找不出解决问题的方案,就想逃避问题,寻求开心的事情。个人对这一点也深有感触,有时候时间越紧越想着去打游戏放松自己。要克服拖延就得先正视拖延,知道是一种正常的心理状态,不要有心理负担,然后可以使用类似番茄钟的方法使自己快速进入状态,也可以预先设定一个奖励来促使自己完成任务。<br>书中提到了一些解题技巧,不要只用脑海中想到的第一种方案,一般第一种方案都和定式思维有关。倾听他人的意见,很多时候都是当局者迷旁观者清。考试的时候可以使用先难后易的方案,但是要主要控制好自己,不要在太难的题目上浪费太多时间,理解题目意思后去做其他题,发散思维会继续运作,在后台思考难题。</p><p>书中介绍了很多的理论知识,但是这仅仅是一个开始,要提高自己的学习效率最重要的一步是实践,在实践的过程中,去体会作者提到的各种技巧,才能取得真正的收获。</p>]]></content>
<summary type="html">
<p>学习对于每个人来说都会,但不是所有人的学习效率都非常高,工作后用于学习的时间锐减,个人觉得提高学习的效率非常重要。于是在知乎上检索了关于学习的书,很多答主都推荐了这本书,通读了一遍该书,有不少收获。</p>
<p>书中谈到和学习相关的三个方面,思维模式,记忆,学习的小技巧。学习主要依靠大脑,所以弄清楚大脑的运作模式有助于我们学习。记忆毫无疑问也是学习中重要的一部分,书中花了很大篇幅介绍怎样去记忆以及遗忘做斗争。最后一部分包含一些学习的小tips来帮助我们更好的学习,部分小技巧自己在之前的学习中有用到过。下面分别介绍这三个方面。</p>
</summary>
<category term="读书笔记" scheme="https://shenwenxing.com/categories/reading/"/>
<category term="学习方法" scheme="https://shenwenxing.com/tags/study-method/"/>
</entry>
<entry>
<title>富爸爸穷爸爸</title>
<link href="https://shenwenxing.com/rich-dad-poor-dad.html"/>
<id>https://shenwenxing.com/rich-dad-poor-dad.html</id>
<published>2017-09-30T16:36:15.000Z</published>
<updated>2017-09-30T16:40:58.000Z</updated>
<content type="html"><![CDATA[<p>《富爸爸穷爸爸》是一本关于投资理财的书,写这本书的初衷是为了帮助大家理解作者所开发的一款叫现金流的游戏,后来”游戏说明书”比游戏本身要畅销多了。书中讲述主人公两位爸爸对待财富上的不同观点,在讲故事的同时传达自己的观点。读书的时候,感觉我的很多观点,或者说社会上大部分人的观点都和书中穷爸爸的思维方式比较类似,读完后,觉得受益匪浅,下面简单介绍该书的内容。</p><a id="more"></a><p>书中描述了大部分人一生的生活:努力学习,考上好大学,毕业后找一个高薪的工作,然后买属于自己房子,买房子之后偿还房贷,收入增加后将小房子换成大房子,然后继续偿还房贷。虽然作者写的是美国人的生活,但是对比一下现实生活,我周边的很多人包括我自己也是这样生活的,之前也没觉得这样有什么不对。作者指出了,这里面最大的误区在于将自住房看成资产,从而陷入穷人生活的恶性循环中。资产和负债是贯穿整本书的重要的概念之一,资产就是能把钱放进你口袋里的东西,负债是把钱从你口袋里取走的东西。自住房需要交各种费用,一直源源不断的将现金从口袋里流出,所以是负债。这样看来,房子和车子只要不带来正的收益,都属于负债。作者也提到了典型的资产有包括股票,共同基金,债券,版税等等。</p><p>作者认为当被动收入超过总支出,就可以称为财务自由,被动收入就是不用你在现场,能够自己运转,给你带来的收入。要能实现财富的增长,需要了解四方面的知识:会计,投资,市场,法律。会计能让你看懂财务报表,鉴别好的生意与坏的生意。投资能让你实现钱生钱。市场让你了解供需关系,知道买入卖出的时机。法律能保障你的财产不受侵犯。这四个方面每一项都不简单,作者书中没有谈到要掌握到什么程度,在我看来需要尽可能深入的了解。另外,要实现财务自由还需要早点建立自己的事业,这里需要区分一下事业和工作,工作必须在场,而事业可以不在场。</p><p>有了上面的知识后,就需要行动起来。行动的之前要调整好心态,每个人都不是天生会理财的,就像骑自行车一样,刚开始可能会晃晃悠悠,越到后面会骑得越稳。理财也是这样,要反复的去尝试,去实践,克服对金钱的恐惧,不要害怕失败。实践的时候不要想着改变别人,要改变自己,改变自己比改变别人要容易得多。在前进的路上,要懂得停下来,思考自己手上的活儿,考虑当前的工作是否值得去做,有时候我们看似勤奋,实际上是懒惰,懒得思考是否有更好的选择,只知道按照之前的步骤去走,用战术上的勤奋掩饰战略上的懒惰。实践的时候记得用专门的人做专门的事情,不要什么事情都自己上,个人的精力是有限的,懂得与别人合作才能更快速的实现财富积累。</p><p>看完这本书后,也体验了一下作者开发的游戏,有几点感触,第一是借贷并不都是坏事情,适当的运用贷款能达到事半功倍的效果。第二是机会稍纵即逝,机会来了要把握住,如果资金不够,可以去借贷。第三是资金少的时候要开源节流,偿还掉利率高的债务。第四是不要疯狂的借贷,保证每月的收益为正,并且能够应对各种突发状况。第五前期资金少的时候可以做股票方面的投资,资金多了后,转型其他产业。当然游戏中模型也有不足,比如投资的类型过少,银行不能存款,只能贷款,贷款的利率永远不变,和其他玩家没有利益互动等等。不过整个游戏玩下来,对书中所介绍的概念也加深了不少。推荐看完书后去体验一下现金流的游戏。</p>]]></content>
<summary type="html">
<p>《富爸爸穷爸爸》是一本关于投资理财的书,写这本书的初衷是为了帮助大家理解作者所开发的一款叫现金流的游戏,后来”游戏说明书”比游戏本身要畅销多了。书中讲述主人公两位爸爸对待财富上的不同观点,在讲故事的同时传达自己的观点。读书的时候,感觉我的很多观点,或者说社会上大部分人的观点都和书中穷爸爸的思维方式比较类似,读完后,觉得受益匪浅,下面简单介绍该书的内容。</p>
</summary>
<category term="读书笔记" scheme="https://shenwenxing.com/categories/reading/"/>
<category term="投资" scheme="https://shenwenxing.com/tags/investment/"/>
</entry>
<entry>
<title>学会提问</title>
<link href="https://shenwenxing.com/asking-the-right-questions.html"/>
<id>https://shenwenxing.com/asking-the-right-questions.html</id>
<published>2017-08-31T14:28:06.000Z</published>
<updated>2017-09-12T03:48:41.000Z</updated>
<content type="html"><![CDATA[<p>《学会提问》一书是批判性思维领域的经典之作,书中的批判性思维主要针对阅读而言。批判性思维主要包涵三个方面:(1)有一套相互关联、环环相扣的关键问题意识;(2)提出和回答关键问题的能力;(3)积极主动的利用关键问题的强烈愿望。常见的思维方式可以分成海绵式思维方式和淘金式思维方式,海绵式思维方式是指接受信息的时候一股脑儿全部接收过来,能够记清楚一些小细节;淘金式思维方式是指接收知识的时候会与知识展开互动,有选择的去接收知识。批判性思维方式就是淘金式的思维方式。</p><a id="more"></a><p>当今社会充满了各种信息,获取海量的信息和过去比较起来变得轻而易举,如果无差异的接收信息,会导致接收到信息千头万绪,很难将信息转变为对自己的知识。这就要求我们熟练掌握批判性思维方式,提升获取信息的能力。锻炼自己的批判性思维需要从论题、结论、理由三个方面入手,这也是本书的核心。</p><p>论题是指讨论或者证明的问题,论题是后续的驱动力,论题分为描述性论题和规定性论题,描述性论题试图还原世界本身是什么样的,而规定性论题提出世界应该是什么样的。比如音乐是否有助于提高人的数学能力?xx大学的科研水平怎么样?都属于描述性论题。公立学校应不应教授智能设计?对于医疗补助的欺诈应该采取什么样的措施?属于规范性论题。只有理解了他人想阐明的问题,才能有的放矢的去批判接收对方观点。</p><p>结论是对论题的回答,是作者对论题持有的观点,结论一般有比较明显的特征,会有特定的指示词,“因此”,“表明”,“由此可知”等都代表后面跟着的有可能是结论。结论的位置一般出现在文章的开头或者结尾部分。</p><p>结论要成为结论,必须有理由的支撑,理由就是用来证明结论的看法、证据、隐喻、类比和其他陈述。理由是模具,结论根据理由成型。从理由到结论的过程叫推理。推理的过程如果不严谨,结论就不可信,我们所需要做的,就是分析推理的过程,思考结论是否合理。</p><p>推理的过程有时候并不是显而易见的,需要补充一些推理过程中的假设,才能从理由推导出结论。假设分为价值观假设和描述性假设。价值观假设和前面提到过的规定性论题是相关的,描述性假设和描述性论题是相关的。比如,工作场合要提供差别工资制,就暗含了相对于合作的价值观来说更加认同竞争的价值观。推理的过程中,必不可少的要用到各种证据,要时刻对各种各样证据的可信度持怀疑态度。直觉,典型案例,个人经历,当事人的证词都是效力比较弱的证据。数据一般被认为是比较有说服力的证据,但是也要慎重考虑,面对数据证据的时候,要多考虑绝对数据和相对数据的区别。</p><p>整本书各个章节的结构相对统一,都是先抛出问题,然后阐述问题,阐述的过程中会有很多案例,会提醒读者论证的时候需要注意哪些地方,每章的结尾有三道练习题,针对本章的主题进行训练。书的前几章结构很紧凑,后面有点过于细节,稍显啰嗦。整体来说,是一本需要阅读后反复运用才能掌握的书。</p>]]></content>
<summary type="html">
<p>《学会提问》一书是批判性思维领域的经典之作,书中的批判性思维主要针对阅读而言。批判性思维主要包涵三个方面:(1)有一套相互关联、环环相扣的关键问题意识;(2)提出和回答关键问题的能力;(3)积极主动的利用关键问题的强烈愿望。常见的思维方式可以分成海绵式思维方式和淘金式思维方式,海绵式思维方式是指接受信息的时候一股脑儿全部接收过来,能够记清楚一些小细节;淘金式思维方式是指接收知识的时候会与知识展开互动,有选择的去接收知识。批判性思维方式就是淘金式的思维方式。</p>
</summary>
<category term="读书笔记" scheme="https://shenwenxing.com/categories/reading/"/>
<category term="方法论" scheme="https://shenwenxing.com/tags/methodology/"/>
<category term="批判性思维" scheme="https://shenwenxing.com/tags/critical-thinking/"/>
<category term="学习方法" scheme="https://shenwenxing.com/tags/study-method/"/>
</entry>
<entry>
<title>枪炮、病菌与钢铁</title>
<link href="https://shenwenxing.com/guns-germs-and-steel.html"/>
<id>https://shenwenxing.com/guns-germs-and-steel.html</id>
<published>2017-05-31T04:31:01.000Z</published>
<updated>2017-09-11T09:43:13.000Z</updated>
<content type="html"><![CDATA[<p>枪炮病菌与钢铁是一本介绍人类社会史的书,探索的主题是为什么不同大陆上的人类发展的速度会不一样,反应到具体问题上是,为什么最终是欧亚大陆的人征服了美洲大陆的人,而不是反过来的情况。关于这类问题的讨论早已存在,一部分人持有种族主义观点,认为欧亚大陆的人要比美洲大陆的人更为聪明,作者不同意这种观点,并在书中详细阐述了自己的观点。</p><a id="more"></a><p>根据现在人类化石出现的年代,可以分析人类的发展史。人类的历史与动物的历史分道扬镳,大约在700万年前。人类生活始于非洲,历经多次迁徙到达其他大陆,迁徙的路线为非洲,欧亚大陆,澳大利亚,北美洲,南美洲。人类社会开始于非洲,非洲的发展速度并没有领先于其他大陆,为什么会出现这种现象呢。</p><p>远古时候,地球上的所有人类大部分时间都是靠着猎捕野兽和采集野生植物生活,直到11000年前,部分种族开始转向粮食生产。粮食生产的意义对于整个人类进化起到非常重要的作用,有了粮食的生产,人类获取食物的效率大大的提高,人类开始定居生活,有了定居的生活,才会出现多余的粮食储备,多余的粮食储备会促进人口的增长。多余的粮食和人口的增长导致出现神职,军人,管理者等其他专职的人员,从而推动整个人类社会的往前发展。</p><p>早期的农业中,最重要的两项活动是驯化植物和驯化动物。根据当前考古学的研究,只有少数几个区域出现了驯化动植物的记录,这几个区域是西南亚(新月沃地);中国;中美洲(墨西哥中部和南部以及中美洲的毗连地区);南美的安第斯山脉地区;美国东部,其他区域的动植物都是这些地区驯化后传播过去的。对于植物驯化来说,可被驯化的植物都具有很多相同的特征,出现这些特征的植物则只在上面提到的区域里面出现,植物的驯化在早期不是人类的主动行为,植物吸引人类去采摘果实,从而达到基因传播的目的。人类通过长期的选择性的采摘果实,无意中达到了驯化植物的目的。对比起来,驯化动物会复杂一点,正如幸福的家庭都是幸福的,不幸的家庭各有各的不幸一样,可驯化的动物都是可驯化的,不可驯化的动物各有各的不可驯化之处。可驯化的动物一定要满足以下特点:(1)草食或者杂食(2)成长速度快(3)圈养中可以繁殖(4)性情温和(5)不容易受惊吓(6)群居。有的大陆虽然动物众多,但是能同时满足上述所有条件的动物少或者没有。正如马太所说:”被传唤者众,而被选中者少”。</p><p>农业的出现促进了社会的分工,进而促使人类部落渐渐发展成国家,帝国。前面提到过欧亚大陆的帝国征服了新大陆,主要有三个原因,枪炮,钢铁,病菌。也就是本书的标题。钢铁的匕首和火器能短时间内对人体造成巨大的伤害,但是征服过程中起决定性作用的是病菌。病菌是伴随着驯养动物而产生的,动物携带的病菌会传染到人身上,驯养动物的地区由于长时间的接触,已经对这些病菌形成了抗体,当他们入侵没有驯养相应动物地区的时候,病菌会随之传播过去,对被侵略的地区形成毁灭性打击,据统计,病菌杀死的印第安人占到90%以上。</p><p>之前有接触过一款叫《文明》的游戏,游戏的进程为不同种族的人类进化史,游戏中的很多元素和本书的内容是重合的,比如游戏中科技树起源于农业,发展完农业后才会出现后面的冶炼,灌溉等相对高的科技。游戏的时候,发现治陶排科技是很多重要科技的前置科技,当时不太理解,读这本书的时候了解到,治陶业的发展使得食物的储存时期更长,从而更快的促进了人口的增长。游戏中,不同的地域含有的动植物或者矿产会不一样,矿产在科技达到一定的程度才能从地图上显示出来。经常会出现这种情况,前期很强大的农业国,进入工业社会后,迅速的被崛起的工业国消灭掉。</p><p>书中也谈到中国的问题,中国在中古近古的时代一直处于世界领先的水平,独立的驯化水稻,猪,狗等动植物,发明了独立的文字,发明了造纸术,印刷术,火药等科技。然而在近代的时候,发展速度却落后于欧洲的国家。作者认为中国近代落后是中国的政权高度统一导致的,政权高度统一有时候会因为一项不明智的政策导致科技停滞很长时间。欧洲的分裂导致了科技可以在不同国家发展,没有任何一个政权能够阻止科技在整个欧洲发展。中国的政权高度统一也是由于整个中国都在同一个大陆上,不像欧洲众多国家被海水阻隔。归根结底,中国近代科技滞后有地理的原因。</p><p>读整本书的时候,脑海中始终响起太祖的一首词,词的上半阕为:”人猿相揖别。只几个石头磨过,小儿时节。铜铁炉中翻火焰,为问何时猜得?不过几千寒热。人世难逢开口笑,上疆场彼此弯弓月。流遍了,郊原血。”这首《贺新郎.读史》上半阕和本书所阐述的内容有一定的联系,词中呈现了整个人类社会的发展要素,整首词大气磅礴,以该词佐读本书,读起来更有感觉。</p>]]></content>
<summary type="html">
<p>枪炮病菌与钢铁是一本介绍人类社会史的书,探索的主题是为什么不同大陆上的人类发展的速度会不一样,反应到具体问题上是,为什么最终是欧亚大陆的人征服了美洲大陆的人,而不是反过来的情况。关于这类问题的讨论早已存在,一部分人持有种族主义观点,认为欧亚大陆的人要比美洲大陆的人更为聪明,作者不同意这种观点,并在书中详细阐述了自己的观点。</p>
</summary>
<category term="读书笔记" scheme="https://shenwenxing.com/categories/reading/"/>
<category term="人类学" scheme="https://shenwenxing.com/tags/humanics/"/>
<category term="社会学" scheme="https://shenwenxing.com/tags/sociology/"/>
<category term="历史" scheme="https://shenwenxing.com/tags/history/"/>
</entry>
<entry>
<title>通往财务自由之路</title>
<link href="https://shenwenxing.com/trade-your-way-to-financial-freedom.html"/>
<id>https://shenwenxing.com/trade-your-way-to-financial-freedom.html</id>
<published>2017-02-28T11:35:19.000Z</published>
<updated>2017-09-14T07:06:56.000Z</updated>
<content type="html"><![CDATA[<p>刚开始别人给我推荐这本书的时候,我是拒绝的,因为书名太像<code>21天精通xxx</code>系列的书了,后来检索了一些网上的资料,读过该书的对书的评价都比较高,于是我抛弃对书名的偏见尝试读了这本书,读完之后,庆幸自己没有错过这本书,整本书用非常通俗易懂的方式介绍了怎样构建自己的交易系统。</p><a id="more"></a><p>读这本书之前,如果有交易系统的基本概念,读起来会更容易,这里推荐<code>海龟交易法则</code>,海龟交易法则中描述了一个典型的交易系统是什么样子的。如果没有交易系统的概念,建议先阅读本书第七章的7.1节,7.1节中用打雪仗(大名鼎鼎的雪球论坛名字应该是出自这里)的比喻方式介绍了交易系统中六个因素,这六个因素对于理解交易系统非常重要。六大因素分别为:<strong>可靠度</strong>,<strong>利润相对规模</strong>,<strong>交易成本</strong>,<strong>交易的频率</strong>,<strong>头寸的规模</strong>,<strong>资本规模</strong>。为了方便理解,这里对该比喻进行复述,假想你躲在一面巨大的雪墙下面,有人向你的雪墙上扔雪球,雪球有两种,一种是能使雪墙融化的黑色雪球,另一种是能增强雪墙的白色的雪球,你的目标是保持墙面尽可能的大以便获得最大程度的保护。雪墙就是你的初始<strong>资金规模</strong>,白色的雪球代表盈利,黑色雪球代表亏损,扔过来的白球的百分比代表<strong>可靠度</strong>,白球和黑球砸在雪墙上,都会有轻微的破坏作用,代表<strong>交易成本</strong>,单位时间内有多少次雪球扔过来,代表<strong>交易的频率</strong>,两种雪球的相对大小,代表<strong>利润的相对规模</strong>,同时扔过来的雪球个数代表<strong>头寸的规模</strong>,书中用R来表示初始的头寸规模,R在该书中也是一个非常重要的概念。通过该比喻,很容易得出下面的结论:白球的百分比和白球与雪球的相对大小决定了雪墙是呈增强趋势还是融化趋势。单位时间内扔过来的雪球次数越多,则雪球对雪墙本身的破坏性就越大,雪墙的变化速度也会越快。要注意控制同时扔过来的雪球个数,不然坚持不到白球的到来,雪墙就已经全部融化。 </p><p>理解了交易系统的关键因素后,就需要关注怎样去构造自己的交易系统了,交易系统一般包含市场选择,入市,离市,头寸规模的控制,其中离市又包含止损离市和止盈离市,每个人的性格,经历,背景都不一样,适合自己的交易系统也不一样。什么叫适合自己的交易系统,简单的说,就是系统发出相应的信号时,自己愿意去执行,而不是怀疑或者不执行。针对入市,离市,头寸的控制等方面,书中都用了大量的篇幅进行了介绍,可以选择适合自己的条件来构造交易系统。 </p><p>整本书读下来,给我的感觉构建交易系统类似于组织战役,市场的选择类似于战场的选择,要选择适合自己的战场,入市代表什么时候发动冲锋,离市代表什么时候撤退,头寸规模代表先锋队用多少兵力,先锋队如果胜利,则后续的增援跟上,先锋队如果全部折损,则必须撤退,防止造成更多的损失。 </p><p>书中有一点令我产生疑惑,作者坚信不能够通过历史的数据去预测未来的走势,然而在构建交易系统的时候,谈到如何使得整个系统的期望收益为正的时候,使用的方法却是用历史数据进行回测,感觉有点自相矛盾。</p>]]></content>
<summary type="html">
<p>刚开始别人给我推荐这本书的时候,我是拒绝的,因为书名太像<code>21天精通xxx</code>系列的书了,后来检索了一些网上的资料,读过该书的对书的评价都比较高,于是我抛弃对书名的偏见尝试读了这本书,读完之后,庆幸自己没有错过这本书,整本书用非常通俗易懂的方式介绍了怎样构建自己的交易系统。</p>
</summary>
<category term="读书笔记" scheme="https://shenwenxing.com/categories/reading/"/>
<category term="投资" scheme="https://shenwenxing.com/tags/investment/"/>
<category term="交易系统" scheme="https://shenwenxing.com/tags/trading-system/"/>
<category term="金融" scheme="https://shenwenxing.com/tags/finance/"/>
<category term="雪球理论" scheme="https://shenwenxing.com/tags/snowball-theory/"/>
</entry>
<entry>
<title>海龟交易法则</title>
<link href="https://shenwenxing.com/way-of-the-turtle.html"/>
<id>https://shenwenxing.com/way-of-the-turtle.html</id>
<published>2017-01-22T23:30:19.000Z</published>
<updated>2017-09-14T07:17:08.000Z</updated>
<content type="html"><![CDATA[<p>先说下为什么读这本书,前一段时间一直在关注比特币市场的量化交易,据统计,目前火币网和okcoin上的80%订单都是程序自动完成的,有人专门为比特币的程序化交易开发了一个平台,在该平台上,大家反复提到一个策略—海龟策略,经常讨论这个策略在前一波行情表现怎样,怎样去优化它。于是我对该策略产生了兴趣,想深入的了解一下海龟策略。</p><a id="more"></a><p>海龟策略源于1983年两位交易大师理查德.丹尼斯与威廉.埃克哈特之间的一次赌约,赌约的内容是伟大的期货交易者是否能通过后天的培养产生。当时报名的有1000多人,最终有13个人参加了为期两周的培训,该计划称为“海龟计划”,参与海龟计划的人称为“海龟”。本书作者柯蒂斯.费思是当时最成功的海龟,在四年内,实现了其他海龟三倍的盈利。海龟策略严格说起来并不仅仅是一个策略,而是一个交易系统,包含了入市离市,止损,资金管理等方面的内容,非常适合作为量化交易的入门阅读。阅读本书的时候,最好从附录开始阅读,附录中用最精炼的语言介绍了海龟交易法则。</p><p>海龟交易法则中有一个非常重要的概念,叫做ATR(真实波动幅度),是海龟交易法则头寸规模和风险控制的基础。ATR代表一天内的最大波动幅度,将资金的1%映射为ATR,就能算出每次交易的头寸,通过对不同市场计算相应的头寸规模,就能保证不同市场的每单位头寸在一天内波动幅度不会超过总资金的1%,这样不同市场的投资规模就能相互进行比较,使得管理不同的市场变得容易,海龟系统规定每个市场不能超过4个单位的头寸,相关性市场的头寸不超过6个单位头寸,总的头寸不超过10个,这样就控制了整个账户资金的风险。当价格低于买入价的2ATR的时候,海龟就会卖出所有的头寸,这就是海龟的止损之道。</p><p>在没有接触这本之前,我以为在交易中只需要通过技术指标去预测价格走向,然后低买高卖就能实现盈利,读完这本书后了解到,要想从市场中盈利并没有那么简单,也让我重新认识了市场。市场与市场之间互不相同,有的市场波动频繁,有的市场波动缓慢,不同的策略对不同的市场有不同的适应性,书中也提到过,海龟们使用的趋势策略在比较稳定市场中的表现要优于波动频繁市场的表现。市场还有一个特征,市场中交易的人在交易过程中会影响市场的走向,交易量越大,对市场的影响就越大。如果采用相同策略的人越多,该策略失效的可能性就越大,所以可能不存在对某个市场一直有效的策略,策略也要不断的改进。</p><p>书中作者反复强调的一个观念就是严格坚持自己既定的计划,很多人会在盈利的时候过早的平仓让利润落袋为安,在损失时候不及时止损,以期望市场的反弹,违背自己之前的计划,最终导致亏损。所以,当你制定一个盈利期望为正的系统后,就要严格的坚持。当然,要实现盈利还需要一点运气和长时间的忍耐。</p>]]></content>
<summary type="html">
<p>先说下为什么读这本书,前一段时间一直在关注比特币市场的量化交易,据统计,目前火币网和okcoin上的80%订单都是程序自动完成的,有人专门为比特币的程序化交易开发了一个平台,在该平台上,大家反复提到一个策略—海龟策略,经常讨论这个策略在前一波行情表现怎样,怎样去优化它。于是我对该策略产生了兴趣,想深入的了解一下海龟策略。</p>
</summary>
<category term="读书笔记" scheme="https://shenwenxing.com/categories/reading/"/>
<category term="投资" scheme="https://shenwenxing.com/tags/investment/"/>
<category term="交易系统" scheme="https://shenwenxing.com/tags/trading-system/"/>
</entry>
<entry>
<title>Android源码编译踩坑记</title>
<link href="https://shenwenxing.com/complie-android-project.html"/>
<id>https://shenwenxing.com/complie-android-project.html</id>
<published>2016-11-18T11:27:01.000Z</published>
<updated>2017-09-12T07:10:32.000Z</updated>
<content type="html"><![CDATA[<p>编译源码的时候首先需要搭建环境,可以参照Google官方的文档进行搭建。<br><a href="https://source.android.com/source/initializing.html" target="_blank" rel="external">Google官方文档-搭建编译环境</a></p><a id="more"></a><h3 id="拉取代码要点"><a href="#拉取代码要点" class="headerlink" title="拉取代码要点"></a>拉取代码要点</h3><p>AOSP工程非常庞大,越靠后的版本,所需空间越大,如果只是为了学习,没必要拉取最新的master分支,在拉取的时候可以指定分支。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ repo init -u https://android.googlesource.com/platform/manifest -b android-6.0.1_r1 --depth=1</div></pre></td></tr></table></figure></p><p><code>-u</code>指定URL,被墙了可以指定国内镜像,例如清华大学的镜像 <a href="https://aosp.tuna.tsinghua.edu.cn/" target="_blank" rel="external">https://aosp.tuna.tsinghua.edu.cn/</a><br><code>-b</code> 指定拉取的分支,比如<code>android-6.0.1_r1</code>,这里一定要记住分支前面的Build类型,6.0.1_r1的Build就是<code>MMB29K</code>,编译之前会用到。<br><code>--depth</code>指定历史记录,没有指定会拉取所有的历史记录。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">repo sync -c</div></pre></td></tr></table></figure></p><p><code>-c</code>拉取的时候指定拉取当前分支,其他分支不拉取,这样大量减少需要传输的文件。<br>采用上面几种方法后,拉取的源码大大缩小,拉取完成后,最终目录为20G左右,编译后是42G左右,算可以接受了。之前我没做处理,拉取过master的代码,一共80G左右,编译后占了100多G。</p><p>用<code>--depth=1</code>抓下的代码叫做<code>shallow git</code>, 这种代码不能作为 mirror 使用,因此想做mirror就不要加这个参数。拉取完后,如果想要重新拉取完整的代码,可以使用unshallow。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">git fetch —unshallow</div></pre></td></tr></table></figure></p><h4 id="编译要点"><a href="#编译要点" class="headerlink" title="编译要点"></a>编译要点</h4><p>编译的时候会用到Xcode里面的sdk,如果系统已经升级到10.12,并且更新了Xcode,则里面的sdk已经更新为10.12了,最新Xcode sdk是编译不过源码的,需要去网上下载MacOSX10.11.sdk。解压后放在<code>/Applications/XCode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs</code>,然后确保AOSP源码下<code>build/core/combo/mac_version.mk</code>文件中<br><code>mac_sdk_versions_supported := 10.9 10.10 10.11</code><br>编译的时候,根据cpu的核的数量指定编译线程数,比如我的电脑是4核,可以指定8个线程同时编译,最终编译完用了59分钟。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">make -j8</div></pre></td></tr></table></figure></p><h4 id="源码导入"><a href="#源码导入" class="headerlink" title="源码导入"></a>源码导入</h4><p>先编译idegen模块,运行后会生成idegen.jar文件<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">mmm development/tools/idegen/</div></pre></td></tr></table></figure></p><p>再生成Android.ipr、Android.iml等工程配置文件<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">development/tools/idegen/idegen.sh</div></pre></td></tr></table></figure></p><p>编译出android.ipr等后,就可以用IDEA或者AS打开android.ipr了,打开该文件会加载整个Android源码。</p><p>如果提示存在duplicate class,可以在android.iml中指定对应的excludeFolder。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><excludeFolder url="file://$MODULE_DIR$/out/target/common/docs" /></div><div class="line"><excludeFolder url="file://$MODULE_DIR$/out/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates" /></div><div class="line"><excludeFolder url="file://$MODULE_DIR$/out/target/product" /></div><div class="line"><excludeFolder url="file://$MODULE_DIR$/prebuilt" /></div></pre></td></tr></table></figure></p><p>导入后源码中不能正确跳转对应的Java文件,切换到项目设置,删除所有依赖,只保留最后面两个。<br><img src="http://img.blog.csdn.net/20151001201951821" alt="module"></p><h4 id="运行emulator"><a href="#运行emulator" class="headerlink" title="运行emulator"></a>运行emulator</h4><p>运行前,需要指定几个路径,不然启动时候会黑屏。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">export ANDROID_PRODUCT_OUT=/Volumes/android/source/out/target/product/generic</div><div class="line">export ANDROID_PRODUCT_OUT_BIN=/Volumes/android/source/out/host/darwin-x86/bin</div><div class="line">export PATH=$PATH:$ANDROID_PRODUCT_OUT_BIN:$ANDROID_PRODUCT_OUT</div><div class="line">export ANDROID_BUILD_TOP=/Volumes/android/source</div></pre></td></tr></table></figure></p><p>运行的时候,可能会提示下面错误。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">emulator: WARNING: system partition size adjusted to match image file (1536 MB > 200 MB)</div></pre></td></tr></table></figure></p><p>解决方案,增大虚拟机内存。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">emulator -partition-size 2048</div></pre></td></tr></table></figure></p>]]></content>
<summary type="html">
<p>编译源码的时候首先需要搭建环境,可以参照Google官方的文档进行搭建。<br><a href="https://source.android.com/source/initializing.html" target="_blank" rel="external">Google官方文档-搭建编译环境</a></p>
</summary>
<category term="编程" scheme="https://shenwenxing.com/categories/programming/"/>
<category term="Android" scheme="https://shenwenxing.com/tags/Android/"/>
<category term="源码编译" scheme="https://shenwenxing.com/tags/source-code/"/>
</entry>
<entry>
<title>MVP模式在Android中的应用</title>
<link href="https://shenwenxing.com/mvp-android.html"/>
<id>https://shenwenxing.com/mvp-android.html</id>
<published>2016-11-10T12:59:44.000Z</published>
<updated>2017-09-11T07:23:36.000Z</updated>
<content type="html"><![CDATA[<p>MVP模式是从MVC模式演化过来的,MVC模式在业界内有广泛的应用,这里简单介绍下MVC模式。</p><ul><li>M (Model) 代表数据访问层</li><li>V (View) 代表用户界面</li><li>C (Controller) 代表控制逻辑</li></ul><a id="more"></a><p>在MVC中,Controller接受View来的指令,操作Model作出改变,Model改变后通知View数据改变,View层读取Model数据刷新UI视觉。<br>对于Android程序而言,Activity可以认为是Controller层,Xml文件配置的界面为View层,由于Andorid中View层能够做的工作实在有限,大量的工作放在了Activity层,这样就使得Activity非常的臃肿,改动起来很麻烦,所以MVC模式在Android中应用起来显得的力不从心,MVP应运而生。</p><h3 id="MVP模式"><a href="#MVP模式" class="headerlink" title="MVP模式"></a>MVP模式</h3><p>MVP模式中P(Presenter)负责控制View和Model之间的交互,是M和V的桥梁。下图是MVC模式和MVP模式的对比,从图中可以看出,MVP模式中,Model和View必须通过Presenter进行交互,不能直接交互。Presenter和View的交互也不是通过直接引用来操作的,而是通过定义而好的接口进行交互,理想情况下,View层视觉改变不会引起Presenter层改变,达到重用的效果。</p><p><img src="http://www.jcodecraeer.com/uploads/20150313/1426239008578777.png" alt="mvpvsmvc"></p><p>MVP模式应用到Android系统中,M层的含义依旧不变,V层变为Activity和Fragment,负责和用户交互,P层为抽象层,负责操作M层,并根据M层的结果来操作V层。</p><h3 id="MVP结构"><a href="#MVP结构" class="headerlink" title="MVP结构"></a>MVP结构</h3><p>参考Google的例子<a href="https://github.com/googlesamples/android-architecture" target="_blank" rel="external">android-architecture</a>,最基本的MVP的结构如下。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div></pre></td><td class="code"><pre><div class="line">public interface BaseView<T> {</div><div class="line"> void setPresenter(T presenter);</div><div class="line">}</div><div class="line"></div><div class="line">public interface BasePresenter {</div><div class="line"> void start();</div><div class="line">}</div><div class="line"></div><div class="line">public interface LoginContract {</div><div class="line"> interface View extends BaseView<Presenter> {</div><div class="line"> void showMainView();</div><div class="line"> void showLoginFailView(int code);</div><div class="line"> }</div><div class="line"></div><div class="line"> interface Presenter extends BasePresenter{</div><div class="line"> void login(String user,String pass);</div><div class="line"> }</div><div class="line"></div><div class="line"> interface Model {</div><div class="line"> void doLogin(String user,String pass,Callback callback);</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line">public LoginActivity extends Activity implement LoginContract.View{</div><div class="line"> private Presenter presenter;</div><div class="line"> void setPresenter(Presenter p) {</div><div class="line"> this.presenter = p;</div><div class="line"> }</div><div class="line"> btnLogin.setOnClickListener((v) -> presenter.login(user,pass));</div><div class="line"> public void showMainView() {</div><div class="line"> Intent intent = new Intent(this,MainActivity.class);</div><div class="line"> startActivity(intent);</div><div class="line"> }</div><div class="line"> public void showLoginFailView(int code) {</div><div class="line"></div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line">public LoginPresenter implement LoginContract.Presenter {</div><div class="line"> LoginContract.View view;</div><div class="line"> LoginContract.Model model;</div><div class="line"> void login(String user,String pass) {</div><div class="line"> //处理登录逻辑</div><div class="line"> model.doLogin(user,pass,new Callback() {</div><div class="line"> void onSuccess() {</div><div class="line"> view.showMainView();</div><div class="line"> }</div><div class="line"> void onError(int code,String msg) {</div><div class="line"> view.showLoginFailView(code);</div><div class="line"> }</div><div class="line"> });</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p><p>为了统一整个程序的编码风格,一般会定义BaseView和BasePresenter,实际的View和Presenter继承BaseView和BasePresenter,在BaseView和BasePresenter中可以定义一系列公共的操作函数。写实际业务流程的时候可以定义一个Contract,Contract中定义了View和Presenter交互的接口以及Presenter和Model的交互接口。这样可以让程序结构看起来更清晰,同时也统一了Presenter和View的名字。</p><p>在上面的Demo中,LoginActivity接收来自用户的操作,用户点击登录,则会调用LoginPresenter进行处理,LoginActivity不用关心怎么去处理,只负责发送指令和显示。LoginPresenter登录前可以做参数校验等,如果没有问题则调用LoginModel去进行登录,根据LoginModel返回的结果去操作LoginView,整个登录流程完成。</p><h3 id="nucleus库"><a href="#nucleus库" class="headerlink" title="nucleus库"></a>nucleus库</h3><p>根据上面的Demo可以写出符合MVP模式的代码,但是每次都自己手动生成Presenter,然后将View的设置进Presenter会显得相当麻烦,更重要的是Presenter持有Acitivity的引用,一旦Presenter中涉及到异步回调操作View,就会有Activity泄露的风险,需要手动管理Presenter的生命周期。如果每次都得自己手动管理Presenter,会显得更繁琐。<a href="https://github.com/konmik/nucleus" target="_blank" rel="external">nucleus库</a>很好的解决了MVP模式中的这些问题,下图为nucleus库的类图。</p><p><img src="http://7xntab.com1.z0.glb.clouddn.com/image/program/nucleus.png" alt="nucleus"></p><p>Presenter中定义了一系列声明周期相关的接口,映射Activity的声明周期。NucleusActiviy作为View层并没有直接的创建和管理Presenter,而是通过代理类PresenterLifecycleDelegate去创建和管理其生命周期。NucleusActivity将默认的PrsenterFactory传递给PresenterLifecycleDelegate,在需要创建Presenter的时候,通过工厂方法创建Presenter。Presenter通过单例PresenterStorage来进行存储,独立于Activity之外,达到复用的目的,在Activity onPause的时候,Presenter会移除自己内部的View引用,防止出现内存泄露,在Activity onDestroy的时候,Presenter会将自己从PresenterStorage中移除。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div></pre></td><td class="code"><pre><div class="line">//注释接口</div><div class="line">@Inherited</div><div class="line">@Retention(RetentionPolicy.RUNTIME)</div><div class="line">public @interface RequiresPresenter {</div><div class="line"> Class<? extends Presenter> value();</div><div class="line">}</div><div class="line"></div><div class="line">//根据注释创建构造Prsenter的工厂</div><div class="line">@Nullable</div><div class="line">public static <P extends Presenter> ReflectionPresenterFactory<P> fromViewClass(Class<?> viewClass) {</div><div class="line"> RequiresPresenter annotation = viewClass.getAnnotation(RequiresPresenter.class);</div><div class="line"> //noinspection unchecked</div><div class="line"> Class<P> presenterClass = annotation == null ? null : (Class<P>)annotation.value();</div><div class="line"> return presenterClass == null ? null : new ReflectionPresenterFactory<>(presenterClass);</div><div class="line">}</div><div class="line"></div><div class="line">public ReflectionPresenterFactory(Class<P> presenterClass) {</div><div class="line"> this.presenterClass = presenterClass;</div><div class="line">}</div><div class="line">//通过反射创建Presenter</div><div class="line">@Override</div><div class="line">public P createPresenter() {</div><div class="line"> try {</div><div class="line"> return presenterClass.newInstance();</div><div class="line"> }</div><div class="line"> catch (Exception e) {</div><div class="line"> throw new RuntimeException(e);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure><h3 id="基于nucleus的MVP模式"><a href="#基于nucleus的MVP模式" class="headerlink" title="基于nucleus的MVP模式"></a>基于nucleus的MVP模式</h3><ul><li><p>定义Contract<br>这里Contract的意义和第一个LoginContract的意义是一样,对各层之间的交互使用接口进行隔离。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">public interface IDemoContract {</div><div class="line"> interface IPresenter {</div><div class="line"> void perforAction();</div><div class="line"> }</div><div class="line"></div><div class="line"> interface IView {</div><div class="line"> void showSuccessMessage();</div><div class="line"></div><div class="line"> void showFailedMessage();</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></li><li><p>实现Presenter<br>为了借助nucleus库帮助我们自动创建和管理Presenter,需要继承库中的Presenter,为了和View层交互,要实现Contract中的Presenter,同时为了隔离Activity中的函数,泛型参数传入IView。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">public class DemoPresenter extends Presenter<IDemoContract.IView> implements IDemoContract.IPresenter {</div><div class="line"></div><div class="line"> @Override</div><div class="line"> public void perforAction() {</div><div class="line"> //处理业务逻辑</div><div class="line"> //...</div><div class="line"> //view层展示</div><div class="line"> IDemoContract.IView view = getView();</div><div class="line"> if (view != null) {</div><div class="line"> view.showSuccessMessage();</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></li><li><p>实现View<br>自动创建和管理Presenter同时也需要Activity继承自NucleusActivity,并在Activity类上添加注释@RequiresPresenter(Presenter.class)。并在Activity中实现View的接口。</p></li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div></pre></td><td class="code"><pre><div class="line">@RequiresPresenter(DemoPresenter.class)</div><div class="line">public class DemoActivity extends NSIBaseActivity<DemoPresenter> implements IDemoContract.IView {</div><div class="line"> private Button btnTestMvp;</div><div class="line"></div><div class="line"> @Override</div><div class="line"> protected void onCreate(@Nullable Bundle savedInstanceState) {</div><div class="line"> super.onCreate(savedInstanceState);</div><div class="line"> setContentView(R.layout.activity_mvp_demo);</div><div class="line"> bindViews(null);</div><div class="line"> setListener();</div><div class="line"> }</div><div class="line"></div><div class="line"> @Override</div><div class="line"> protected void bindViews(View view) {</div><div class="line"> btnTestMvp = (Button) findViewById(R.id.btn_test_mvp);</div><div class="line"> }</div><div class="line"></div><div class="line"> @Override</div><div class="line"> protected void setListener() {</div><div class="line"> btnTestMvp.setOnClickListener(new View.OnClickListener() {</div><div class="line"> @Override</div><div class="line"> public void onClick(View v) {</div><div class="line"> DemoPresenter presenter = getPresenter();</div><div class="line"> if (presenter != null) {</div><div class="line"> presenter.perforAction();</div><div class="line"> }</div><div class="line"> }</div><div class="line"> });</div><div class="line"> }</div><div class="line"></div><div class="line"> @Override</div><div class="line"> protected void initData() {</div><div class="line"></div><div class="line"> }</div><div class="line"></div><div class="line"> @Override</div><div class="line"> public void showSuccessMessage() {</div><div class="line"> showShortToast("Action success");</div><div class="line"> }</div><div class="line"></div><div class="line"> @Override</div><div class="line"> public void showFailedMessage() {</div><div class="line"> showShortToast("Action Failed");</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure><h3 id="自动化测试"><a href="#自动化测试" class="headerlink" title="自动化测试"></a>自动化测试</h3><p>由于Presenter通过View接口对Activity进行了隔离,Presenter中的代码是Android无关的,自动化测试起来会很方便。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">public class MvpDemoPresenterTest {</div><div class="line"> @Test</div><div class="line"> public void test_perform_action() {</div><div class="line"> IDemoContract.IView view = Mockito.mock(IDemoContract.IView.class);</div><div class="line"> DemoPresenter presenter = new DemoPresenter();</div><div class="line"> presenter.takeView(view);</div><div class="line"> presenter.perforAction();</div><div class="line"> Mockito.verify(view).showSuccessMessage();</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p><h3 id="优点"><a href="#优点" class="headerlink" title="优点"></a>优点</h3><ul><li>接口隔离,容易复用</li><li>便于自动化测试</li><li>耦合度低</li><li>交互清晰<h3 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h3><a href="https://github.com/googlesamples/android-architecture" target="_blank" rel="external">Android Architecture Blueprints</a><br><a href="https://github.com/konmik/konmik.github.io/wiki/Introduction-to-Model-View-Presenter-on-Android" target="_blank" rel="external">Introduction to Model View Presenter on Android</a><br><a href="https://github.com/android10/Android-CleanArchitecture" target="_blank" rel="external">Android Clean Architecture</a><br><a href="http://frodoking.github.io/2015/02/01/android-mvc-mvp-analysis/" target="_blank" rel="external">MVC和MVP在app中的对比分析以及实际应用</a></li></ul>]]></content>
<summary type="html">
<p>MVP模式是从MVC模式演化过来的,MVC模式在业界内有广泛的应用,这里简单介绍下MVC模式。</p>
<ul>
<li>M (Model) 代表数据访问层</li>
<li>V (View) 代表用户界面</li>
<li>C (Controller) 代表控制逻辑</li>
</ul>
</summary>
<category term="编程" scheme="https://shenwenxing.com/categories/programming/"/>
<category term="Android" scheme="https://shenwenxing.com/tags/Android/"/>
<category term="MVP" scheme="https://shenwenxing.com/tags/MVP/"/>
<category term="设计模式" scheme="https://shenwenxing.com/tags/design-patterns/"/>
</entry>
<entry>
<title>Android单元测试</title>
<link href="https://shenwenxing.com/android-unit-test.html"/>
<id>https://shenwenxing.com/android-unit-test.html</id>
<published>2016-11-04T04:07:37.000Z</published>
<updated>2017-09-11T07:23:36.000Z</updated>
<content type="html"><![CDATA[<p>单元测试框架分为Java平台下的测试框架和Android平台下的测试框架,Java平台下常用的框架有Junit,Mockito,Powermockito等,Android平台下的测试框架有Robolectric和Espresso等。<br>Junit运行在jvm上,一般自动化测试都会使用其他测试框架搭配Junit测试框架使用,Mockito框架可以解决单元测试中对象依赖的问题。Powermockito在功能上扩展了Mockito功能,Powermockito能够mock静态、final、私有等方法 ,这些在Mockito中不能完成的。Espresso是Google官方出的Android测试框架,语法简单,缺点是只能在真机上进行测试。Robolectric不需要借助Android手机,就能完成对Android代码的自动化测试。</p><a id="more"></a><h3 id="Junit测试框架"><a href="#Junit测试框架" class="headerlink" title="Junit测试框架"></a>Junit测试框架</h3><p>Junit测试的语法非常简单,如果使用Gradle进行打包,要集成该框架也很简单。只需要在相应模块的依赖中加入库的配置即可。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">dependencies {</div><div class="line"> testCompile 'junit:junit:4.12'</div><div class="line">}</div></pre></td></tr></table></figure></p><p>Junit测试中用得最多的当属Assert语法。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div></pre></td><td class="code"><pre><div class="line">public interface IFilter {</div><div class="line"> boolean isValidate(String content);</div><div class="line">}</div><div class="line"></div><div class="line">public class PhoneFilter implements IFilter {</div><div class="line"> private static final String PHONE_REGEX = "\\d{11}";</div><div class="line"> private Pattern mPattern;</div><div class="line"> private Matcher mMatcher;</div><div class="line"></div><div class="line"> public PhoneFilter() {</div><div class="line"> mPattern = Pattern.compile(mRegex);</div><div class="line"> mMatcher = mPattern.matcher(" ");</div><div class="line"> }</div><div class="line"></div><div class="line"> @Override</div><div class="line"> public boolean isValidate(String content) {</div><div class="line"> if (content == null || content.isEmpty()) {</div><div class="line"> return false;</div><div class="line">}</div><div class="line">mMatcher.reset(content);</div><div class="line">return mMatcher.matches();</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line">public class PhoneFilterTest {</div><div class="line"> private IFilter filter;</div><div class="line"> private IFilter nullFilter = null;</div><div class="line"></div><div class="line"> @BeforeClass</div><div class="line"> public void beforeClass() {</div><div class="line"> filter = new PhoneFilter();</div><div class="line"> }</div><div class="line"></div><div class="line"> @Test</div><div class="line"> public void test_isValidate() {</div><div class="line"> Assert.assertTrue(filter.isValidate("13991756543"));</div><div class="line"> Assert.assertTrue(filter.isValidate("00000000000"));</div><div class="line"> Assert.assertFalse(filter.isValidate("10086"));</div><div class="line"> }</div><div class="line"></div><div class="line"> @Test</div><div class="line"> public void test_Assert() {</div><div class="line"> String strExpect = "expect";</div><div class="line"> String strActual = "expect";</div><div class="line"> Assert.assertEquals(strExpect,strActual);</div><div class="line"> Assert.assertArrayEquals(strExpect.getBytes(),strActual.getBytes());</div><div class="line"> Assert.assertNotNull(strActual);</div><div class="line"> Assert.assertNull(nullFilter);</div><div class="line"> }</div><div class="line"></div><div class="line"></div><div class="line">}</div></pre></td></tr></table></figure></p><p>上面是<code>PhoneFilter</code>及其测试类<code>PhoneFilterTest</code>。写单元测试的时候,测试的类名一般命名为原类名后加<code>Test</code>前缀,测试函数为原函数加<code>_test</code>前缀,测试函数上面必须加上标记<code>@Test</code>,并且必须为<code>public</code>的函数。然后根据实际情况使用<code>Assert</code>语句,常用的Assert语句为<code>assertEquals</code>,<code>assertTrue</code>,<code>assertFalse</code>,<code>assertNull</code>,<code>assertNotNull</code>。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line">@BeforeClass</div><div class="line">public void beforeClass() {</div><div class="line"></div><div class="line">}</div><div class="line"></div><div class="line">@Before</div><div class="line">public void before() {</div><div class="line"> filter = new PhoneFilter();</div><div class="line">}</div><div class="line"></div><div class="line">@After</div><div class="line">public void after() {</div><div class="line"></div><div class="line">}</div><div class="line"></div><div class="line">@AfterClass</div><div class="line">public void afterClass() {</div><div class="line"></div><div class="line">}</div></pre></td></tr></table></figure><p>Junit框架中提供了能做初始化和清理的函数,<code>@Before</code>和<code>@After</code>在每个测试函数调用之前和之后都会执行,<code>@BeforeClass</code>和<code>@AfterClass</code>对于每个测试类只执行一次,函数名称可以随便改变。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">@Test(expected = IndexOutOfBoundsException.class)</div><div class="line">public void empty() {</div><div class="line"> new ArrayList<Object>().get(0);</div><div class="line">}</div></pre></td></tr></table></figure><p>Junit框架对<code>Exception</code>的测试也提供了支持。</p><h3 id="Mockito框架"><a href="#Mockito框架" class="headerlink" title="Mockito框架"></a>Mockito框架</h3><blockquote><p>Mockito is a mocking framework that tastes really good. It lets you write beautiful tests with a clean & simple API.</p></blockquote><p>引用<a href="http://site.mockito.org/" target="_blank" rel="external">Mockito官网</a>的说法,Mockito是一个mocking的框架,可以做依赖隔离。项目中引入Mockito测试也很简单,只需要在加入下面的依赖即可。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">testCompile 'org.mockito:mockito-core:2.2.9'</div></pre></td></tr></table></figure></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div></pre></td><td class="code"><pre><div class="line">public interface IFilter {</div><div class="line"> boolean isValidate(String content);</div><div class="line">}</div><div class="line"></div><div class="line">public class PhonePresenter {</div><div class="line"> private IFilter phoneFilter;</div><div class="line"> pulbic void setFilter(IFilter filter) {</div><div class="line"> phoneFilter = filter;</div><div class="line"> }</div><div class="line"></div><div class="line"> public boolean callPhone(String phoneNum) {</div><div class="line"> if (phoneFilter.isValidate(phoneNum)) {</div><div class="line"> //TODO:call phone logic</div><div class="line"> return true;</div><div class="line"> } else {</div><div class="line"> return false;</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line">public class PhonePresenterTest {</div><div class="line"> @Test</div><div class="line"> public void test_callPhone() {</div><div class="line"> PhonePresenter presenter = new PhonePresenter();</div><div class="line"> IFilter filter = Mockito.mock(IFilter.class);</div><div class="line"> Mockito.when(filter.isValidate("12345")).thenReturn(false);</div><div class="line"> Mockito.when(filter.isValidate("10086")).thenReturn(true);</div><div class="line"> presenter.setFilter(filter);</div><div class="line"></div><div class="line"> Assert.assertTrue(presenter.callPhone("10086"));</div><div class="line"> Assert.assertFalse(presenter.callPhone("12345"));</div><div class="line"> Mockito.verify(filter,Mockito.times(1)).isValidate("10086");</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure><p>我们要对<code>callPhone</code>函数进行测试,函数中引用了<code>phoneFilter</code>,在该函数测试的时候,我们不希望<code>phoneFilter</code>内部逻辑的失败导致当前函数测试的失败,更糟糕的是,<code>phoneFilter</code>可能引用了Android库,我们没有办法去正常调用,这时候就可以使用<code>mock</code>来做依赖隔离。隔离后可以对函数调用结果做判断,也可以对<code>isValidate</code>函数是否执行了进行验证。 从这个例子也可以看出mock的两大功能。</p><ul><li>验证某个方法是否被调用,以及调用了多少次,参数是什么。</li><li>指定某个方法在各种情况下的返回值。</li></ul><h3 id="Robolectric框架"><a href="#Robolectric框架" class="headerlink" title="Robolectric框架"></a>Robolectric框架</h3><p>前面介绍Robolectric的时候提到过,Robolectric能够在jvm环境下测试Android代码,是怎么做到的呢。Robolectric通过实现一套jvm能运行的Android代码,然后在单元测试运行的时候去截取android相关的代码调用,然后转到他们的他们实现的代码去执行这个调用的过程。<br>加入依赖到Gradle脚本中就可以使用Robolectric了。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">testCompile 'org.robolectric:robolectric:3.1.4'</div></pre></td></tr></table></figure></p><p>值得注意的是,在API23下面测试Activity相关的代码会报类似以下错误:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">java.lang.NoClassDefFoundError: javax/microedition/khronos/opengles/GL</div></pre></td></tr></table></figure></p><p>解决方案是在测试的类上指定API21。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line">@RunWith(RobolectricTestRunner.class)</div><div class="line">@Config(constants = BuildConfig.class, sdk = 21)</div><div class="line">public class NSITestActivityTest {</div><div class="line"> private NSITestActivity testActivity;</div><div class="line"></div><div class="line"> @Before</div><div class="line"> public void setup() {</div><div class="line"> ShadowLog.stream = System.out;</div><div class="line"> testActivity = Robolectric.setupActivity(NSITestActivity.class);</div><div class="line"> }</div><div class="line"></div><div class="line"> @Test</div><div class="line"> public void test_start_activity() {</div><div class="line"> Button btnMvp = (Button) testActivity.findViewById(R.id.btn_test_mvp);</div><div class="line"> Assert.assertNotNull(btnMvp);</div><div class="line"> btnMvp.performClick();</div><div class="line"> Intent expect = new Intent(testActivity, DemoActivity.class);</div><div class="line"> Intent actual = Shadows.shadowOf(testActivity).getNextStartedActivity();</div><div class="line">// Intent actual = ShadowApplication.getInstance().getNextStartedActivity();</div><div class="line">// Assert.assertEquals(expect, actual);</div><div class="line"> Assert.assertTrue(actual.filterEquals(expect));</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure><p>测试Android相关代码的时候,有几点需要注意。</p><ul><li>需要在测试类前面指定Runner为<code>RobolectricTestRunner.class</code>,早期的版本指定的是<code>RobolectricGradleTestRunner.class</code>,3.0以后一定要用前者。</li><li>需要指定<code>constants = BuildConfig.class</code></li><li>需要指定<code>sdk = 21</code>,原因如前面所述。</li></ul><p>在启动测试Activity的时候,判断Intent是否相等的时候,不能简单的使用<code>Assert.assertEquals(expect,actual)</code>,而应该使用<code>Assert.assertTrue(actual.filterEquals(expect))</code>。在3.0之前使用前者是能作出正确判断的,3.0之后使用前者会报错,错误信息如下:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"></div><div class="line">java.lang.AssertionError: expected: android.content.Intent<Intent { cmp=com.nsip.android/.business.mvpDemo.DemoActivity }> but was: android.content.Intent<Intent { cmp=com.nsip.android/.business.mvpDemo.DemoActivity }></div><div class="line">Expected :android.content.Intent<Intent { cmp=com.nsip.android/.business.mvpDemo.DemoActivity }></div><div class="line">Actual :android.content.Intent<Intent { cmp=com.nsip.android/.business.mvpDemo.DemoActivity }></div></pre></td></tr></table></figure></p><p>对于其他Android相关的测试类似于Activity的测试。<br>Activity代码如下:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div></pre></td><td class="code"><pre><div class="line">public class UTSampleActivity extends BaseActivity {</div><div class="line"> @BindView(R.id.tv_life_circle)</div><div class="line"> TextView tvLifeCircle;</div><div class="line"> @BindView(R.id.tv_about)</div><div class="line"> TextView btnAbout;</div><div class="line"> @BindView(R.id.tv_show_toast)</div><div class="line"> TextView btnShowToast;</div><div class="line"> @BindView(R.id.tv_show_dialog)</div><div class="line"> TextView btnShowDialog;</div><div class="line"> @BindView(R.id.tv_delay_task)</div><div class="line"> TextView btnDelayTask;</div><div class="line"> @BindView(R.id.tv_inverse)</div><div class="line"> TextView btnInverse;</div><div class="line"> @BindView(R.id.cb_state)</div><div class="line"> CheckBox cbState;</div><div class="line"></div><div class="line"> public boolean isFinished = false;</div><div class="line"></div><div class="line"> public static final String TAG = UTSampleActivity.class.getSimpleName();</div><div class="line"></div><div class="line"> @Override</div><div class="line"> protected void onCreate(Bundle savedInstanceState) {</div><div class="line"> super.onCreate(savedInstanceState);</div><div class="line"> setContentView(R.layout.unit_test_sample_layout);</div><div class="line"> ButterKnife.bind(this);</div><div class="line"> tvLifeCircle.setText("onCreate");</div><div class="line"> }</div><div class="line"></div><div class="line"> @Override</div><div class="line"> protected void onResume() {</div><div class="line"> super.onResume();</div><div class="line"> tvLifeCircle.setText("onResume");</div><div class="line"> Log.d(TAG,"sample activity resume");</div><div class="line"> }</div><div class="line"></div><div class="line"> @Override</div><div class="line"> protected void onPause() {</div><div class="line"> super.onPause();</div><div class="line"> tvLifeCircle.setText("onPause");</div><div class="line"> }</div><div class="line"></div><div class="line"> @Override</div><div class="line"> protected void onDestroy() {</div><div class="line"> super.onDestroy();</div><div class="line"> tvLifeCircle.setText("onDestroy");</div><div class="line"> }</div><div class="line"></div><div class="line"> @OnClick(R.id.tv_about)</div><div class="line"> public void onClick() {</div><div class="line"> Intent intent = new Intent(UTSampleActivity.this, AboutActivity.class);</div><div class="line"> startActivity(intent);</div><div class="line"> }</div><div class="line"></div><div class="line"> @OnClick({R.id.tv_show_toast, R.id.tv_show_dialog, R.id.tv_delay_task,</div><div class="line"> R.id.tv_inverse, R.id.cb_state})</div><div class="line"> public void onClick(View view) {</div><div class="line"> switch (view.getId()) {</div><div class="line"> case R.id.tv_show_toast:</div><div class="line"> Toast.makeText(view.getContext(), "toast info", Toast.LENGTH_SHORT).show();</div><div class="line"> break;</div><div class="line"> case R.id.tv_show_dialog:</div><div class="line"> AlertDialog dialog = new AlertDialog.Builder(view.getContext())</div><div class="line"> .setMessage("Dialog Message")</div><div class="line"> .setTitle("Dialog")</div><div class="line"> .create();</div><div class="line"> dialog.show();</div><div class="line"> break;</div><div class="line"> case R.id.tv_delay_task:</div><div class="line"> isFinished = false;</div><div class="line"> mHander.postDelayed(() -> isFinished = true, 2000);</div><div class="line"> break;</div><div class="line"> case R.id.tv_inverse:</div><div class="line"> cbState.setChecked(!cbState.isChecked());</div><div class="line"> break;</div><div class="line"> case R.id.cb_state:</div><div class="line"> cbState.setChecked(!cbState.isChecked());</div><div class="line"> break;</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p><p>Activity对应的xml文件如下:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div></pre></td><td class="code"><pre><div class="line"><?xml version="1.0" encoding="utf-8"?></div><div class="line"><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"</div><div class="line"> android:layout_width="match_parent"</div><div class="line"> android:layout_height="match_parent"</div><div class="line"> android:orientation="vertical"</div><div class="line"> android:paddingBottom="@dimen/activity_vertical_margin"</div><div class="line"> android:paddingLeft="@dimen/activity_horizontal_margin"</div><div class="line"> android:paddingRight="@dimen/activity_horizontal_margin"</div><div class="line"> android:paddingTop="@dimen/activity_vertical_margin"></div><div class="line"></div><div class="line"> <TextView</div><div class="line"> android:id="@+id/tv_life_circle"</div><div class="line"> style="@style/LDTDCommonButton"</div><div class="line"> android:text="life circle" /></div><div class="line"></div><div class="line"> <TextView</div><div class="line"> android:id="@+id/tv_about"</div><div class="line"> style="@style/LDTDCommonButton"</div><div class="line"> android:text="About Page" /></div><div class="line"></div><div class="line"> <TextView</div><div class="line"> android:id="@+id/tv_show_toast"</div><div class="line"> style="@style/LDTDCommonButton"</div><div class="line"> android:text="Show Toast" /></div><div class="line"></div><div class="line"> <TextView</div><div class="line"> android:id="@+id/tv_show_dialog"</div><div class="line"> style="@style/LDTDCommonButton"</div><div class="line"> android:text="Show Dialog" /></div><div class="line"></div><div class="line"> <TextView</div><div class="line"> android:id="@+id/tv_delay_task"</div><div class="line"> style="@style/LDTDCommonButton"</div><div class="line"> android:text="Delay Task" /></div><div class="line"></div><div class="line"> <TextView</div><div class="line"> android:id="@+id/tv_inverse"</div><div class="line"> style="@style/LDTDCommonButton"</div><div class="line"> android:text="Inverse" /></div><div class="line"></div><div class="line"> <CheckBox</div><div class="line"> android:id="@+id/cb_state"</div><div class="line"> android:layout_width="wrap_content"</div><div class="line"> android:layout_height="wrap_content"</div><div class="line"> android:checked="false"</div><div class="line"> android:text="state" /></div><div class="line"></LinearLayout></div></pre></td></tr></table></figure></p><p>下面是对Android相关组件的测试,在测试的过程中可以设置日志的输出。这样代码中的日志和测试中的日志都会输出到同一个地方,非常方便。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">@Before</div><div class="line">public void setup() throws URISyntaxException{</div><div class="line"> //输出日志</div><div class="line"> ShadowLog.stream = System.out;</div><div class="line">}</div></pre></td></tr></table></figure></p><p>测试整个Activity的生命周期需要用到ActivityController。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line">@Test</div><div class="line">public void testLifeCirecle() {</div><div class="line"> ActivityController<UTSampleActivity> controller = Robolectric.buildActivity(UTSampleActivity.class).create().start();</div><div class="line"> Activity sampleActivity = controller.get();</div><div class="line"> TextView tvlife = (TextView) sampleActivity.findViewById(R.id.tv_life_circle);</div><div class="line"> Assert.assertEquals("onCreate", tvlife.getText());</div><div class="line"> controller.resume();</div><div class="line"> Assert.assertEquals("onResume", tvlife.getText());</div><div class="line"> Log.d("lifeCircle","onResume");</div><div class="line"> controller.pause();</div><div class="line"> Assert.assertEquals("onPause", tvlife.getText());</div><div class="line"> controller.destroy();</div><div class="line"> Assert.assertEquals("onDestroy", tvlife.getText());</div><div class="line">}</div></pre></td></tr></table></figure></p><p>测试Toast是否弹出,这里在生成Activity的时候,并没有使用ActivityController,而是直接使用的setupActivity。两者之间的区别在于,通过Robolectric.setupActivity(UTSampleActivity.class)生成的instance在返回的时候就已经完成了onCreate、onStart、onResume这几个生命周期的回调了。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"></div><div class="line">@Test</div><div class="line">public void testToast() {</div><div class="line"> mSampleActivity = Robolectric.setupActivity(UTSampleActivity.class);</div><div class="line"> TextView btnToast = (TextView) mSampleActivity.findViewById(R.id.tv_show_toast);</div><div class="line"> btnToast.performClick();</div><div class="line"> Assert.assertEquals(ShadowToast.getTextOfLatestToast(), "toast info");</div><div class="line">}</div></pre></td></tr></table></figure></p><p>使用<code>ShadowLooper.runUiThreadTasksIncludingDelayedTasks()</code>可以让延时的任务立即执行。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">@Test</div><div class="line">public void testDelayTask() {</div><div class="line"> TextView btnTask = (TextView) mSampleActivity.findViewById(R.id.tv_delay_task);</div><div class="line"> btnTask.performClick();</div><div class="line"> Assert.assertFalse(mSampleActivity.isFinished);</div><div class="line"> ShadowLooper.runUiThreadTasksIncludingDelayedTasks();</div><div class="line"> Assert.assertTrue(mSampleActivity.isFinished);</div><div class="line">}</div></pre></td></tr></table></figure><p>Dialog测试</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"></div><div class="line">@Test</div><div class="line">public void testDialog() {</div><div class="line"> TextView btnDialog = (TextView) mSampleActivity.findViewById(R.id.tv_show_dialog);</div><div class="line"> btnDialog.performClick();</div><div class="line"> AlertDialog latestDialog = ShadowAlertDialog.getLatestAlertDialog();</div><div class="line"> Assert.assertNotNull(latestDialog);</div><div class="line">}</div></pre></td></tr></table></figure><p>资源测试<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">@Test</div><div class="line">public void testResource() {</div><div class="line"> Application application = RuntimeEnvironment.application;</div><div class="line"> String appName = application.getString(R.string.app_name);</div><div class="line"> Assert.assertEquals(appName, "LDTaskDemo");</div><div class="line">}</div></pre></td></tr></table></figure></p><p>Service类以及对应的测试类<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div></pre></td><td class="code"><pre><div class="line">public class SampleIntentService extends IntentService {</div><div class="line"> public SampleIntentService(String name) {</div><div class="line"> super(name);</div><div class="line"> }</div><div class="line"></div><div class="line"> public SampleIntentService() {</div><div class="line"> this("defaultSampleIntentService");</div><div class="line"> }</div><div class="line"></div><div class="line"> @Override</div><div class="line"> protected void onHandleIntent(Intent intent) {</div><div class="line"> SharedPreferences.Editor editor = getApplicationContext().getSharedPreferences("example", MODE_PRIVATE).edit();</div><div class="line"> String extData = intent.getStringExtra("EXTRA_DATA");</div><div class="line"> if (extData != null && !extData.isEmpty()) {</div><div class="line"> editor.putString("SAMPLE_DATA", extData);</div><div class="line"> } else {</div><div class="line"> editor.putString("SAMPLE_DATA", "sample intent data");</div><div class="line"> }</div><div class="line"> editor.apply();</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line">@Test</div><div class="line"> public void testWriteData() {</div><div class="line"> Application application = RuntimeEnvironment.application;</div><div class="line"> RoboSharedPreferences roboSharedPreferences = (RoboSharedPreferences) application</div><div class="line"> .getSharedPreferences("example", Context.MODE_PRIVATE);</div><div class="line"> SampleIntentService intentService = new SampleIntentService();</div><div class="line"> Intent intent = new Intent();</div><div class="line"> intent.putExtra("EXTRA_DATA", "swx_sample");</div><div class="line"> intentService.onHandleIntent(intent);</div><div class="line"> Assert.assertEquals(roboSharedPreferences.getString("SAMPLE_DATA", ""), "swx_sample");</div><div class="line"> }</div></pre></td></tr></table></figure></p><p>Checkbox测试<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">@Test</div><div class="line">public void testCheckbox() {</div><div class="line"> TextView btnInverse = (TextView) mSampleActivity.findViewById(R.id.tv_inverse);</div><div class="line"> CheckBox cbState = (CheckBox) mSampleActivity.findViewById(R.id.cb_state);</div><div class="line"> Assert.assertFalse(cbState.isChecked());</div><div class="line"> btnInverse.performClick();</div><div class="line"> Assert.assertTrue(cbState.isChecked());</div><div class="line"></div><div class="line"> btnInverse.performClick();</div><div class="line"> Assert.assertFalse(cbState.isChecked());</div><div class="line">}</div></pre></td></tr></table></figure></p><p>对于Android中的每一个组件,Robolectric中都有对应的类。Application与ShadowApplication对应,Toast与ShadowToast对应等。Robolectric中也提供了方法自己去实现Shadow类,并提供方法在测试框架中调用自己实现的Shadow类。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line">public class NSIEntry {</div><div class="line"> private float x = 0.0f;</div><div class="line"></div><div class="line"> public NSIEntry() {</div><div class="line"></div><div class="line"> }</div><div class="line"></div><div class="line"> public NSIEntry(float x) {</div><div class="line"> this.x = x;</div><div class="line"> }</div><div class="line"></div><div class="line"> public void setX(float x) {</div><div class="line"> this.x = x;</div><div class="line"> }</div><div class="line"></div><div class="line"> public float getX() {</div><div class="line"> return x;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p><p>首先实现对应的Shadow类,在类上加注释<code>Implements(xxx.class)</code>,在需要调用的方法前加注释<code>@Implementation</code>。<code>__constructor__</code>为其对应的构造函数。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">@Implements(NSIEntry.class)</div><div class="line">public class ShadowEntry {</div><div class="line"></div><div class="line"> @Implementation</div><div class="line"> public float getX() {</div><div class="line"> return 0.618f;</div><div class="line"> }</div><div class="line"></div><div class="line"> @Implementation</div><div class="line"> public void __constructor__() {</div><div class="line"></div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure><p>Shadow类定义出来后,下面要做的就是在测试框架中去调用它。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">@RunWith(RobolectricTestRunner.class)</div><div class="line">@Config(constants = BuildConfig.class, shadows = {ShadowEntry.class})</div><div class="line">public class CustomShadowTest {</div><div class="line"></div><div class="line"> @Test</div><div class="line"> public void test_entry() {</div><div class="line"> //ShadowEntry将自动代替原始对象,调用Shadow对象的数据和行为</div><div class="line"> NSIEntry entry = new NSIEntry(2.0f);</div><div class="line"> Assert.assertEquals(0.618f, entry.getX(), 0.0001f);</div><div class="line"></div><div class="line"> //获取Entry对象对应的ShadowEntry</div><div class="line"> ShadowEntry shadowEntry = (ShadowEntry) ShadowExtractor.extract(entry);</div><div class="line"> Assert.assertEquals(0.618f, shadowEntry.getX(), 0.0001f);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p><p>该测试是能通过的,构造的时候,传进去的值为2.0f,调用getX()的时候,被替换为Shadow类的函数调用,结果变为0.618。</p><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>写单元测试的时候,一般用JUnit作为主要框架,Mockito在实现依赖隔离,对于Android代码的测试,可以引入Robolectric来进行测试。写单元测试并不难,要写出能被测试的代码比较难。</p>]]></content>
<summary type="html">
<p>单元测试框架分为Java平台下的测试框架和Android平台下的测试框架,Java平台下常用的框架有Junit,Mockito,Powermockito等,Android平台下的测试框架有Robolectric和Espresso等。<br>Junit运行在jvm上,一般自动化测试都会使用其他测试框架搭配Junit测试框架使用,Mockito框架可以解决单元测试中对象依赖的问题。Powermockito在功能上扩展了Mockito功能,Powermockito能够mock静态、final、私有等方法 ,这些在Mockito中不能完成的。Espresso是Google官方出的Android测试框架,语法简单,缺点是只能在真机上进行测试。Robolectric不需要借助Android手机,就能完成对Android代码的自动化测试。</p>
</summary>
<category term="编程" scheme="https://shenwenxing.com/categories/programming/"/>
<category term="Android" scheme="https://shenwenxing.com/tags/Android/"/>
<category term="单元测试" scheme="https://shenwenxing.com/tags/unit-test/"/>
</entry>
<entry>
<title>Markdown学习手记</title>
<link href="https://shenwenxing.com/mark-down-study.html"/>
<id>https://shenwenxing.com/mark-down-study.html</id>
<published>2015-10-30T06:18:34.000Z</published>
<updated>2017-09-08T07:17:01.000Z</updated>
<content type="html"><![CDATA[<p>Markdown的语法和emacs中org模式的语法很接近,也非常简单。之前用过emacs中的org模式,所以切换到Markdown文档没有遇到太多阻碍。</p><a id="more"></a><h3 id="基本语法"><a href="#基本语法" class="headerlink" title="基本语法"></a>基本语法</h3><h4 id="插入超链接"><a href="#插入超链接" class="headerlink" title="插入超链接"></a>插入超链接</h4><pre><code>[链接文字](链接url)eg: [小米](http://www.mi.com)</code></pre><p>效果:<a href="http://www.mi.com" target="_blank" rel="external">小米</a></p><h4 id="插入图片"><a href="#插入图片" class="headerlink" title="插入图片"></a>插入图片</h4><p><code>![alt文字](图片地址)</code>,用这种方式在Markdown插入图片是没法指定图片的宽和高的,不过可以使用<a href="http://www.qiniu.com/" target="_blank" rel="external">七牛</a>做为图床,然后使用七牛提供的<a href="http://developer.qiniu.com/docs/v6/api/reference/fop/image/imageview2.html#imageView2-specification" target="_blank" rel="external">图片接口</a>返回指定宽高的图片。</p><pre><code>使用原图:![Alt文字](http://7xntab.com1.z0.glb.clouddn.com/xxx/xxx.JPG)限定缩略图最大宽度为200,最大高度为600![Alt文字](http://7xntab.com1.z0.glb.clouddn.com/xxx/xxx.JPG?imageView2/2/w/200/h/600)</code></pre><h4 id="插入代码"><a href="#插入代码" class="headerlink" title="插入代码"></a>插入代码</h4><p>使用一对 ``` 将代码包起来,就可以格式化输出代码,记得<code>代码前空一行</code>。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">#include<stdio.h></div><div class="line">int main(){</div><div class="line"> printf("hello Mark Down\n");</div><div class="line"> return 0;</div><div class="line">}</div></pre></td></tr></table></figure><h4 id="文字修饰"><a href="#文字修饰" class="headerlink" title="文字修饰"></a>文字修饰</h4><p>需要高亮的文字用两个`包起来,两个星号<code>*</code>和下划线<code>_</code>包含的文字会变成斜体,四个星号和下划线包含的文字会变成粗体。</p><pre><code>`高亮文字`*Hello* _Markdown_**Hi** __Github__</code></pre><p>效果:</p><p><code>高亮文字</code></p><p><em>Hello</em> <em>Markdown</em></p><p><strong>Hi</strong> <strong>Github</strong></p><h4 id="段落引用"><a href="#段落引用" class="headerlink" title="段落引用"></a>段落引用</h4><p>引用某段文字,在段落前加上<code>></code>即可。</p><p>> I have a dream that one day this nation will rise up, live up to the true meaning of its creed: “We hold these truths to be self-evident; that all men are created equal.”</p><p>> I have a dream that one day on the red hills of Georgia the sons of former slaves and the sons of former slave-owners will be able to sit down together at the table of brotherhood.</p><p>效果:</p><blockquote><p>I have a dream that one day this nation will rise up, live up to the true meaning of its creed: “We hold these truths to be self-evident; that all men are created equal.”</p><p>I have a dream that one day on the red hills of Georgia the sons of former slaves and the sons of former slave-owners will be able to sit down together at the table of brotherhood.</p></blockquote><h4 id="使用列表"><a href="#使用列表" class="headerlink" title="使用列表"></a>使用列表</h4><p>Markdown中可以使用无序列表和有序列表,无序列表用星号<code>*</code>,加号<code>+</code>,减号<code>-</code>作为行首标记。有序列表使用<code>数字</code>接一个英文句号<code>.</code>作为行首标记,标记后面需要空一格。</p><pre><code>* 第一列+ 第二列- 第三列1. 第一列2. 第二列3. 第三列</code></pre><p>效果:</p><ul><li>第一列</li></ul><ul><li>第二列</li></ul><ul><li>第三列</li></ul><ol><li>第一列</li><li>第二列</li><li>第三列</li></ol><h4 id="字符转义"><a href="#字符转义" class="headerlink" title="字符转义"></a>字符转义</h4><p>有的时候我们不希望对特殊字符进行转义,只需要在特殊字符前加一个<code>反斜杠\</code>就可以了,比如输入\`就可以禁止`转义。</p><h3 id="emacs中编写Markdown"><a href="#emacs中编写Markdown" class="headerlink" title="emacs中编写Markdown"></a>emacs中编写Markdown</h3><p>通过emacs的markdown模式能非常方便的编辑Markdown格式的文件,首先需要下载<a href="http://jblevins.org/projects/markdown-mode/markdown-mode.el" target="_blank" rel="external">markdown-mode.el</a>,然后将markdown-mode.el文件放入emacs的加载路径下,比如<em>~/.emacs.d/\</em>文件夹中,最后在<code>.emacs</code>中加入以下代码,使得emacs在打开<code>.md</code>,<code>.markdown</code>,<code>.text</code>文件的时候自动使用markdown模式。</p><pre><code>(autoload 'markdown-mode "markdown-mode""Major mode for editing Markdown files" t)(add-to-list 'auto-mode-alist '("\\.text\\'" . markdown-mode))(add-to-list 'auto-mode-alist '("\\.markdown\\'" . markdown-mode))(add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-mode))</code></pre><h4 id="快捷键"><a href="#快捷键" class="headerlink" title="快捷键"></a>快捷键</h4><table><thead><tr><th>快捷键</th><th style="text-align:center">作用</th><th style="text-align:center">Markdown符号</th></tr></thead><tbody><tr><td>C-c C-t 数字</td><td style="text-align:center">标题</td><td style="text-align:center"># #</td></tr><tr><td>C-c C-s e</td><td style="text-align:center">斜体</td><td style="text-align:center">* *</td></tr><tr><td>C-c C-s s</td><td style="text-align:center">粗体</td><td style="text-align:center">** **</td></tr><tr><td>C-c C-s b</td><td style="text-align:center">引用</td><td style="text-align:center">></td></tr><tr><td>C-c C-i i</td><td style="text-align:center">插入图片</td><td style="text-align:center">![]()</td></tr><tr><td>C-c C-a l</td><td style="text-align:center">插入链接</td><td style="text-align:center">[]()</td></tr><tr><td>C-c -</td><td style="text-align:center">插入水平线</td><td style="text-align:center">——</td></tr></tbody></table><p>有些快捷键使用起来还不如直接使用Markdown符号方便,比如引用快捷键。我经常使用到的快捷键只有插入标题,插入图片,插入链接和插入水平线。</p><h3 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h3><ul><li><a href="http://jblevins.org/projects/markdown-mode/" target="_blank" rel="external">Emacs Markdown Mode</a></li></ul>]]></content>
<summary type="html">
<p>Markdown的语法和emacs中org模式的语法很接近,也非常简单。之前用过emacs中的org模式,所以切换到Markdown文档没有遇到太多阻碍。</p>
</summary>
<category term="工具" scheme="https://shenwenxing.com/categories/tools/"/>
<category term="Markdown" scheme="https://shenwenxing.com/tags/Markdown/"/>
</entry>
<entry>
<title>使用jekyll搭建博客</title>
<link href="https://shenwenxing.com/creat-blog-with-jekyll.html"/>
<id>https://shenwenxing.com/creat-blog-with-jekyll.html</id>
<published>2015-10-29T05:21:00.000Z</published>
<updated>2017-09-08T10:18:24.000Z</updated>
<content type="html"><![CDATA[<p>一直以来都想搭建一个属于自己的博客,在上面放一些自己东西,搭建博客虽说不是很难,但还是繁琐,需要自己去购买虚拟服务器、购买域名、将博客程序部署在虚拟服务器上,搭建完成后维护的工作也并不轻松,对于我这种比较懒的人来说简直不能忍受。工作过程中,接触Github有了一段时间,在浏览的时候发现Github上能托管自己的博客,稍微了解了一下,发现对于码农来说,让博客run起来比以前容易多了。于是,在工作之余,抽出空闲时间,搭建了自己的博客。</p><a id="more"></a><h3 id="搭建过程"><a href="#搭建过程" class="headerlink" title="搭建过程"></a>搭建过程</h3><h4 id="准备"><a href="#准备" class="headerlink" title="准备"></a>准备</h4><p>搭建博客过程中必须要用到的有 <code>git</code>,和<code>Github账号</code>,git可以直接去<a href="http://git-scm.com/download/" target="_blank" rel="external">git官网</a>下载,Github账号需要在<a href="https://github.com" target="_blank" rel="external">Github</a>上面注册。Github的博客系统使用的是<code>jekyll</code>的引擎,为了方便本地调试,可以在本地安装一个<a href="http://jekyll.bootcss.com/" target="_blank" rel="external">jekyll</a>。安装jekyll并不是必须的。</p><h4 id="流程"><a href="#流程" class="headerlink" title="流程"></a>流程</h4><p>核心流程分为注册Github账号,在Github上建立博客仓库,撰写博文,将文章推送到Github等几个步骤,整个流程可以参考<a href="https://pages.github.com/" target="_blank" rel="external">Pages官网</a>。</p><h5 id="注册账号"><a href="#注册账号" class="headerlink" title="注册账号"></a>注册账号</h5><p>注册Github账号相当简单,直接在<a href="https://github.com" target="_blank" rel="external">Github主页</a>进行注册即可。</p><h5 id="建立远程仓库"><a href="#建立远程仓库" class="headerlink" title="建立远程仓库"></a>建立远程仓库</h5><p>注册完后,需要<a href="https://github.com/new" target="_blank" rel="external">新建</a>一个特定的仓库来保存博客,该仓库的名称必须是<code>username.github.io</code>,<code>username</code>为注册Github时候的用户名,访问博客的时候,使用域名<code>username.github.io</code>就能访问了。</p><h5 id="克隆仓库"><a href="#克隆仓库" class="headerlink" title="克隆仓库"></a>克隆仓库</h5><p>在Github上创建仓库后,需要将仓库clone到本地,以便在本地使用自己顺手的工具来撰写文章。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$git clone https://github.com/username/username.github.io.git</div></pre></td></tr></table></figure><h5 id="发布主页"><a href="#发布主页" class="headerlink" title="发布主页"></a>发布主页</h5><p>进入本地博客根目录,创建index.html文件,然后推送到远程仓库中。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">$cd username.github.io</div><div class="line">$echo "my blog" > index.html</div><div class="line">$git add .</div><div class="line">$git commit -m "add blog file"</div><div class="line">$git push origin master</div></pre></td></tr></table></figure><p>做完这些步骤后,就能通过博客的域名访问你刚才推送的index.html网页了,至此,搭建博客就算初步完成了。但是,如果发布博客的时候,每次写文章都需要写html文件,同时编辑index文件来导航到我们的每一篇文章,将会相当麻烦,幸好Jekyll将这两个难题都解决了。我们可以在本地编写符合Jekyll规范的网站源码,然后上传的Github上,这样就能够简单方便的发布博文了。</p><h4 id="创建Jekyll规范的站点"><a href="#创建Jekyll规范的站点" class="headerlink" title="创建Jekyll规范的站点"></a>创建Jekyll规范的站点</h4><p>为了方便撰写博客文章,同时也为了方便对文章进行管理,我们需要创建符合Jekyll规范的站点,Jekyll最主要的文件和文件夹如下:</p><pre><code>_config.yml 整个系统的配置文件_includes 放头文件的文件夹,一般会被_layouts文件夹中的文件使用_layouts 放布局文件的文件夹,写文章的时候采用里面的布局文件_posts 放文章的文件夹index.html 博客系统主页面</code></pre><p><code>_config.yml</code>文件为博客网站系统的配置文件,在该文件中可以设置一些变量,然后写博客的时候就可以直接引用了。例如配置变量<code>title:myblog</code>,在博客中就可以通访问该变量。</p><p><code>_includes</code>文件夹中存放一些被其他文件引用的公共文件,主要是为了模块化和统一风格用。例如可以新建一个head.html的文件来控制页面标题的显示,内容如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><head></div><div class="line"> <meta charset="utf-8"></div><div class="line"> <title></div><div class="line"> {% if page.title %}{{ page.title }}{% else %}{{ site.title }}{% endif %}</div><div class="line"> </title></div><div class="line"></head></div></pre></td></tr></table></figure><p><code>page.title</code>表示当前页面的标题,该语句表示如果当前页面设置了标题,就显示当前页面的标题,否则显示站点的标题。</p><p><code>_layouts</code>中存放的是页面布局文件,对于相同风格的页面可以使用同一布局文件。比如:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><!DOCTYPE html></div><div class="line"><html></div><div class="line"> { % include head.html %}</div><div class="line"> <body></div><div class="line"> { % include header.html %}</div><div class="line"> {{ content }}</div><div class="line"> </body></div><div class="line"></html></div></pre></td></tr></table></figure><p>该布局文件中使用了_includes文件夹中的head.html文件来统一标题风格,<code>content</code>为文章的内容部分。</p><p><code>_posts</code>中存放的是博客文件,_post文件夹中的文件名必须符合<code>2015-11-3-file-name.md</code>这样的格式才会被视为有效的文件,比如:</p><pre><code>---layout: posttitle: 在Github上搭建博客date: 2015-10-29 13:21:00categories: blogexcerpt: 在github上搭建静态的博客---### 搭建博客</code></pre><p>每篇文章的开头必须有上面的描述信息,<code>layout</code>表示使用的是<code>_layouts</code>中的哪个布局文件,<code>title</code>为该页面的标题,后面的三项可以省略。</p><p><code>index.html</code>为博客主页,用来导航到网站的其他页面。</p><h5 id="使用现有模板"><a href="#使用现有模板" class="headerlink" title="使用现有模板"></a>使用现有模板</h5><p>如果嫌麻烦不想自己去创建站点,或者觉得自己创建的站点不够漂亮,可以在网上搜索一下各种Jekyll theme,然后应用在自己的博客上,<a href="http://jekyllthemes.org" target="_blank" rel="external">Jekyll Themes</a>上面提供了很多主题,我这里使用的是<a href="https://github.com/Gaohaoyang/gaohaoyang.github.io" target="_blank" rel="external">HyG的博客</a>的主题,搜索到自己满意的主题后,将其下载下来,然后覆盖自己的博客根目录,删掉_posts文件夹内别人的文章,然后修改一下网站的配置信息,注明主题的开发者,做完这些后,上传到Github上就可以了。</p><h5 id="撰写博文"><a href="#撰写博文" class="headerlink" title="撰写博文"></a>撰写博文</h5><p>博客搭建完了后只需要将文件放在_post文件夹中,同步到远程仓库,就可以通过浏览器访问了。</p><h5 id="发布文章"><a href="#发布文章" class="headerlink" title="发布文章"></a>发布文章</h5><p>每次将文章添加到_post文件夹中后,通过git将文章从本地同步到Github来实现发布新的文章,比如:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">$git add .</div><div class="line">$git commit -m "new paper"</div><div class="line">$git push origin master</div></pre></td></tr></table></figure><h3 id="搭建本地环境-可省略"><a href="#搭建本地环境-可省略" class="headerlink" title="搭建本地环境(可省略)"></a>搭建本地环境(可省略)</h3><p>为了更方便的在本地预览博客,可以在本地安装Jekyll,安装Jekyll也相当简单,只需要在终端输入</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$gem install Jekyll</div></pre></td></tr></table></figure><p>就会自动安装Jekyll了,为了和Github上Jekyll环境保持一致,可以键入以下命令</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$gem install github-pages</div></pre></td></tr></table></figure><p>搭建完本地环境后,可以使用Jekyll按以下步骤创建博客框架</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$cd username.github.io</div><div class="line">$jekyll new .</div></pre></td></tr></table></figure><p>这时username.github.io文件夹中会自动创建出下列文件和文件夹,包含了所有Jekyll所需要的文件和文件夹。</p><pre><code>_config.yml 整个系统的配置文件_includes 放头文件的文件夹,一般会被_layouts文件夹中的文件使用_layouts 放布局文件的文件夹,写文章的时候采用里面的布局文件_posts 放文章的文件夹_site jekyll生成的站点文件夹,可以不推送到远程仓库index.html 博客系统主页面</code></pre><p>用终端进入username.github.io文件夹,输入</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$jekyll serve</div></pre></td></tr></table></figure><p>就会启动本地的博客服务器,通过浏览器访问<code>http://localhost:4000</code>就可以看到jekyll创建博客的主页了。</p><h3 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a>参考文章</h3><ul><li><a href="https://pages.github.com/" target="_blank" rel="external">Github Pages</a></li><li><a href="https://help.github.com/categories/github-pages-basics/" target="_blank" rel="external">Github Pages Basics</a></li><li><a href="https://gaohaoyang.github.io" target="_blank" rel="external">HyG的博客</a></li><li><a href="http://jekyllbootstrap.com/lessons/jekyll-introduction.html" target="_blank" rel="external">How Jekyll Works</a></li><li><a href="http://jekyllthemes.org/" target="_blank" rel="external">Jekyll Themes</a></li></ul>]]></content>
<summary type="html">
<p>一直以来都想搭建一个属于自己的博客,在上面放一些自己东西,搭建博客虽说不是很难,但还是繁琐,需要自己去购买虚拟服务器、购买域名、将博客程序部署在虚拟服务器上,搭建完成后维护的工作也并不轻松,对于我这种比较懒的人来说简直不能忍受。工作过程中,接触Github有了一段时间,在浏览的时候发现Github上能托管自己的博客,稍微了解了一下,发现对于码农来说,让博客run起来比以前容易多了。于是,在工作之余,抽出空闲时间,搭建了自己的博客。</p>
</summary>
<category term="博客搭建" scheme="https://shenwenxing.com/categories/blog/"/>
<category term="jekyll" scheme="https://shenwenxing.com/tags/jekyll/"/>
<category term="GitPage" scheme="https://shenwenxing.com/tags/GitPage/"/>
</entry>
</feed>