diff --git a/post/index.xml b/post/index.xml index 8f9bd1c..474a2ad 100644 --- a/post/index.xml +++ b/post/index.xml @@ -1,7 +1,7 @@ - Folay‘s Blog - Posts + Folay‘s Blog https://folay.top/post/ Recent posts on Folay‘s Blog Hugo -- gohugo.io @@ -20,20 +20,20 @@ https://folay.top/about/ ” 老骥伏枥,志在千里 ”,你好,我是 Folay,或者可以喊我老纪,目前在北京一家陌生社交领域的互联网公司工作。 我喜欢: 有序 木制品 面 中医推拿 我讨 - ” 老骥伏枥,志在千里 ”,你好,我是 Folay,或者可以喊我老纪,目前在北京一家陌生社交领域的互联网公司工作。

-

我喜欢:

-
    -
  • 有序
  • -
  • 木制品
  • -
  • -
  • 中医推拿
  • -
-

我讨厌:

-
    -
  • 风险
  • -
  • 情绪不稳定的人
  • -
-]]>
+ <![CDATA[&lt;p&gt;” 老骥伏枥,志在千里 ”,你好,我是 Folay,或者可以喊我老纪,目前在北京一家陌生社交领域的互联网公司工作。&lt;/p&gt; +&lt;p&gt;我喜欢:&lt;/p&gt; +&lt;ul&gt; +&lt;li&gt;有序&lt;/li&gt; +&lt;li&gt;木制品&lt;/li&gt; +&lt;li&gt;面&lt;/li&gt; +&lt;li&gt;中医推拿&lt;/li&gt; +&lt;/ul&gt; +&lt;p&gt;我讨厌:&lt;/p&gt; +&lt;ul&gt; +&lt;li&gt;风险&lt;/li&gt; +&lt;li&gt;情绪不稳定的人&lt;/li&gt; +&lt;/ul&gt; +]]> @@ -44,16 +44,16 @@ https://folay.top/post/aw/ <p>Rock spider man!</p> <p><img src="https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202403192033150.jpg" alt=""></p> - Rock spider man!

-

-

漫展里非常显眼的 ”黑白双煞“,首先是黑裙子的小姐姐。

-

131710605757

-

10061710852852

-

然后是白色。(拍这个小姐姐的人多到主摄都被挤到没位置

-

IMG_0399

-

IMG_0403

-

最后是冰晶礼服鬼化蕾姆。

-

IMG_0473

]]>
+ <![CDATA[&lt;p&gt;Rock spider man!&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202403192033150.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt; +&lt;p&gt;漫展里非常显眼的 ”黑白双煞“,首先是黑裙子的小姐姐。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202403192039816.jpg&#34; alt=&#34;131710605757&#34;&gt;&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202403192055018.jpg&#34; alt=&#34;10061710852852&#34;&gt;&lt;/p&gt; +&lt;p&gt;然后是白色。(拍这个小姐姐的人多到主摄都被挤到没位置&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202403192040092.jpg&#34; alt=&#34;IMG_0399&#34;&gt;&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202403192040138.jpg&#34; alt=&#34;IMG_0403&#34;&gt;&lt;/p&gt; +&lt;p&gt;最后是冰晶礼服鬼化蕾姆。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202403192100637.jpg&#34; alt=&#34;IMG_0473&#34;&gt;&lt;/p&gt;]]>
@@ -63,41 +63,41 @@ https://folay.top/post/infj/ 事情是这样的,在最近一坤年的跨度里我做过 5 次 MBTI 人格类型分析,最终得出的结果都是 「INFJ-A」,期间还经历了题库的几次迭代变化,所以能如此稳 - 事情是这样的,在最近一坤年的跨度里我做过 5 次 MBTI 人格类型分析,最终得出的结果都是 「INFJ-A」,期间还经历了题库的几次迭代变化,所以能如此稳定的得到统一结果,这让我还是蛮意外的。为了进一步验证 MBTI 对于人格类型归纳的准确性,我打算将网上对于 INFJ 人格的描述与自己察觉的真实情况进行比较。

-

+ <![CDATA[&lt;p&gt;事情是这样的,在最近一坤年的跨度里我做过 5 次 MBTI 人格类型分析,最终得出的结果都是 「INFJ-A」,期间还经历了题库的几次迭代变化,所以能如此稳定的得到统一结果,这让我还是蛮意外的。为了进一步验证 MBTI 对于人格类型归纳的准确性,我打算将网上对于 INFJ 人格的描述与自己察觉的真实情况进行比较。&lt;/p&gt; +&lt;p style=&#34;color: gray;&#34;&gt; 下文中引用的内容均来自于 - + &lt;a href=&#34;https://www.16personalities.com&#34; style=&#34;color: gray; text-decoration: underline;&#34; target=&#34;_blank&#34;&gt; 16personalities - + &lt;/a&gt; 网站中对于 INFJ 人格的描述。 -

-
-

INFJ 可能是最稀有的人格类型,但他们肯定会在世界上留下自己的印记。他们理想主义、有原则,不满足于平平安安地度过一生–他们想要站起来,有所作为。对于 INFJ 来说,成功不是来自金钱或地位,而是来自寻求成就感,帮助他人,成为世界上一股向善的力量。

-
-

最稀有的人格类型?哈哈查了一些资料发现,国内互联网上关于 MBTI 的内容大多都是直接 “ 英译汉 ” 从外网搬运来的,所以引用中的修饰词也要加上 “ America ” 的前缀,实际上在国内的特殊环境下成长的 95 后中 INFJ 人格类型的占比还是不少的,国内最稀少的人格类型应该是被称为「指挥官」的 ENTJ。当然这些不属于自我人格的范畴。

-

回到正题本身,“ 理想主义、原则性 ” 这些标签我是欣然接受的,毕竟寻找理论落地过程中由于人类劣根性导致的偏差是我乐子之一。但 “ 不满足于平淡生活 ” 这就有点不符合了,近几年所作的事情和近期对未来的规划都是在为畅想中的 “ 平淡生活 ” 作准备,虽然我很纠结于 “ 人生意义 ” 这个话题,但这种意义不是来自于社会地位的提升、也不是外在的物质财富,而是来自于内心畅想的实现,或者说将自己所坚持的规则(原则)具现出来并应用到现实社会。所以……哈哈太难了,畅想仅仅是畅想。

-
-

INFJ 往往会有一种与大多数人不同的感觉,由于他们丰富的内心生活和寻找生活目标的深沉而持久的渴望,他们并不总是与周围的人融为一体,幸运的是,这种不合拍的感觉并没有削弱 INFJ 让世界变得更美好的承诺。

-

INFJ 受到不公正的困扰,他们通常更关心利他主义而不是个人利益,许多 INFJ 将帮助他人视为生活中的使命,他们一直在寻找介入并为正确的事情大声疾呼的方法。具有这种人格类型的人也渴望解决社会更深层次的问题,希望不公平和艰辛能够成为过去。

-
-

别说了,像在被讽刺一样,如果我从小在一个比较包容开放,接受多元价值观的地方成长,那可能确实会像上面说的一样现在还在坚持倡导一些事情。但事实是我目前想不到任何我所能做的可以 “ 让世界变得更美好 ” 的事情,只能顾好自己和珍视的人,虽然现在看见 “ 精致的利己主义者 ” 还会恶心作呕,但也不会有任何想要争辩或者 “ 他是错的我是对的 ” 这样的想法。其实我也不确认对方是错的,只是不喜欢而已。

-
-

INFJ 可能内向,但他们重视与他人的深厚、真实的关系。很少有事情会像真正认识另一个人一样给这些性格类型带来快乐–同时也为别人所理解。INFJ 喜欢有意义的对话远远超过闲聊,他们倾向于以热情和敏感的方式进行交流。这种情感上的诚实和洞察力可以给周围的人留下深刻的印象。

-
-

这倒是真的,我虽然是个典型的 i 人,但对于认识新朋友,特别是了解新朋友、与新朋友进行深度对话,我是毫不吝啬的,在解锁一个人的人生经历、情感经历、特别是三观塑造历程的时,我内心得到的满足和快感是十分强烈的。(我始终于好奇不同成长经历对于个体三观塑造的影响)

-
-

INFJ 喜欢在工作中保持整洁有序,喜欢平和、安静的气氛,希望工作中每个人的贡献能得到肯定,每个人都有成就感,所有的工作都能和谐地取得良好的结果。

-
-

这让我不得不贴一下我使用了两年的工位了,就像我 About 页所写的一样,「有序」始终是我生活的主旋律。

-
- -
-

最后,其实之前在纠结 MBTI 准确性的时,有跟朋友讨论过 MBTI 与星座的区别,讨论过程有个类比觉得十分恰当。

-
-

一场考试中你物理得分 90 分,化学得分 60 分,说明你物理比较好,我会给你打上 “ 物理 ” 的标签,这就是 MBTI,MBTI 中对于人格的描述与自我察觉的实际情况间的差异可以看作 “ 考试中影响结果的运气等不稳定因素 ”。而星座的话,就是不用参加考试,直接根据你的生辰来判定你物理、化学所得的分数。

-
-

就这样吧,希望有个愉快的春节。

-]]>
+&lt;/p&gt; +&lt;blockquote&gt; +&lt;p&gt;INFJ 可能是最稀有的人格类型,但他们肯定会在世界上留下自己的印记。他们理想主义、有原则,不满足于平平安安地度过一生&amp;ndash;他们想要站起来,有所作为。对于 INFJ 来说,成功不是来自金钱或地位,而是来自寻求成就感,帮助他人,成为世界上一股向善的力量。&lt;/p&gt; +&lt;/blockquote&gt; +&lt;p&gt;最稀有的人格类型?哈哈查了一些资料发现,国内互联网上关于 MBTI 的内容大多都是直接 “ 英译汉 ” 从外网搬运来的,所以引用中的修饰词也要加上 “ America ” 的前缀,实际上在国内的特殊环境下成长的 95 后中 INFJ 人格类型的占比还是不少的,国内最稀少的人格类型应该是被称为「指挥官」的 ENTJ。当然这些不属于自我人格的范畴。&lt;/p&gt; +&lt;p&gt;回到正题本身,“ 理想主义、原则性 ” 这些标签我是欣然接受的,毕竟寻找理论落地过程中由于人类劣根性导致的偏差是我乐子之一。但 “ 不满足于平淡生活 ” 这就有点不符合了,近几年所作的事情和近期对未来的规划都是在为畅想中的 “ 平淡生活 ” 作准备,虽然我很纠结于 “ 人生意义 ” 这个话题,但这种意义不是来自于社会地位的提升、也不是外在的物质财富,而是来自于内心畅想的实现,或者说将自己所坚持的规则(原则)具现出来并应用到现实社会。所以&amp;hellip;&amp;hellip;哈哈太难了,畅想仅仅是畅想。&lt;/p&gt; +&lt;blockquote&gt; +&lt;p&gt;INFJ 往往会有一种与大多数人不同的感觉,由于他们丰富的内心生活和寻找生活目标的深沉而持久的渴望,他们并不总是与周围的人融为一体,幸运的是,这种不合拍的感觉并没有削弱 INFJ 让世界变得更美好的承诺。&lt;/p&gt; +&lt;p&gt;INFJ 受到不公正的困扰,他们通常更关心利他主义而不是个人利益,许多 INFJ 将帮助他人视为生活中的使命,他们一直在寻找介入并为正确的事情大声疾呼的方法。具有这种人格类型的人也渴望解决社会更深层次的问题,希望不公平和艰辛能够成为过去。&lt;/p&gt; +&lt;/blockquote&gt; +&lt;p&gt;别说了,像在被讽刺一样,如果我从小在一个比较包容开放,接受多元价值观的地方成长,那可能确实会像上面说的一样现在还在坚持倡导一些事情。但事实是我目前想不到任何我所能做的可以 “ 让世界变得更美好 ” 的事情,只能顾好自己和珍视的人,虽然现在看见 “ 精致的利己主义者 ” 还会恶心作呕,但也不会有任何想要争辩或者 “ 他是错的我是对的 ” 这样的想法。其实我也不确认对方是错的,只是不喜欢而已。&lt;/p&gt; +&lt;blockquote&gt; +&lt;p&gt;INFJ 可能内向,但他们重视与他人的深厚、真实的关系。很少有事情会像真正认识另一个人一样给这些性格类型带来快乐&amp;ndash;同时也为别人所理解。INFJ 喜欢有意义的对话远远超过闲聊,他们倾向于以热情和敏感的方式进行交流。这种情感上的诚实和洞察力可以给周围的人留下深刻的印象。&lt;/p&gt; +&lt;/blockquote&gt; +&lt;p&gt;这倒是真的,我虽然是个典型的 i 人,但对于认识新朋友,特别是了解新朋友、与新朋友进行深度对话,我是毫不吝啬的,在解锁一个人的人生经历、情感经历、特别是三观塑造历程的时,我内心得到的满足和快感是十分强烈的。(我始终于好奇不同成长经历对于个体三观塑造的影响)&lt;/p&gt; +&lt;blockquote&gt; +&lt;p&gt;INFJ 喜欢在工作中保持整洁有序,喜欢平和、安静的气氛,希望工作中每个人的贡献能得到肯定,每个人都有成就感,所有的工作都能和谐地取得良好的结果。&lt;/p&gt; +&lt;/blockquote&gt; +&lt;p&gt;这让我不得不贴一下我使用了两年的工位了,就像我 About 页所写的一样,「有序」始终是我生活的主旋律。&lt;/p&gt; +&lt;div align=&#34;center&#34;&gt; +&lt;img src=https://imgoldjii.oss-cn-beijing.aliyuncs.com/202402061752931.png width=100%/&gt; +&lt;/div&gt; +&lt;p&gt;最后,其实之前在纠结 MBTI 准确性的时,有跟朋友讨论过 MBTI 与星座的区别,讨论过程有个类比觉得十分恰当。&lt;/p&gt; +&lt;blockquote&gt; +&lt;p&gt;一场考试中你物理得分 90 分,化学得分 60 分,说明你物理比较好,我会给你打上 “ 物理 ” 的标签,这就是 MBTI,MBTI 中对于人格的描述与自我察觉的实际情况间的差异可以看作 “ 考试中影响结果的运气等不稳定因素 ”。而星座的话,就是不用参加考试,直接根据你的生辰来判定你物理、化学所得的分数。&lt;/p&gt; +&lt;/blockquote&gt; +&lt;p&gt;就这样吧,希望有个愉快的春节。&lt;/p&gt; +]]>
@@ -108,70 +108,70 @@ https://folay.top/post/318/ <p>成都太古里裸眼 3D 巨幕,很震撼。</p> <p><img src="https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202310162320454.jpg" alt="f87a1afd-55a5-4f84-82a0-039d2185e59e_1_201_a-fotor-20231016231950"></p> - 成都太古里裸眼 3D 巨幕,很震撼。

-

f87a1afd-55a5-4f84-82a0-039d2185e59e_1_201_a-fotor-20231016231950

-

雅安自驾大本营回身随拍,318 旅途的起点。

-

D8937780-FFFC-47F4-A916-435263463273_1_105_c

-

驾离市区,向大山前进,海拔开始拔升,云气触手可摸。

-

A719CE54-D096-47C0-BEB9-40490990776B_1_105_c

-

路上随处可见的经幡和国旗。

-

18F06311-BC61-4121-8F76-4CB64C8457B1_1_105_c

-

云雾弥漫中的折多山。

-

D9150F04-0806-49BD-AE90-602AD2740502_1_105_c

-

团队的第一张合照。(我是摄影狮

-

C8AF718D-3A5C-42A0-BF85-55719D77E0EB_1_105_c

-

我在折多山很想你。

-

image-20231016233815991

-

喜马拉雅山脉阻断的印度洋暖湿气流形成的云雾。

-

3518F120-7FEE-487F-9F26-90444436DBA9_1_105_c

-

新都桥民宿的藏族老板招待的牦牛肉火锅,超香!(有个伙计因为高反在休息,错过了

-

E4793CC4-9EB5-4C71-AA68-B12FAA7B3384_1_105_c

-

民宿门口的无名小河。

-

BCDF7642-B7C0-495B-9472-B292AA4A6DFD_1_201_a

-

继续出发,行驶在山野。

-

C4D737B2-6E4A-43DD-A17C-CD3C0420B0E8_1_105_c

-

观景台对面山上的藏文牌子。(在西藏支教过的堂哥说是 “ 宗喀巴大师 ” 的意思

-

A6AB89F8-516E-4A91-A61C-6155BA00087E_1_105_c

-

随处可见的雪山观景台。

-

865545D0-C93E-419E-9DFD-4065EC34F38A_1_105_c

-

牛吃草。

-

774F6031-EE3A-494A-B86B-31125CA066D7_1_105_c

-

山在脚下,张开手就可以拥抱云彩。

-

-

Windows 10 壁纸。

-

D28310F2-7B4D-4AD9-9B6D-FEC81F903C40_1_105_c

-

草原哈雷。

-

FDBE6E1F-BE18-4608-86DA-1BD25FE42AAB_1_105_c

-

甘孜的 “ 蒙古包 ”。

-

7ACB93F8-BD79-48F7-9D0B-0A7DAAD6D3B4_1_105_c

-

万岁!

-

29388513-6F7D-46F4-A728-FE199D0E88D2_1_105_c

-

到达世界最高城,理塘!(偶遇一个很帅的爆改 3 系

-

8D1CAD08-E254-44DC-A92A-1AF84242CDF8_1_105_c

-

初入稻城亚丁。(要坐 1 个小时大巴车,累的很

-

2C1078D8-437C-48D8-9DEC-4A1C6501D065_1_105_c

-

跟 Chichi 一起爬央迈勇神山。

-

image-20231017004646532

-

珍珠海(藏语叫 “ 卓玛拉措 “,其实只是一个大一点的水塘

-

E0964912-B26C-4A6B-AD1F-660253AD207D_1_105_c

-

亚丁里的雪山,忘记是哪座了。(在亚丁后面相机没电了,拍的比较少

-

791C9072-9EF5-4B3D-A109-32392A4926DA_1_105_c

-

跟 Chichi 堆的玛尼堆。

-

4D284DDE-DEB7-4D14-9664-5F2A2E5BCA84_1_105_c

-

海子山的姊妹湖!

-

314C1B67-C73C-4141-B288-AE8993236075_1_105_c

-

观景台附近的移动咖啡店。

-

AAC4EC2C-D34A-40BB-9EBF-5CFAC9274DF9_1_105_c

-

一只狗

-

86205820-3A51-45D1-AE6C-C9E1EEFF7EA9_1_105_c

-

初入措普沟(可以感觉出来巴塘官方是真心想发展这个景区,对外游客特别友好,力推!

-

BDAF952D-EDEB-4934-9440-3E145563B0E2_1_105_c

-

措普沟圣山脚下的 Tiffany 蓝。

-

E5494276-2342-4FC1-924D-31B256556191_1_105_c

-

三小只。

-

1591697707273_.pic_hd

-

色拉山垭口拍的南迦巴瓦峰的日照金山。

-

B40C550B-E1A1-4184-927A-9AF93E4F3518_1_105_c

]]>
+ <![CDATA[&lt;p&gt;成都太古里裸眼 3D 巨幕,很震撼。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202310162320454.jpg&#34; alt=&#34;f87a1afd-55a5-4f84-82a0-039d2185e59e_1_201_a-fotor-20231016231950&#34;&gt;&lt;/p&gt; +&lt;p&gt;雅安自驾大本营回身随拍,318 旅途的起点。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202310162323482.jpeg&#34; alt=&#34;D8937780-FFFC-47F4-A916-435263463273_1_105_c&#34;&gt;&lt;/p&gt; +&lt;p&gt;驾离市区,向大山前进,海拔开始拔升,云气触手可摸。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202310162327502.jpeg&#34; alt=&#34;A719CE54-D096-47C0-BEB9-40490990776B_1_105_c&#34;&gt;&lt;/p&gt; +&lt;p&gt;路上随处可见的经幡和国旗。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202310162332550.jpeg&#34; alt=&#34;18F06311-BC61-4121-8F76-4CB64C8457B1_1_105_c&#34;&gt;&lt;/p&gt; +&lt;p&gt;云雾弥漫中的折多山。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202310162334121.jpeg&#34; alt=&#34;D9150F04-0806-49BD-AE90-602AD2740502_1_105_c&#34;&gt;&lt;/p&gt; +&lt;p&gt;团队的第一张合照。(我是摄影狮&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202310162336242.jpeg&#34; alt=&#34;C8AF718D-3A5C-42A0-BF85-55719D77E0EB_1_105_c&#34;&gt;&lt;/p&gt; +&lt;p&gt;我在折多山很想你。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202310162338019.png&#34; alt=&#34;image-20231016233815991&#34;&gt;&lt;/p&gt; +&lt;p&gt;喜马拉雅山脉阻断的印度洋暖湿气流形成的云雾。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202310162343860.jpeg&#34; alt=&#34;3518F120-7FEE-487F-9F26-90444436DBA9_1_105_c&#34;&gt;&lt;/p&gt; +&lt;p&gt;新都桥民宿的藏族老板招待的牦牛肉火锅,超香!(有个伙计因为高反在休息,错过了&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202310162344276.jpeg&#34; alt=&#34;E4793CC4-9EB5-4C71-AA68-B12FAA7B3384_1_105_c&#34;&gt;&lt;/p&gt; +&lt;p&gt;民宿门口的无名小河。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202310162347661.jpeg&#34; alt=&#34;BCDF7642-B7C0-495B-9472-B292AA4A6DFD_1_201_a&#34;&gt;&lt;/p&gt; +&lt;p&gt;继续出发,行驶在山野。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202310162348562.jpeg&#34; alt=&#34;C4D737B2-6E4A-43DD-A17C-CD3C0420B0E8_1_105_c&#34;&gt;&lt;/p&gt; +&lt;p&gt;观景台对面山上的藏文牌子。(在西藏支教过的堂哥说是 “ 宗喀巴大师 ” 的意思&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202310162357502.jpeg&#34; alt=&#34;A6AB89F8-516E-4A91-A61C-6155BA00087E_1_105_c&#34;&gt;&lt;/p&gt; +&lt;p&gt;随处可见的雪山观景台。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202310162358030.jpeg&#34; alt=&#34;865545D0-C93E-419E-9DFD-4065EC34F38A_1_105_c&#34;&gt;&lt;/p&gt; +&lt;p&gt;牛吃草。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202310162359385.jpeg&#34; alt=&#34;774F6031-EE3A-494A-B86B-31125CA066D7_1_105_c&#34;&gt;&lt;/p&gt; +&lt;p&gt;山在脚下,张开手就可以拥抱云彩。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202310170002027.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt; +&lt;p&gt;Windows 10 壁纸。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202310170004008.jpeg&#34; alt=&#34;D28310F2-7B4D-4AD9-9B6D-FEC81F903C40_1_105_c&#34;&gt;&lt;/p&gt; +&lt;p&gt;草原哈雷。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202310170005430.jpeg&#34; alt=&#34;FDBE6E1F-BE18-4608-86DA-1BD25FE42AAB_1_105_c&#34;&gt;&lt;/p&gt; +&lt;p&gt;甘孜的 “ 蒙古包 ”。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202310170006276.jpeg&#34; alt=&#34;7ACB93F8-BD79-48F7-9D0B-0A7DAAD6D3B4_1_105_c&#34;&gt;&lt;/p&gt; +&lt;p&gt;万岁!&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202310170007526.jpeg&#34; alt=&#34;29388513-6F7D-46F4-A728-FE199D0E88D2_1_105_c&#34;&gt;&lt;/p&gt; +&lt;p&gt;到达世界最高城,理塘!(偶遇一个很帅的爆改 3 系&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202310170007314.jpeg&#34; alt=&#34;8D1CAD08-E254-44DC-A92A-1AF84242CDF8_1_105_c&#34;&gt;&lt;/p&gt; +&lt;p&gt;初入稻城亚丁。(要坐 1 个小时大巴车,累的很&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202310170012295.jpeg&#34; alt=&#34;2C1078D8-437C-48D8-9DEC-4A1C6501D065_1_105_c&#34;&gt;&lt;/p&gt; +&lt;p&gt;跟 Chichi 一起爬央迈勇神山。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202310170046579.png&#34; alt=&#34;image-20231017004646532&#34;&gt;&lt;/p&gt; +&lt;p&gt;珍珠海(藏语叫 “ 卓玛拉措 “,其实只是一个大一点的水塘&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/202310191620300.jpeg&#34; alt=&#34;E0964912-B26C-4A6B-AD1F-660253AD207D_1_105_c&#34;&gt;&lt;/p&gt; +&lt;p&gt;亚丁里的雪山,忘记是哪座了。(在亚丁后面相机没电了,拍的比较少&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/202310191622184.jpeg&#34; alt=&#34;791C9072-9EF5-4B3D-A109-32392A4926DA_1_105_c&#34;&gt;&lt;/p&gt; +&lt;p&gt;跟 Chichi 堆的玛尼堆。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/202310191623373.jpeg&#34; alt=&#34;4D284DDE-DEB7-4D14-9664-5F2A2E5BCA84_1_105_c&#34;&gt;&lt;/p&gt; +&lt;p&gt;海子山的姊妹湖!&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/202310191624260.jpeg&#34; alt=&#34;314C1B67-C73C-4141-B288-AE8993236075_1_105_c&#34;&gt;&lt;/p&gt; +&lt;p&gt;观景台附近的移动咖啡店。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/202310191625187.jpeg&#34; alt=&#34;AAC4EC2C-D34A-40BB-9EBF-5CFAC9274DF9_1_105_c&#34;&gt;&lt;/p&gt; +&lt;p&gt;一只狗&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/202310191626270.jpeg&#34; alt=&#34;86205820-3A51-45D1-AE6C-C9E1EEFF7EA9_1_105_c&#34;&gt;&lt;/p&gt; +&lt;p&gt;初入措普沟(可以感觉出来巴塘官方是真心想发展这个景区,对外游客特别友好,力推!&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/202310191627568.jpeg&#34; alt=&#34;BDAF952D-EDEB-4934-9440-3E145563B0E2_1_105_c&#34;&gt;&lt;/p&gt; +&lt;p&gt;措普沟圣山脚下的 Tiffany 蓝。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/202310191630465.jpeg&#34; alt=&#34;E5494276-2342-4FC1-924D-31B256556191_1_105_c&#34;&gt;&lt;/p&gt; +&lt;p&gt;三小只。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/202310191723943.jpg&#34; alt=&#34;1591697707273_.pic_hd&#34;&gt;&lt;/p&gt; +&lt;p&gt;色拉山垭口拍的南迦巴瓦峰的日照金山。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/202310191633790.jpeg&#34; alt=&#34;B40C550B-E1A1-4184-927A-9AF93E4F3518_1_105_c&#34;&gt;&lt;/p&gt;]]>
@@ -184,42 +184,42 @@ <div align="center"> <img src=https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202310192146502.png width=100%/> </div> - 最近在网上经常有看到类似上面这种图片,图片里的建筑物或者是光影形成了一些文字,给图片附加了更多的信息。评论有很多人很好奇,所以记录下制作方式。

-
- -
-

其实制作的方法很简单,只是在基本 text2img 的基础上使用 ControlNet 进行垫图并附加一些光影性强的 Model 即可。

-

Checkpoint 配置

-
    -
  • Checkpoint Model:推荐使用写实类的模型,比如 majicMIX realistic、dalcefoPainting 3rd。
  • -
  • Sampler method:推荐使用 DMP++ 2S a Karras、DMP++ 2M Karras。
  • -
  • Sampling Steps:推荐 25 左右,不要超过 30,因为要实现光影文字效果,Step 数太多会渲染掉文-字效果。
  • -
-
- -
-

底图制作

-

需要用到一张黑底白字的图片,用什么做随意,用你熟悉工具即可,尺寸需要跟你上面配置的 Size 信息保持一致,以保证文字的位置是准确的,比如下图。

-
- -
-

另外,如果你能让文字的边缘实现模糊效果就更好了,生成的图片光影文字效果会更贴合。

-

ControlNet 配置

-
    -
  • ControlNet Model:对出图效果的影响比较关键,推荐使用 control_v1p_sd15_brightness、lightingBasedPicture_v10,都是比较优秀的光影 Model。
  • -
  • ControlNet Type:全部
  • -
  • 预处理器:推荐 none,也可以使用 tile_colorfix,但是要搭配 control_v11f1e_sd15_tile 模型。
  • -
  • Control Weight:0.4 ~ 0.55
  • -
  • Starting Control Step:0 ~ 0.1
  • -
  • Ending Control Step:0.45 ~ 0.6
  • -
-
- -
-

最后就抽卡吧,慢慢抽,另外附上一张网友测试的 Control Step 与出图效果的对比图。

-
- -
]]>
+ <![CDATA[&lt;p&gt;最近在网上经常有看到类似上面这种图片,图片里的建筑物或者是光影形成了一些文字,给图片附加了更多的信息。评论有很多人很好奇,所以记录下制作方式。&lt;/p&gt; +&lt;div align=&#34;center&#34;&gt; +&lt;img src=https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202310192146502.png width=100%/&gt; +&lt;/div&gt; +&lt;p&gt;其实制作的方法很简单,只是在基本 text2img 的基础上使用 ControlNet 进行垫图并附加一些光影性强的 Model 即可。&lt;/p&gt; +&lt;h2 id=&#34;checkpoint-配置&#34;&gt;Checkpoint 配置&lt;/h2&gt; +&lt;ul&gt; +&lt;li&gt;Checkpoint Model:推荐使用写实类的模型,比如 majicMIX realistic、dalcefoPainting 3rd。&lt;/li&gt; +&lt;li&gt;Sampler method:推荐使用 DMP++ 2S a Karras、DMP++ 2M Karras。&lt;/li&gt; +&lt;li&gt;Sampling Steps:推荐 25 左右,不要超过 30,因为要实现光影文字效果,Step 数太多会渲染掉文-字效果。&lt;/li&gt; +&lt;/ul&gt; +&lt;div align=&#34;center&#34;&gt; +&lt;img src=https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202310192143384.png width=100%/&gt; +&lt;/div&gt; +&lt;h2 id=&#34;底图制作&#34;&gt;底图制作&lt;/h2&gt; +&lt;p&gt;需要用到一张黑底白字的图片,用什么做随意,用你熟悉工具即可,尺寸需要跟你上面配置的 Size 信息保持一致,以保证文字的位置是准确的,比如下图。&lt;/p&gt; +&lt;div align=&#34;center&#34;&gt; +&lt;img src=https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202310192150418.png width=100%/&gt; +&lt;/div&gt; +&lt;p&gt;另外,如果你能让文字的边缘实现模糊效果就更好了,生成的图片光影文字效果会更贴合。&lt;/p&gt; +&lt;h2 id=&#34;controlnet-配置&#34;&gt;ControlNet 配置&lt;/h2&gt; +&lt;ul&gt; +&lt;li&gt;ControlNet Model:对出图效果的影响比较关键,推荐使用 control_v1p_sd15_brightness、lightingBasedPicture_v10,都是比较优秀的光影 Model。&lt;/li&gt; +&lt;li&gt;ControlNet Type:全部&lt;/li&gt; +&lt;li&gt;预处理器:推荐 none,也可以使用 tile_colorfix,但是要搭配 control_v11f1e_sd15_tile 模型。&lt;/li&gt; +&lt;li&gt;Control Weight:0.4 ~ 0.55&lt;/li&gt; +&lt;li&gt;Starting Control Step:0 ~ 0.1&lt;/li&gt; +&lt;li&gt;Ending Control Step:0.45 ~ 0.6&lt;/li&gt; +&lt;/ul&gt; +&lt;div align=&#34;center&#34;&gt; +&lt;img src=https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202310192144711.png width=100%/&gt; +&lt;/div&gt; +&lt;p&gt;最后就抽卡吧,慢慢抽,另外附上一张网友测试的 Control Step 与出图效果的对比图。&lt;/p&gt; +&lt;div align=&#34;center&#34;&gt; +&lt;img src=https://imgoldjii.oss-cn-beijing.aliyuncs.com/picgo/202310192154764.webp width=100%/&gt; +&lt;/div&gt;]]>
@@ -229,18 +229,18 @@ https://folay.top/post/read_travel_love/ 现代生活无时无刻不经受着冲击,滚滚历史八面来风,如何在纷纭中走自己的人生路?将阅读梁永安老师的《阅读、游历和爱情&rdquo;》一书过程中的 - 现代生活无时无刻不经受着冲击,滚滚历史八面来风,如何在纷纭中走自己的人生路?将阅读梁永安老师的《阅读、游历和爱情”》一书过程中的所得、体会记录于此。

-

关于自我,涉及多个层面。一个是本然的自我,一路成长过来所形成的自我,是以往你所有选择的结果。另一个是想象中的自我,是经过自我意识洗礼,变形折射而来的,不是本真的自我。还有一个是理想中的自我,即自己觉得我应该是怎样的。

-

认识自己的过程就是寻找本然自我的过程,首先一定要了解自己在社会和历史舞台上所处的位置,而不是做一个漫无目的的自然人,现在很多人看不清自我,就是不知道自己所处在什么位置上,有什么样的价值,千万别在你还没了解这个世界的时候,就把自己固定了。

-

在失去坐标的转型时代,青年人该如何定位自己?青年要认识自我,不能坐在房子里,坐井观天,往往需要在不完美的探索中认识自己,有痛苦,有欢乐,于痛苦中发现自己活着,于欢乐中发现自己还很平庸,在这个过程中,才逐渐知道自己热爱什么样的生活,跟什么样的世界联系在一起。这就是所谓青春的激情。

-

在这个过程中 孤独是很重要的环节,年轻的时候扔了很多珍贵的东西,到后来才发现,当年所藐视的、不值一提的东西才是最宝贵的,我们经常将无意义的东西误以为宝,只有在孤独中、在自己的选择里,体会出来的东西才是最真实的。

-

本然的自我一定是不完美的,但确实独一无二的,人只有受到阻力时,才能触动自己,反思自己的生命是否真实。一个人一旦通过这种野性的方式触动自己,心里便会一片透亮,感觉特别有价值感、幸福感,而人一旦体会到这种感觉,就不肯放弃了。这种愉悦一定是在路上探索才能体会的,不经一番寒彻骨,怎得梅花扑鼻香。

-

希望我们这新一代的人有生而为人的生命的连续性,在他的儿童、少年、青年、中年、老年各个时期,都能活得很真实,是一个由内向外生活的人,而不是由外向内、活在别人眼光里的人。

-

书中也有梁老师关于人格、修养的阐述,但我其实不太赞成梁永安老师在这本书中对于好人的定义,人格的好坏不能与认知是否清醒、人生价值是否实现相关联,单纯的、愚昧的、不思进取的人可能有一个发自内心对外在释放善意的人格,所幸的是对于道德在于人格的重要性,跟梁老师的看法还是一致的,就像康德所说,“ 有两种东西,我对它们的思考越是深沉和持久,它们在我心灵中唤起的惊奇和敬畏就会日新月异,不断增长,这就是我头上的星空和心中的道德定律。”

-

我们偶然来到这个世界,活着是为了什么? 很多人是以获得为目的的,但是一个人究竟需要多少获得?很多人不珍惜自己,把自己放在虚荣、竞争等相对价值里,以为这样就可以令人羡慕,但最终又有怎样的价值呢?我们有可能正在恶的链条上发力,争取着虚名浮利,仅仅以周围的人为参照人群——我要过得比他们好,这其实是对自己价值非常大的贬低。

-

真正有价值的是寻找、珍惜自我,懂得这个世界的自由,珍惜个人生命的自由,关注自己的价值和社会需要的价值,确认自己能够做些什么,最终做出一些跟别人不一样的事情,为社会增添一些亮光,而不是执着于千篇一律的东西。这时,我们才会有一种全新的生命观。

-

最后,送给大家一句我很喜欢的话,这也是目前我对于生活的态度,“ 拥有可以接纳并允许一切发生的可能,从自我出发去分享表达,但不对反馈反响抱有限定的期待 ”。

-]]>
+ <![CDATA[&lt;p&gt;现代生活无时无刻不经受着冲击,滚滚历史八面来风,如何在纷纭中走自己的人生路?将阅读梁永安老师的《阅读、游历和爱情&amp;rdquo;》一书过程中的所得、体会记录于此。&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;关于自我&lt;/strong&gt;,涉及多个层面。一个是本然的自我,一路成长过来所形成的自我,是以往你所有选择的结果。另一个是想象中的自我,是经过自我意识洗礼,变形折射而来的,不是本真的自我。还有一个是理想中的自我,即自己觉得我应该是怎样的。&lt;/p&gt; +&lt;p&gt;认识自己的过程就是寻找本然自我的过程,首先一定要了解自己在社会和历史舞台上所处的位置,而不是做一个漫无目的的自然人,现在很多人看不清自我,就是不知道自己所处在什么位置上,有什么样的价值,千万别在你还没了解这个世界的时候,就把自己固定了。&lt;/p&gt; +&lt;p&gt;在失去坐标的转型时代,青年人该如何定位自己?青年要认识自我,不能坐在房子里,坐井观天,往往需要在不完美的探索中认识自己,有痛苦,有欢乐,于痛苦中发现自己活着,于欢乐中发现自己还很平庸,在这个过程中,才逐渐知道自己热爱什么样的生活,跟什么样的世界联系在一起。这就是所谓青春的激情。&lt;/p&gt; +&lt;p&gt;在这个过程中 &lt;strong&gt;孤独是很重要的环节&lt;/strong&gt;,年轻的时候扔了很多珍贵的东西,到后来才发现,当年所藐视的、不值一提的东西才是最宝贵的,我们经常将无意义的东西误以为宝,只有在孤独中、在自己的选择里,体会出来的东西才是最真实的。&lt;/p&gt; +&lt;p&gt;本然的自我一定是不完美的,但确实独一无二的,人只有受到阻力时,才能触动自己,反思自己的生命是否真实。一个人一旦通过这种野性的方式触动自己,心里便会一片透亮,感觉特别有价值感、幸福感,而人一旦体会到这种感觉,就不肯放弃了。这种愉悦一定是在路上探索才能体会的,不经一番寒彻骨,怎得梅花扑鼻香。&lt;/p&gt; +&lt;p&gt;希望我们这新一代的人有生而为人的生命的连续性,在他的儿童、少年、青年、中年、老年各个时期,都能活得很真实,是一个由内向外生活的人,而不是由外向内、活在别人眼光里的人。&lt;/p&gt; +&lt;p&gt;书中也有梁老师&lt;strong&gt;关于人格、修养&lt;/strong&gt;的阐述,但我其实不太赞成梁永安老师在这本书中对于好人的定义,人格的好坏不能与认知是否清醒、人生价值是否实现相关联,单纯的、愚昧的、不思进取的人可能有一个发自内心对外在释放善意的人格,所幸的是对于道德在于人格的重要性,跟梁老师的看法还是一致的,就像康德所说,“ 有两种东西,我对它们的思考越是深沉和持久,它们在我心灵中唤起的惊奇和敬畏就会日新月异,不断增长,这就是我头上的星空和心中的道德定律。”&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;我们偶然来到这个世界,活着是为了什么?&lt;/strong&gt; 很多人是以获得为目的的,但是一个人究竟需要多少获得?很多人不珍惜自己,把自己放在虚荣、竞争等相对价值里,以为这样就可以令人羡慕,但最终又有怎样的价值呢?我们有可能正在恶的链条上发力,争取着虚名浮利,仅仅以周围的人为参照人群——我要过得比他们好,这其实是对自己价值非常大的贬低。&lt;/p&gt; +&lt;p&gt;真正有价值的是寻找、珍惜自我,懂得这个世界的自由,珍惜个人生命的自由,关注自己的价值和社会需要的价值,确认自己能够做些什么,最终做出一些跟别人不一样的事情,为社会增添一些亮光,而不是执着于千篇一律的东西。这时,我们才会有一种全新的生命观。&lt;/p&gt; +&lt;p&gt;最后,送给大家一句我很喜欢的话,这也是目前我对于生活的态度,“ 拥有可以接纳并允许一切发生的可能,从自我出发去分享表达,但不对反馈反响抱有限定的期待 ”。&lt;/p&gt; +]]>
@@ -253,54 +253,54 @@ <img src=https://imgoldjii.oss-cn-beijing.aliyuncs.com/sdsm.png width=600 /> </div> <p>Form <a href="https://weirdwonderfulai.art/resources/disco-diffusion-70-plus-artist-studies">Weird Wonderful Ai Art</a></p> - - - -

Form Weird Wonderful Ai Art

-
-

2023 年 9 月 23 日更新:Colab 官方禁用 Stable Diffusion WebUI,无法解决断网问题,本文方法作废。详情见 https://decrypt.co/197428/google-colab-stable-diffusion-web-ui-ban

-
-

🎨 Ai 绘画

-

Stable Diffusion 是 stability.ai 于 2022 年发布的深度学习文生图模型,可以在大多数配备有适度 GPU 的电脑硬件上运行。

-

Colaboratory 简称 Colab,是 Google Research 团队发布的一种托管式 Jupyter 笔记本服务,可以免费使用 GPU / TPU 计算资源。

-

这是一份保姆级的在 Colab 搭建 Stable Diffusion 服务的教程,按照以下步骤操作,即可浅尝 Ai 绘画的魅力。

-

分配资源

-

进入 Stable_Diffusion_WebUi_Altryne Colab 工程。

-

点击右上角的连接获取云主机资源并连接运行时。

-

1-image-20230222142539642

-

运行 1.0 单元格中的命令可以获主机信息。

-

1-image-20230222142936172

-

一般分配到的都是 Tesla T4 机型,如果人品欧分配到 V100,那可以好好的吹一波了。

-

配置模型

-

首先注册 Hugging Face 账号,并生成 WRITE 类型的 Access Tokens

-

1-image-20230222144043327

-

返回到 Colab 界面,展开 1.4 Connect to Google Drive 单元格,勾选 download_if_missing 并输入 Hugging Face 的 Accsee Token。

-

1-image-20230222144232989

-

设置密码

-

2.1 Optional - Set webUI settings and configs before running 中配置密码。

-

1-image-20230222144551905

-

运行服务

-

将所有单元块收起,依次运行所有单元块部署服务即可。

-

运行耗时

-
    -
  • Setup stage:5min
  • -
  • Run the Stable Diffusion webui:1s
  • -
  • Launch WebUI for stable diffusion:1min
  • -
-

1-image-20230222144857944

-

运行过程中会弹窗请求登录 Google Drive,正常登录并允许即可。

-

1-image-20230222145720360

-

运行完成点击 Public URL 进入 Stable Diffusion WebUI 界面。账号为 wubei,密码为上面自己配置的。

-

1-image-20230222150541478

-

然后就可以尽情的享用了。

-

输入 prompt,即可生成对应的图像,比如输入 “ an astronaut in the water ”。

-

1-image-20230222151132661

-

当然,你可以输入更多的 prompt 信息,来生成更加具体准确的图像。

-
-

(((masterpiece))),((best quality)), flat chest,((loli)),((one girl)),very long light white hair, beautiful detailed red eyes,aqua eyes,white robe, cat ears,(flower hairpin),sunlight, light smile,blue necklace,see-through,

-

杰作,最佳品质,贫乳,萝莉,1个女孩,很长的头发,淡白色头发,红色眼睛,浅绿色眼睛,白色长裙,猫耳,发夹,阳光下,淡淡的微笑,蓝色项链,透明

-
-

1-image-20230222154010936

]]>
+ <![CDATA[&lt;div align=&#34;center&#34;&gt; +&lt;img src=https://imgoldjii.oss-cn-beijing.aliyuncs.com/sdsm.png width=600 /&gt; +&lt;/div&gt; +&lt;p&gt;Form &lt;a href=&#34;https://weirdwonderfulai.art/resources/disco-diffusion-70-plus-artist-studies&#34;&gt;Weird Wonderful Ai Art&lt;/a&gt;&lt;/p&gt; +&lt;blockquote&gt; +&lt;p&gt;2023 年 9 月 23 日更新:Colab 官方禁用 Stable Diffusion WebUI,无法解决断网问题,本文方法作废。详情见 &lt;a href=&#34;https://decrypt.co/197428/google-colab-stable-diffusion-web-ui-ban&#34;&gt;https://decrypt.co/197428/google-colab-stable-diffusion-web-ui-ban&lt;/a&gt;&lt;/p&gt; +&lt;/blockquote&gt; +&lt;h2 id=&#34;-ai-绘画&#34;&gt;🎨 Ai 绘画&lt;/h2&gt; +&lt;p&gt;Stable Diffusion 是 stability.ai 于 2022 年发布的深度学习文生图模型,可以在大多数配备有适度 GPU 的电脑硬件上运行。&lt;/p&gt; +&lt;p&gt;Colaboratory 简称 Colab,是 Google Research 团队发布的一种托管式 Jupyter 笔记本服务,可以免费使用 GPU / TPU 计算资源。&lt;/p&gt; +&lt;p&gt;这是一份保姆级的在 Colab 搭建 Stable Diffusion 服务的教程,按照以下步骤操作,即可浅尝 Ai 绘画的魅力。&lt;/p&gt; +&lt;h2 id=&#34;分配资源&#34;&gt;分配资源&lt;/h2&gt; +&lt;p&gt;进入 &lt;a href=&#34;https://colab.research.google.com/github/altryne/sd-webui-colab/blob/main/Stable_Diffusion_WebUi_Altryne.ipynb&#34;&gt;Stable_Diffusion_WebUi_Altryne&lt;/a&gt; Colab 工程。&lt;/p&gt; +&lt;p&gt;点击右上角的&lt;strong&gt;连接&lt;/strong&gt;获取云主机资源并连接运行时。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/1-image-20230222142539642.png&#34; alt=&#34;1-image-20230222142539642&#34;&gt;&lt;/p&gt; +&lt;p&gt;运行 1.0 单元格中的命令可以获主机信息。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/1-image-20230222142936172.png&#34; alt=&#34;1-image-20230222142936172&#34;&gt;&lt;/p&gt; +&lt;p&gt;一般分配到的都是 Tesla T4 机型,如果人品欧分配到 V100,那可以好好的吹一波了。&lt;/p&gt; +&lt;h2 id=&#34;配置模型&#34;&gt;配置模型&lt;/h2&gt; +&lt;p&gt;首先注册 &lt;a href=&#34;https://huggingface.co/&#34;&gt;Hugging Face&lt;/a&gt; 账号,并生成 WRITE 类型的 &lt;a href=&#34;https://huggingface.co/settings/tokens&#34;&gt;Access Tokens&lt;/a&gt;。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/1-image-20230222144043327.png&#34; alt=&#34;1-image-20230222144043327&#34;&gt;&lt;/p&gt; +&lt;p&gt;返回到 Colab 界面,展开 &lt;em&gt;1.4 Connect to Google Drive&lt;/em&gt; 单元格,勾选 download_if_missing 并输入 Hugging Face 的 Accsee Token。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/1-image-20230222144232989.png&#34; alt=&#34;1-image-20230222144232989&#34;&gt;&lt;/p&gt; +&lt;h2 id=&#34;设置密码&#34;&gt;设置密码&lt;/h2&gt; +&lt;p&gt;在 &lt;em&gt;2.1 Optional - Set webUI settings and configs before running&lt;/em&gt; 中配置密码。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/1-image-20230222144551905.png&#34; alt=&#34;1-image-20230222144551905&#34;&gt;&lt;/p&gt; +&lt;h2 id=&#34;运行服务&#34;&gt;运行服务&lt;/h2&gt; +&lt;p&gt;将所有单元块收起,依次运行所有单元块部署服务即可。&lt;/p&gt; +&lt;p&gt;运行耗时&lt;/p&gt; +&lt;ul&gt; +&lt;li&gt;Setup stage:5min&lt;/li&gt; +&lt;li&gt;Run the Stable Diffusion webui:1s&lt;/li&gt; +&lt;li&gt;Launch WebUI for stable diffusion:1min&lt;/li&gt; +&lt;/ul&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/1-image-20230222144857944.png&#34; alt=&#34;1-image-20230222144857944&#34;&gt;&lt;/p&gt; +&lt;p&gt;运行过程中会弹窗请求登录 Google Drive,正常登录并允许即可。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/1-image-20230222145720360.png&#34; alt=&#34;1-image-20230222145720360&#34;&gt;&lt;/p&gt; +&lt;p&gt;运行完成点击 Public URL 进入 Stable Diffusion WebUI 界面。账号为 wubei,密码为上面自己配置的。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/1-image-20230222150541478.png&#34; alt=&#34;1-image-20230222150541478&#34;&gt;&lt;/p&gt; +&lt;p&gt;然后就可以尽情的享用了。&lt;/p&gt; +&lt;p&gt;输入 prompt,即可生成对应的图像,比如输入 “ an astronaut in the water ”。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/1-image-20230222151132661.png&#34; alt=&#34;1-image-20230222151132661&#34;&gt;&lt;/p&gt; +&lt;p&gt;当然,你可以输入更多的 prompt 信息,来生成更加具体准确的图像。&lt;/p&gt; +&lt;blockquote&gt; +&lt;p&gt;(((masterpiece))),((best quality)), flat chest,((loli)),((one girl)),very long light white hair, beautiful detailed red eyes,aqua eyes,white robe, cat ears,(flower hairpin),sunlight, light smile,blue necklace,see-through,&lt;/p&gt; +&lt;p&gt;杰作,最佳品质,贫乳,萝莉,1个女孩,很长的头发,淡白色头发,红色眼睛,浅绿色眼睛,白色长裙,猫耳,发夹,阳光下,淡淡的微笑,蓝色项链,透明&lt;/p&gt; +&lt;/blockquote&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/1-image-20230222154010936.png&#34; alt=&#34;1-image-20230222154010936&#34;&gt;&lt;/p&gt;]]>
@@ -331,185 +331,185 @@ git config --global --unset user.email </code></pre></td></tr></table> </div> </div> - 清除配置 -

查看 Git 本地配置

-
- -
-
1
-
-
git config --list
-
-
-

清除用户名和邮箱

-
- -
-
1
-2
-
-
git config --global --unset user.name
+      <![CDATA[&lt;h2 id=&#34;清除配置&#34;&gt;清除配置&lt;/h2&gt;
+&lt;p&gt;查看 Git 本地配置&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;git config --list
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;清除用户名和邮箱&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;git config --global --unset user.name
 git config --global --unset user.email
-
-
-

生成 ssh-key

-

使用 ssh-keygen 命令生成 ssh-key,并手动指定 id

-
- -
-
1
-
-
ssh-keygen -t rsa -f ~/.ssh/id_rsa_xx1@gmail.com -C "xx1@gmail.com"
-
-
-

生成成功会输出以下内容

-
- -
-
 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
-10
-11
-12
-13
-14
-15
-16
-17
-18
-19
-20
-
-
Generating public/private rsa key pair.
-Enter file in which to save the key (/Users/james/.ssh/id_rsa):
-Enter passphrase (empty for no passphrase):
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;h2 id=&#34;生成-ssh-key&#34;&gt;生成 ssh-key&lt;/h2&gt;
+&lt;p&gt;使用 &lt;code&gt;ssh-keygen&lt;/code&gt; 命令生成 ssh-key,并手动指定 id&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;ssh-keygen -t rsa -f ~/.ssh/id_rsa_xx1@gmail.com -C &lt;span class=&#34;s2&#34;&gt;&amp;#34;xx1@gmail.com&amp;#34;&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;生成成功会输出以下内容&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;Generating public/private rsa key pair.
+Enter file in which to save the key &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;/Users/james/.ssh/id_rsa&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;:
+Enter passphrase &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;empty &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; no passphrase&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;:
 Enter same passphrase again:
 Your identification has been saved in /Users/james/.ssh/id_rsa.
 Your public key has been saved in /Users/james/.ssh/id_rsa.pub.
 The key fingerprint is:
-SHA256:rwtxjGTJPoV9Mg8lFSf8D4X6jFexWVXKOMRaVyo+RO8 xx1@gmail.com
-The key's randomart image is:
-+---[RSA 3072]----+
-|        .o=o+. .*|
-|     . + o.=+++o.|
-|      * * .==o== |
-|     + + *ooo++  |
-|      = S .+o+E  |
-|       + .. +..  |
-|      .   ..     |
-|       . .       |
-|        o.       |
-+----[SHA256]-----+
-
-
-

然后继续生成你的其他 Git 账号对应的 ssh-key

-
- -
-
1
-
-
ssh-keygen -t rsa -f ~/.ssh/id_rsa_xx2@gmail.com -C "xx2@gmail.com"
-
-
-

信任 ssh-key

-

使用 ssh-add 命令将生成的 ssh-key 添加到 ssh-agent 信任列表

-
- -
-
1
-2
-
-
ssh-add ~/.ssh/id_rsa_xx1@gmail.com
+SHA256:rwtxjGTJPoV9Mg8lFSf8D4X6jFexWVXKOMRaVyo+RO8 xx1@gmail.com
+The key&lt;span class=&#34;err&#34;&gt;&amp;#39;&lt;/span&gt;s randomart image is:
++---&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;RSA 3072&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;----+
+&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;        .o&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;o+. .*&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
+&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;     . + o.&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;+++o.&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
+&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;      * * .&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
+&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;     + + *ooo++  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
+&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;      &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; S .+o+E  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
+&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;       + .. +..  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
+&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;      .   ..     &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
+&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;       . .       &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
+&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;        o.       &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
++----&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;SHA256&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;-----+
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;然后继续生成你的其他 Git 账号对应的 ssh-key&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;ssh-keygen -t rsa -f ~/.ssh/id_rsa_xx2@gmail.com -C &lt;span class=&#34;s2&#34;&gt;&amp;#34;xx2@gmail.com&amp;#34;&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;h2 id=&#34;信任-ssh-key&#34;&gt;信任 ssh-key&lt;/h2&gt;
+&lt;p&gt;使用 &lt;code&gt;ssh-add&lt;/code&gt; 命令将生成的 ssh-key 添加到 ssh-agent 信任列表&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;ssh-add ~/.ssh/id_rsa_xx1@gmail.com
 ssh-add ~/.ssh/id_rsa_xx2@gmail.com
-
-
-

如果遇到 Could not open a connection to your authentication agent. ,输入

-
- -
-
1
-
-
ssh-agent bash
-
-
-

然后重复执行 ssh-add 即可解决

-

配置公钥

-

复制对应公钥,配置到对应 Git 网站中(GitHub / GitLab)

-
- -
-
1
-2
-
-
pbcopy < ~/.ssh/id_rsa_xx1@gmail.com.pub
-pbcopy < ~/.ssh/id_rsa_xx2@gmail.com.pub
-
-
-

这步具体的操作如果有不清楚的可以参考 Adding a new SSH key to your GitHub account

-

配置 config

-

进入 ssh 目录

-
- -
-
1
-
-
open ~/.ssh/
-
-
-

按照以下规则编辑 config 文件,没有则创建

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
说明
Host主机自己起
Hostname主机名Git 公有地址,比如 gitub.com / gittee.com
IdentityFile身份文件rsa 文件路径
User用户自己起,一般邮箱就好
-

编辑完 config 内容如下

-
- -
-
1
-2
-3
-4
-5
-6
-7
-8
-9
-
-
Host github1.com
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;如果遇到 &lt;code&gt;Could not open a connection to your authentication agent.&lt;/code&gt; ,输入&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;ssh-agent bash
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;然后重复执行 &lt;code&gt;ssh-add&lt;/code&gt; 即可解决&lt;/p&gt;
+&lt;h2 id=&#34;配置公钥&#34;&gt;配置公钥&lt;/h2&gt;
+&lt;p&gt;复制对应公钥,配置到对应 Git 网站中(GitHub / GitLab)&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;pbcopy &amp;lt; ~/.ssh/id_rsa_xx1@gmail.com.pub
+pbcopy &amp;lt; ~/.ssh/id_rsa_xx2@gmail.com.pub
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;这步具体的操作如果有不清楚的可以参考 &lt;a href=&#34;https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account&#34;&gt;Adding a new SSH key to your GitHub account&lt;/a&gt;&lt;/p&gt;
+&lt;h2 id=&#34;配置-config&#34;&gt;配置 config&lt;/h2&gt;
+&lt;p&gt;进入 ssh 目录&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;open ~/.ssh/
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;按照以下规则编辑 config 文件,没有则创建&lt;/p&gt;
+&lt;table&gt;
+&lt;thead&gt;
+&lt;tr&gt;
+&lt;th&gt;键&lt;/th&gt;
+&lt;th&gt;值&lt;/th&gt;
+&lt;th&gt;说明&lt;/th&gt;
+&lt;/tr&gt;
+&lt;/thead&gt;
+&lt;tbody&gt;
+&lt;tr&gt;
+&lt;td&gt;Host&lt;/td&gt;
+&lt;td&gt;主机&lt;/td&gt;
+&lt;td&gt;自己起&lt;/td&gt;
+&lt;/tr&gt;
+&lt;tr&gt;
+&lt;td&gt;Hostname&lt;/td&gt;
+&lt;td&gt;主机名&lt;/td&gt;
+&lt;td&gt;Git 公有地址,比如 gitub.com / gittee.com&lt;/td&gt;
+&lt;/tr&gt;
+&lt;tr&gt;
+&lt;td&gt;IdentityFile&lt;/td&gt;
+&lt;td&gt;身份文件&lt;/td&gt;
+&lt;td&gt;rsa 文件路径&lt;/td&gt;
+&lt;/tr&gt;
+&lt;tr&gt;
+&lt;td&gt;User&lt;/td&gt;
+&lt;td&gt;用户&lt;/td&gt;
+&lt;td&gt;自己起,一般邮箱就好&lt;/td&gt;
+&lt;/tr&gt;
+&lt;/tbody&gt;
+&lt;/table&gt;
+&lt;p&gt;编辑完 config 内容如下&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;9
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;Host github1.com
 Hostname github.com
 IdentityFile ~/.ssh/id_rsa_xx1@gmail.com
 User xx1@gmail.com
@@ -518,30 +518,30 @@ Host github2.com
 Hostname github.com
 IdentityFile ~/.ssh/id_rsa_xx2@gmail.com
 User xx2@gmail.com
-
-
-

测试链接

-

测试 Git 账号是否连接成功,git@ 之后是 config 文件中配置的 Host

-
- -
-
1
-2
-
-
ssh -T git@github1.com
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;h2 id=&#34;测试链接&#34;&gt;测试链接&lt;/h2&gt;
+&lt;p&gt;测试 Git 账号是否连接成功,&lt;code&gt;git@&lt;/code&gt; 之后是 config 文件中配置的 Host&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;ssh -T git@github1.com
 ssh -T git@github2.com
-
-
-

连接成功会有以下输出

-
- -
-
1
-
-
Hi xxx! You've successfully authenticated, but GitHub does not provide shell access.
-
-
-
]]>
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; +&lt;/div&gt; +&lt;/div&gt;&lt;p&gt;连接成功会有以下输出&lt;/p&gt; +&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt; +&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt; +&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1 +&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt; +&lt;td class=&#34;lntd&#34;&gt; +&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;Hi xxx! You&lt;span class=&#34;err&#34;&gt;&amp;#39;&lt;/span&gt;ve successfully authenticated, but GitHub does not provide shell access. +&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; +&lt;/div&gt; +&lt;/div&gt;]]>
@@ -551,53 +551,53 @@ ssh -T git@github2.com https://folay.top/post/china_economy/ 微观 在我国,政府不但影响“蛋糕”的分配,也参与“蛋糕”的生产,所以我们不可能脱离政府谈经济。 地方政府的权利与事务 要理解政府治理和运作的模式, - 微观 -

在我国,政府不但影响“蛋糕”的分配,也参与“蛋糕”的生产,所以我们不可能脱离政府谈经济。

-

地方政府的权利与事务

-

要理解政府治理和运作的模式,必须首先了解权力和资源在政府体系中的分布规则。这一分布取决于两个重要体制特点:一是央地关系,二是条块分割。央地关系是指整体上中央与地方之间权力要平衡;条块分割是指地方部门要同时接受上级垂直部门和横向地方政府的领导,受到双重制约。

-

基于这两个特点,地方政府的权力划分可以从三个视角考察:从外部性的视角,若政府提供的公共服务只影响本地,就可以由本地全权处理,若存在外部性,则需要上级协调;从信息的视角,有信息优势的一方实际权威也会更大,因此上级往往也会花费大量的精力获取下级的信息;从激励相容的视角,与本地发展目标无关或相反的事项更倾向于垂直领导,与本地发展目标相符或本地能受益的事项则倾向于交给本地政府处理。

-

根据这些事权划分原则,地方政府在招商引资、发展经济的过程中享有非常广泛的权力,因此可以深度参与资源的生产与分配过程

-

财务与政府行为

-

要发展经济,地方政府除了有事权还不够,更重要的是财权。1985-1993年,推行财政包干,导致中央财政收入占比逐年下降,于是1994年进行分税制改革,增加了中央的收入,却大大降低了地方的财政资源。

-

分税制并没有改变地方政府以经济建设为中心的任务,却减少了其手头可支配的财政来源,地方不得不另谋出路,寻找资金来源,于是轰轰烈烈的土地财政就此登场。土地财政简单来讲就是指地方政府依靠出让土地使用权的收入来维持地方财政支出。

-

1998年,发生了两件改变房地产市场的大事:第一,单位停止福利分房,老百姓从此需要自己买房子了,房地产时代自此拉开大幕;第二,修订后的《中华人民共和国土地管理法》开始实施,简言之就是地方政府拥有卖地的决定权,而卖地的收入也归地方所有。地方政府就可以限制商住用地供给,从不断攀升的地价中赚取土地垄断收益。

-

一些地区政府实行土地财政。一方面补贴工业用地,招商引资,一方面限制商住用地,房价带动地价。但对于无法依靠土地财政的地区,则出现了财政困难,地区间不平等,需要依靠中央转移支付,并继续进行财政改革。如农村税费改革,乡财县管、省直管县等等。

-

政府投融资与债务

-

土地真正的力量还不在土地财政,而在以土地为抵押而撬动的银行信贷和其他各路资金,土地财政一旦嫁接了资本市场,加上了杠杆,就成了土地金融,能像滚雪球般越滚越大,推动经济飞速扩张。

-

政府成立地方政府融资平台,即“城投公司”,以未来的土地收益为抵押撬动大量银行贷款,就可以进行投资以推动城市化和工业化。政府投资主要分两个方向,一是投资基础建设,二是投资工业。投资基础建设就是城投公司开发后再交给政府招商引资,或企业从开发到引资全负责,政府付费使用,这就是“政府与社会资本合作”模式(PPP)。

-

但是地方政府通过土地金融推动经济发展的模式仍然存在一些问题。从微观层面,土地金融会增加地方政府债务风险。地方政府债务除了账面的显性负债,更多的是融资平台公司的“隐性负债”。经济向好时可以以土地增值收益作为还款来源,一旦经济遇冷,低价下跌,就可能出现严重的债务问题。

-

目前改革的主要措施有用政府公债置换城投公司债务,将城投公司转型为普通国企,避免资金继续流入,约束官员的投资冲动。但是从根本上说,现行体制下,地方官员和普通政府工作人员都拥有非常强的激励去发展经济,这一方面导致投资过度,一方面出现了“官商勾结共同发财式”的系统性腐败。因此深层次的改革应当简政放权,从生产投资型政府向服务型政府转变。

-

工业化中的政府角色

-

我国的经济改革脱胎于计划经济,政府手中掌握大量对产业发展至关重要的资源,如土地、银行、大学和科研机构等,所以必然会以各种方式深度参与工业化进程。

-

上面描述的政府投资的第二个方向就是投资工业,由政府对特定企业进行支持和补贴。此外,政府还会设置产业引导基金,成立投资公司或交给市场化的基金管理人运作,作为“母基金”投资其他基金,再通过后者投资未上市公司的股权,从而将更多社会资本引导至战略新兴产业。

-

书中以京东方、光伏发展为例讲解了具体的地方政府投资工业的过程,在此不做赘述。

-

宏观

-

上面描述了土地金融模式微观层面上的影响,下面描述宏观影响

-

城市化与不平衡

-

第一,城市化过程中“重土地、轻人”,从而推高房价,增加居民债务负担,加剧收入差距和贫富差距。

-

房价升高在中长期看来是因为大城市的建设用地指标被严格管理,跟不上人口流入的速度,导致住房供不应求。房价越高,居民债务负担就越重。

-

收入差距则是由于城市相关公共服务供给不足,劳动力无法自由流动,低技能人员难以在城市立足。在经济快速增长的过程中,低收入群体对于贫富差距的敏感度没有那么高,但一旦经济放缓,社会对不平等的容忍度将会降低。

-

因此改革办法是将重心从土地转移到人,既要让建设用地指标流转起来,打破城市政府对城市住宅用地的垄断,又要改革户籍制度,增加低收入群体的流动性和选择权。

-

债务与风险

-

第二,招商引资过程中“重规模,重扩张”,加重企业债务负担,以及经济整体的债务和金融风险。

-

债务的成因在于金融危机后我国金融管制的放松,银行更愿意把钱借出去。

-

债务人企业的角度,主要是地方政府融资平台企业、国有企业、房地产企业债务负担较重。它们为了还贷抛售资产,会造成资产价格下跌,而资产价格下跌又会使银行坏账上升,不愿意继续贷款,使企业资金链断裂,从而带来经济衰退。

-

债权人银行的角度,一是规模大,杠杆率高;二是银行负债与资产期限的不匹配会带来流动性风险;三是银行信贷大多以房地产或土地为抵押,因此经济衰退时,银行反而不愿放贷,加速经济下行;四是银行风险会传导至其他金融部门,形成系统风险。银行为了逃避监管设立影子银行,资本在金融机构流转,一旦泡沫破裂,将影响整个金融行业。

-

要解决债务问题,可以分为两个部分,一是偿还已有债务,二是遏制新增债务

-

偿还债务要么变卖资产,要么压缩支出,要么增发货币。后者有三种方法:直接增发、量化宽松、债务货币化。

-

要遏制新增债务,除了严控房价上涨、限制土地金融等金融改革,关键是要找到债务增长的根源。企业为什么总是向银行贷款?根据“谁做投资决策,谁承担投资风险”的原则,我国的投资主要有政府和国企主导,因此风险也是由政府及其控制的金融机构,即银行承担。因为银行的风险归根结底是政府的风险。因此限制债务增长的根本措施是资本市场改革,将权力下放给市场,拓宽直接融资渠道,让企业通过股权、债券筹资。

-

国内国际失衡

-

第三,发展战略“重投资、重生产、轻消费”,导致经济结构不平衡。

-

对于内部,结构失衡最突出的特征是消费不足,这一方面是因为计划生育、民生支出不足、房价上涨等因素造成的储蓄偏高,一方面也是因为居民收入份额较低

-

在经济发展初期,大量投入资本可以有效实现工业化,推动经济增长,但当经济发展到一定阶段,这种发展模式会造成产能过剩、债务风险(投资流入房地产,推高房价)、贫富差距过大、外部失衡(出口常年大于进口)等一系列问题。

-

但是,中美之间的贸易冲突,并非因为中国的出口对美国就业的冲击,而是制造业崛起对美国技术的冲击,再加上美国政治保守主义的兴起。这势必要求我国构建以国内大循环为主体的双循环模式,壮大国内市场,实现“市场—研发—迭代—更大市场”的良性循环。

-

这一转型的关键在于提高居民收入与消费。这应当从三个方面入手,一是继续推进城市化,让服务业进一步发展;二是加大民生支出,降低生产性支出;三是发展直接融资渠道,扩宽居民财产性收入。

-

政府与经济发展

-

政府过去发展经济的核心在于引入竞争机制,由中央协调,地方政府竞争。竞争的评价标准就是经济建设。

-

这种“官场+市场”的体制优势在于设计了一晰有效的升迁标准,从而给官员提供了极强的激励发展经济。但也会产生地方保护主义、腐败等问题。正如计划经济时代政府干预的特点延续到市场化改革,这一体制也会留下重视经济、忽视民生的路径依赖。

-

在市场机制尚不完善的情况下,政府可以作为其补充动员和调配资源,为完善和建设市场经济争取时间。但当市场机制已经相对成熟,政府也要适时改变自己的角色定位,降低政府投资支出,加大民生支出,从生产型政府转向服务型政府

-

这一转变也契合发展经济学的一般视角:对于发展中国家,提高生产率的关键在于学习已知的技术和管理模式,当生产率提升到一定水平后,又要由组织学习模式转向探索创新模式。而无论是怎样的发展模式,不同的国家、不同的地区都会根据具体的政治社会现实而有所不同。

-]]>
+ <![CDATA[&lt;h1 id=&#34;微观&#34;&gt;微观&lt;/h1&gt; +&lt;p&gt;在我国,政府不但影响“蛋糕”的分配,也参与“蛋糕”的生产,所以我们不可能脱离政府谈经济。&lt;/p&gt; +&lt;h2 id=&#34;地方政府的权利与事务&#34;&gt;地方政府的权利与事务&lt;/h2&gt; +&lt;p&gt;要理解政府治理和运作的模式,必须首先了解权力和资源在政府体系中的分布规则。这一分布取决于两个重要体制特点:一是&lt;strong&gt;央地关系&lt;/strong&gt;,二是&lt;strong&gt;条块分割&lt;/strong&gt;。央地关系是指整体上中央与地方之间权力要平衡;条块分割是指地方部门要同时接受上级垂直部门和横向地方政府的领导,受到双重制约。&lt;/p&gt; +&lt;p&gt;基于这两个特点,地方政府的权力划分可以从三个视角考察:从&lt;strong&gt;外部性&lt;/strong&gt;的视角,若政府提供的公共服务只影响本地,就可以由本地全权处理,若存在外部性,则需要上级协调;从&lt;strong&gt;信息&lt;/strong&gt;的视角,有信息优势的一方实际权威也会更大,因此上级往往也会花费大量的精力获取下级的信息;从&lt;strong&gt;激励相容&lt;/strong&gt;的视角,与本地发展目标无关或相反的事项更倾向于垂直领导,与本地发展目标相符或本地能受益的事项则倾向于交给本地政府处理。&lt;/p&gt; +&lt;p&gt;根据这些事权划分原则,地方政府在招商引资、发展经济的过程中享有非常广泛的权力,因此可以&lt;strong&gt;深度参与资源的生产与分配过程&lt;/strong&gt;。&lt;/p&gt; +&lt;h2 id=&#34;财务与政府行为&#34;&gt;财务与政府行为&lt;/h2&gt; +&lt;p&gt;要发展经济,地方政府除了有事权还不够,更重要的是财权。1985-1993年,推行财政包干,导致中央财政收入占比逐年下降,于是1994年进行&lt;strong&gt;分税制改革&lt;/strong&gt;,增加了中央的收入,却大大降低了地方的财政资源。&lt;/p&gt; +&lt;p&gt;分税制并没有改变地方政府以经济建设为中心的任务,却减少了其手头可支配的财政来源,地方不得不另谋出路,寻找资金来源,于是轰轰烈烈的&lt;strong&gt;土地财政&lt;/strong&gt;就此登场。土地财政简单来讲就是指地方政府依靠出让土地使用权的收入来维持地方财政支出。&lt;/p&gt; +&lt;p&gt;1998年,发生了两件改变房地产市场的大事:第一,单位停止福利分房,老百姓从此需要自己买房子了,房地产时代自此拉开大幕;第二,修订后的《中华人民共和国土地管理法》开始实施,简言之就是地方政府拥有卖地的决定权,而卖地的收入也归地方所有。地方政府就可以限制商住用地供给,从不断攀升的地价中赚取土地垄断收益。&lt;/p&gt; +&lt;p&gt;一些地区政府实行土地财政。一方面补贴工业用地,招商引资,一方面限制商住用地,房价带动地价。但对于无法依靠土地财政的地区,则出现了财政困难,地区间不平等,需要依靠中央转移支付,并继续进行财政改革。如农村税费改革,乡财县管、省直管县等等。&lt;/p&gt; +&lt;h2 id=&#34;政府投融资与债务&#34;&gt;政府投融资与债务&lt;/h2&gt; +&lt;p&gt;土地真正的力量还不在土地财政,而在以土地为抵押而撬动的银行信贷和其他各路资金,土地财政一旦嫁接了资本市场,加上了杠杆,就成了&lt;strong&gt;土地金融&lt;/strong&gt;,能像滚雪球般越滚越大,推动经济飞速扩张。&lt;/p&gt; +&lt;p&gt;政府成立地方政府融资平台,即“城投公司”,以未来的土地收益为抵押撬动大量银行贷款,就可以进行投资以推动城市化和工业化。政府投资主要分两个方向,一是&lt;strong&gt;投资基础建设&lt;/strong&gt;,二是&lt;strong&gt;投资工业&lt;/strong&gt;。投资基础建设就是城投公司开发后再交给政府招商引资,或企业从开发到引资全负责,政府付费使用,这就是“政府与社会资本合作”模式(PPP)。&lt;/p&gt; +&lt;p&gt;但是地方政府通过土地金融推动经济发展的模式仍然存在一些问题。从&lt;strong&gt;微观层面&lt;/strong&gt;,土地金融会增加&lt;strong&gt;地方政府债务风险&lt;/strong&gt;。地方政府债务除了账面的显性负债,更多的是融资平台公司的“隐性负债”。经济向好时可以以土地增值收益作为还款来源,一旦经济遇冷,低价下跌,就可能出现严重的债务问题。&lt;/p&gt; +&lt;p&gt;目前改革的主要措施有用政府公债置换城投公司债务,将城投公司转型为普通国企,避免资金继续流入,约束官员的投资冲动。但是从根本上说,现行体制下,地方官员和普通政府工作人员都拥有非常强的激励去发展经济,这一方面导致投资过度,一方面出现了“官商勾结共同发财式”的系统性腐败。因此深层次的改革应当简政放权,从生产投资型政府向服务型政府转变。&lt;/p&gt; +&lt;h2 id=&#34;工业化中的政府角色&#34;&gt;工业化中的政府角色&lt;/h2&gt; +&lt;p&gt;我国的经济改革脱胎于计划经济,政府手中掌握大量对产业发展至关重要的资源,如土地、银行、大学和科研机构等,所以必然会以各种方式深度参与工业化进程。&lt;/p&gt; +&lt;p&gt;上面描述的政府投资的第二个方向就是&lt;strong&gt;投资工业&lt;/strong&gt;,由政府对特定企业进行支持和补贴。此外,政府还会设置&lt;strong&gt;产业引导基金&lt;/strong&gt;,成立投资公司或交给市场化的基金管理人运作,作为“母基金”投资其他基金,再通过后者投资未上市公司的股权,从而将更多社会资本引导至战略新兴产业。&lt;/p&gt; +&lt;p&gt;书中以京东方、光伏发展为例讲解了具体的地方政府投资工业的过程,在此不做赘述。&lt;/p&gt; +&lt;h1 id=&#34;宏观&#34;&gt;宏观&lt;/h1&gt; +&lt;p&gt;上面描述了土地金融模式微观层面上的影响,下面描述&lt;strong&gt;宏观影响&lt;/strong&gt;。&lt;/p&gt; +&lt;h2 id=&#34;城市化与不平衡&#34;&gt;城市化与不平衡&lt;/h2&gt; +&lt;p&gt;第一,城市化过程中“&lt;strong&gt;重土地、轻人&lt;/strong&gt;”,从而推高房价,增加居民债务负担,加剧收入差距和贫富差距。&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;房价升高&lt;/strong&gt;在中长期看来是因为大城市的建设用地指标被严格管理,跟不上人口流入的速度,导致住房供不应求。房价越高,居民债务负担就越重。&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;收入差距&lt;/strong&gt;则是由于城市相关公共服务供给不足,劳动力无法自由流动,低技能人员难以在城市立足。在经济快速增长的过程中,低收入群体对于贫富差距的敏感度没有那么高,但一旦经济放缓,社会对不平等的容忍度将会降低。&lt;/p&gt; +&lt;p&gt;因此&lt;strong&gt;改革办法&lt;/strong&gt;是将重心从土地转移到人,既要让建设用地指标流转起来,打破城市政府对城市住宅用地的垄断,又要改革户籍制度,增加低收入群体的流动性和选择权。&lt;/p&gt; +&lt;h2 id=&#34;债务与风险&#34;&gt;债务与风险&lt;/h2&gt; +&lt;p&gt;第二,招商引资过程中“&lt;strong&gt;重规模,重扩张&lt;/strong&gt;”,加重企业债务负担,以及经济整体的债务和金融风险。&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;债务的成因&lt;/strong&gt;在于金融危机后我国金融管制的放松,银行更愿意把钱借出去。&lt;/p&gt; +&lt;p&gt;从&lt;strong&gt;债务人企业&lt;/strong&gt;的角度,主要是地方政府融资平台企业、国有企业、房地产企业债务负担较重。它们为了还贷抛售资产,会造成资产价格下跌,而资产价格下跌又会使银行坏账上升,不愿意继续贷款,使企业资金链断裂,从而带来经济衰退。&lt;/p&gt; +&lt;p&gt;从&lt;strong&gt;债权人银行&lt;/strong&gt;的角度,一是规模大,杠杆率高;二是银行负债与资产期限的不匹配会带来流动性风险;三是银行信贷大多以房地产或土地为抵押,因此经济衰退时,银行反而不愿放贷,加速经济下行;四是银行风险会传导至其他金融部门,形成系统风险。银行为了逃避监管设立影子银行,资本在金融机构流转,一旦泡沫破裂,将影响整个金融行业。&lt;/p&gt; +&lt;p&gt;要解决债务问题,可以分为两个部分,一是&lt;strong&gt;偿还已有债务&lt;/strong&gt;,二是&lt;strong&gt;遏制新增债务&lt;/strong&gt;。&lt;/p&gt; +&lt;p&gt;偿还债务要么变卖资产,要么压缩支出,要么增发货币。后者有三种方法:直接增发、量化宽松、债务货币化。&lt;/p&gt; +&lt;p&gt;要遏制新增债务,除了严控房价上涨、限制土地金融等金融改革,关键是要找到债务增长的根源。企业为什么总是向银行贷款?根据“谁做投资决策,谁承担投资风险”的原则,&lt;strong&gt;我国的投资主要有政府和国企主导,因此风险也是由政府及其控制的金融机构,即银行承担&lt;/strong&gt;。因为银行的风险归根结底是政府的风险。因此限制债务增长的根本措施是资本市场改革,将权力下放给市场,拓宽直接融资渠道,让企业通过股权、债券筹资。&lt;/p&gt; +&lt;h2 id=&#34;国内国际失衡&#34;&gt;国内国际失衡&lt;/h2&gt; +&lt;p&gt;第三,发展战略“&lt;strong&gt;重投资、重生产、轻消费&lt;/strong&gt;”,导致经济结构不平衡。&lt;/p&gt; +&lt;p&gt;对于内部,结构失衡最突出的特征是&lt;strong&gt;消费不足&lt;/strong&gt;,这一方面是因为计划生育、民生支出不足、房价上涨等因素造成的&lt;strong&gt;储蓄偏高&lt;/strong&gt;,一方面也是因为&lt;strong&gt;居民收入份额较低&lt;/strong&gt;。&lt;/p&gt; +&lt;p&gt;在经济发展初期,大量投入资本可以有效实现工业化,推动经济增长,但当经济发展到一定阶段,这种发展模式会造成产能过剩、债务风险(投资流入房地产,推高房价)、贫富差距过大、外部失衡(出口常年大于进口)等一系列问题。&lt;/p&gt; +&lt;p&gt;但是,中美之间的贸易冲突,并非因为中国的出口对美国就业的冲击,而是制造业崛起对美国技术的冲击,再加上美国政治保守主义的兴起。这势必要求我国构建以国内大循环为主体的&lt;strong&gt;双循环模式&lt;/strong&gt;,壮大国内市场,实现“市场—研发—迭代—更大市场”的良性循环。&lt;/p&gt; +&lt;p&gt;这一转型的关键在于&lt;strong&gt;提高居民收入与消费&lt;/strong&gt;。这应当从三个方面入手,一是继续推进城市化,让服务业进一步发展;二是加大民生支出,降低生产性支出;三是发展直接融资渠道,扩宽居民财产性收入。&lt;/p&gt; +&lt;h2 id=&#34;政府与经济发展&#34;&gt;政府与经济发展&lt;/h2&gt; +&lt;p&gt;政府过去发展经济的核心在于引入竞争机制,&lt;strong&gt;由中央协调,地方政府竞争&lt;/strong&gt;。竞争的评价标准就是经济建设。&lt;/p&gt; +&lt;p&gt;这种“&lt;strong&gt;官场+市场&lt;/strong&gt;”的体制优势在于设计了一晰有效的升迁标准,从而给官员提供了极强的激励发展经济。但也会产生地方保护主义、腐败等问题。正如计划经济时代政府干预的特点延续到市场化改革,这一体制也会留下重视经济、忽视民生的路径依赖。&lt;/p&gt; +&lt;p&gt;在市场机制尚不完善的情况下,政府可以作为其补充动员和调配资源,为完善和建设市场经济争取时间。但当市场机制已经相对成熟,政府也要适时改变自己的角色定位,降低政府投资支出,加大民生支出,从&lt;strong&gt;生产型政府转向服务型政府&lt;/strong&gt;。&lt;/p&gt; +&lt;p&gt;这一转变也契合发展经济学的一般视角:对于发展中国家,提高生产率的关键在于学习已知的技术和管理模式,当生产率提升到一定水平后,又要由组织学习模式转向探索创新模式。而无论是怎样的发展模式,不同的国家、不同的地区都会根据具体的政治社会现实而有所不同。&lt;/p&gt; +]]>
@@ -614,64 +614,64 @@ ssh -T git@github2.com <li>修改定位</li> <li>开启商户特权</li> </ol> - 商户特权 -

该教程适用于普通家用路由器网络以及 iOS 系统的手机,操作可以分为以下几步:

-
    -
  1. 获取商户 WiFi 名称
  2. -
  3. 修改连接 WiFi 名称
  4. -
  5. 修改定位
  6. -
  7. 开启商户特权
  8. -
-
- -
-

获取商户 WiFi 名称

-

通过 “ 王者人生 ” 微信小程序获取商户 WiFi 名称,具体步骤如下:

-
    -
  1. 微信搜索进入 “ 王者人生 ” 小程序
  2. -
  3. 选择到店游戏特权
  4. -
  5. 在附近特权合作商户列表中选择目标商户
  6. -
  7. 点击 “ 切换到店 WiFi ” 按钮
  8. -
  9. 记录 WiFi 名称
  10. -
-

操作视频如下:

-
- -
-

⚠️ 值得注意的是,请选择与手机 IP 地址所在城市的商户,另外最好选择 WiFi 很少变化的商户,如电影院、KFC 等。

-

修改连接 WiFi 名称

-

在路由器管理后台修改当前 WiFi 名称为第一步获取的商户 WiFi 名称即可,常见路由器管理后台地址为:192.168.1.1

-

⚠️ 值得注意的是,如果你是合租房等与他人共享 WiFi 的情况,可以通过 “ 朋友专属网络 ” 功能避免修改 WiFi 名称对他人的网络状态造成影响。现在大多路由器的管理后台都提供了开辟朋友专属子网络的功能,修改该网路的名称即可在不影响他人使用的情况下完成该步骤。

-

修改定位

-

最后一步,使用爱思助手修改手机定位。

-
    -
  1. 电脑安装爱思助手
  2. -
  3. 手机连接电脑并完成爱思助手初始化插件的安装
  4. -
  5. 使用工具箱中的虚拟定位功能
  6. -
  7. 修改手机定位为第一步获取商户的地址
  8. -
-
- -
-

开启商户特权

-

完成以上步骤,即可在王者荣耀中开启商家特权了,开启方法很简单:

-
    -
  1. 设置
  2. -
  3. 功能/隐私
  4. -
  5. 商户特权管理
  6. -
-
- -
-

注意事项

-
    -
  • 商户特权为官方活动,并不会导致账号封禁。
  • -
  • 选定一个商户 WiFi 后需无需更换,一直使用该 WiFi 即可。
  • -
  • 每次使用爱思助手修改定位都是长时间有效的,即使是短时间的重启手机也不会失效,如果想恢复定位,可以使用爱思助手恢复。
  • -
  • 请勿勾选 “ 每次登陆自动定位并开启特权 ”。
  • -
]]>
+ <![CDATA[&lt;h2 id=&#34;商户特权&#34;&gt;商户特权&lt;/h2&gt; +&lt;p&gt;该教程适用于普通家用路由器网络以及 iOS 系统的手机,操作可以分为以下几步:&lt;/p&gt; +&lt;ol&gt; +&lt;li&gt;获取商户 WiFi 名称&lt;/li&gt; +&lt;li&gt;修改连接 WiFi 名称&lt;/li&gt; +&lt;li&gt;修改定位&lt;/li&gt; +&lt;li&gt;开启商户特权&lt;/li&gt; +&lt;/ol&gt; +&lt;div align=&#34;center&#34;&gt; +&lt;img src=https://imgoldjii.oss-cn-beijing.aliyuncs.com/4431666347115_.pic.jpg width=600/&gt; +&lt;/div&gt; +&lt;h2 id=&#34;获取商户-wifi-名称&#34;&gt;获取商户 WiFi 名称&lt;/h2&gt; +&lt;p&gt;通过 “ 王者人生 ” 微信小程序获取商户 WiFi 名称,具体步骤如下:&lt;/p&gt; +&lt;ol&gt; +&lt;li&gt;微信搜索进入 “ 王者人生 ” 小程序&lt;/li&gt; +&lt;li&gt;选择到店游戏特权&lt;/li&gt; +&lt;li&gt;在附近特权合作商户列表中选择目标商户&lt;/li&gt; +&lt;li&gt;点击 “ 切换到店 WiFi ” 按钮&lt;/li&gt; +&lt;li&gt;记录 WiFi 名称&lt;/li&gt; +&lt;/ol&gt; +&lt;p&gt;操作视频如下:&lt;/p&gt; +&lt;div align=&#34;center&#34;&gt; +&lt;video height=&#34;400&#34; controls&gt; + &lt;source src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/441_1666345189.mp4&#34; type=&#34;video/mp4&#34;&gt; +&lt;/video&gt; +&lt;/div&gt; +&lt;p&gt;⚠️ 值得注意的是,请选择与手机 IP 地址所在城市的商户,另外最好选择 WiFi 很少变化的商户,如电影院、KFC 等。&lt;/p&gt; +&lt;h2 id=&#34;修改连接-wifi-名称&#34;&gt;修改连接 WiFi 名称&lt;/h2&gt; +&lt;p&gt;在路由器管理后台修改当前 WiFi 名称为第一步获取的商户 WiFi 名称即可,常见路由器管理后台地址为:&lt;a href=&#34;192.168.1.1&#34;&gt;192.168.1.1&lt;/a&gt;。&lt;/p&gt; +&lt;p&gt;⚠️ 值得注意的是,如果你是合租房等与他人共享 WiFi 的情况,可以通过 “ 朋友专属网络 ” 功能避免修改 WiFi 名称对他人的网络状态造成影响。现在大多路由器的管理后台都提供了开辟朋友专属子网络的功能,修改该网路的名称即可在不影响他人使用的情况下完成该步骤。&lt;/p&gt; +&lt;h2 id=&#34;修改定位&#34;&gt;修改定位&lt;/h2&gt; +&lt;p&gt;最后一步,使用爱思助手修改手机定位。&lt;/p&gt; +&lt;ol&gt; +&lt;li&gt;电脑安装爱思助手&lt;/li&gt; +&lt;li&gt;手机连接电脑并完成爱思助手初始化插件的安装&lt;/li&gt; +&lt;li&gt;使用工具箱中的虚拟定位功能&lt;/li&gt; +&lt;li&gt;修改手机定位为第一步获取商户的地址&lt;/li&gt; +&lt;/ol&gt; +&lt;div align=&#34;center&#34;&gt; +&lt;img src=https://imgoldjii.oss-cn-beijing.aliyuncs.com/iShot2022-10-21-123213.png width=600/&gt; +&lt;/div&gt; +&lt;h2 id=&#34;开启商户特权&#34;&gt;开启商户特权&lt;/h2&gt; +&lt;p&gt;完成以上步骤,即可在王者荣耀中开启商家特权了,开启方法很简单:&lt;/p&gt; +&lt;ol&gt; +&lt;li&gt;设置&lt;/li&gt; +&lt;li&gt;功能/隐私&lt;/li&gt; +&lt;li&gt;商户特权管理&lt;/li&gt; +&lt;/ol&gt; +&lt;div align=&#34;center&#34;&gt; +&lt;img src=https://imgoldjii.oss-cn-beijing.aliyuncs.com/iShot2022-10-2123134123.png width=600/&gt; +&lt;/div&gt; +&lt;h2 id=&#34;注意事项&#34;&gt;注意事项&lt;/h2&gt; +&lt;ul&gt; +&lt;li&gt;商户特权为官方活动,并不会导致账号封禁。&lt;/li&gt; +&lt;li&gt;选定一个商户 WiFi 后需无需更换,一直使用该 WiFi 即可。&lt;/li&gt; +&lt;li&gt;每次使用爱思助手修改定位都是长时间有效的,即使是短时间的重启手机也不会失效,如果想恢复定位,可以使用爱思助手恢复。&lt;/li&gt; +&lt;li&gt;请勿勾选 “ 每次登陆自动定位并开启特权 ”。&lt;/li&gt; +&lt;/ul&gt;]]>
@@ -683,94 +683,94 @@ ssh -T git@github2.com <h1 id="写在前面">写在前面</h1> <p>记录阅读 罗兰·米勒 的《亲密关系》过程中的收获。</p> <p>人际关系是由多种影响因素构成的,其范围从当前文化的流行时尚到人类种族的基本属性,非常广泛。在这些一般的影响因素之外,还有很多个体独有的影响因素如人格和经验,它们有些是习得的,有些是遗传的。最终,两个来自同一星球,但在很多方面存在一定程度差异的人,开始了他们的互动。互动的结果或许令人沮丧,或许令人满意。</p> - 写在前面 -

记录阅读 罗兰·米勒 的《亲密关系》过程中的收获。

-

人际关系是由多种影响因素构成的,其范围从当前文化的流行时尚到人类种族的基本属性,非常广泛。在这些一般的影响因素之外,还有很多个体独有的影响因素如人格和经验,它们有些是习得的,有些是遗传的。最终,两个来自同一星球,但在很多方面存在一定程度差异的人,开始了他们的互动。互动的结果或许令人沮丧,或许令人满意。

-

亲密关系

-

人际关系种类多样,规格不齐,我们上有父母,还可能下有子女,我们还有朋友和爱人,《亲密关系》书中重点关注的是后两种伙伴关系,而且限制于成人之间。

-

亲密关系的定义

-

亲密关系是一个复杂的概念,包含各种成分,很难去下定义,好在研究者和普通人都认为亲密关系和泛泛之交之间存在六个方面的程度差异。

-
    -
  • 了解:广泛而私密的了解
  • -
  • 关心:相互的关心
  • -
  • 相互依赖性:彼此需要和影响对方的程度时频繁、强烈、多样且持久的
  • -
  • 相互一致性:自我接纳他人的程度是否高度重合
  • -
  • 信任:相信对方会善待和尊重自己
  • -
  • 忠诚:希望关系能地老天长,并不惜时间、人力和物力的消耗
  • -
-

归属需要

-

为什么人们需要亲密关系呢?

-

亲密关系对于人类的重要性主要体现在归属需要上,归属需要是人类长期演化的产物,逐渐成为所有人类共同的自然倾向,让我们觉得与和自己有关联的人的正常社会交往是必不可少的,是对持续关爱和包容的一种需要。

-

文化的影响

-

文化标准是人们建立人际关系的基石,它影响着人们对人际关系的期望,限定了正常的人际关系模式。

-

就拿最近普遍流行的同居现象来说,现在许多高中生认为情侣未婚同居是个“好主意”,因为他们能据此考察彼此是否真正能“和睦相处”(Bachman etal.,2001)。这种态度使未婚同居看上去很有道理,好像是个不错的选择。

-
- -
-

然而,如果人们并没有切实的结婚计划,未婚同居并不能确保随后的婚姻幸福美满;相反,同居增加了夫妻离婚的危险。如上图的研究结果所示,随着时间的推移,同居情侣结婚的可能性逐渐降低,但分手的可能性却不下降。

-

总的说来,草率同居原本用来测试伴侣能否和睦共处,却好像会损害人们对婚姻的积极态度和维持婚姻的决心,这种态度和决心是幸福婚姻的支柱(Rhoades et al.,2009)。

-

个人经历的影响

-

依恋类型不受基因影响,是在与生俱来的个体特征和养育水平的影响下塑造的。

-
    -
  • 安全型:对亲密关系和相互依赖安心、乐观、好交际
  • -
  • 痴迷型:对有损坏亲密关系的任何威胁都不安和警惕、嫉妒、贪婪
  • -
  • 恐惧型:自立、漠视亲密关系、冷淡、独立
  • -
  • 疏离型:害怕被遗弃、不信任他人、猜忌多疑、害羞
  • -
-

我们幼时对人际交往价值和他人是否可信的观念,起源于我们与照料者的交往,由于运气的好坏,我们就此走向了信任或恐惧的亲密关系之路。这段历程永远不会停止,同行者随后给予的阻碍或帮助会改变我们亲密关系的方向和进程。视乎人际交往经验的不同,我们习得的依恋类型既可随时间发生变化,也可永久保持稳定。

-

个体差异的影响

-

性别差异

-

两性群体差异确实存在,但远低于同性不同个体间的差异程度。

-

两性性别内的行为和观点差异通常远大于两性之间的平均差异。男性较女性更能接受随意、短暂的性关系(Peterson & Hyde,2010),这未必表示所有男性都喜欢随意的性关系。

-

有些男人喜欢与陌生人发生性行为,但也有些男人根本不喜欢这样做,这两组男性在性生活上的相似程度远不如男性和女性的平均水平。换句话说,尽管在性放任上存在两性差异,一位非常性放任的男人与女性性放任的平均水平的差别,远低于他与另一位性保守的男人在性放任上的差别。

-

书中一则描述刻板化印象的笑话很有趣。

-

怎样吸引女人:

-

夸赞她,依偎她,亲吻她,爱抚她,热爱她,安慰她,保护她,拥抱她,不惜千金买笑,奉出美酒佳肴,烦心的时候听她唠叨,微恙的时候悉心照料,永远和她待在一起,永远支持她的意见,为她走遍天涯海角。

-

怎样吸引男人:

-

脱光衣服,带上啤酒。

-

实际上,两性对亲密关系的期望差异很小,他们根本不是“相反的”两类人(Hyde,2007)。如果认为男女两性差异很大,当面临冲突时(这无法避免)不太可能去努力修复自己与异性的亲密关系。认为异性是来自另一世界的外星人不仅是错误的,更是有害的,它阻碍了对伴侣观点的理解,妨碍了双方协作解决问题。

-

性认同差异

-

性认同差异指的是由文化和教育引起的两性在社会性和心理上的差异,或者叫社会性别(Wood & Eagly,2009)。

-

性认同最好的例子是性别角色(gender roles),即社会文化所期待的男女两性应有的“正常”行为模式,下面是常见的性别角色分类,社会期望和鼓励男人具有工具性,女人具有表达性。

-
    -
  • 工具性:自信、独立、有抱负、领导力、果敢,约占 25%
  • -
  • 表达性:热情、温柔、有同情心、亲切、敏感,约占 25%
  • -
  • 跨类型:约占 35%
  • -
  • 未分化:约占 15%
  • -
-

人格

-

大五人格特质可以很好地区分人们在诸多方面(如行为、思维和情感等)的差异(McCrae & Costa,2010)。

-
    -
  • 开放性:富有想象力、不墨守成规、艺术气质,相对应的是拘泥、僵化和教条。
  • -
  • 外倾性:开朗、合群、热情、喜欢社交,相对应的是谨慎、内敛及害羞。
  • -
  • 尽责性:勤劳、可依赖、有序,相对应的是不可靠、粗心大意。
  • -
  • 宜人性:同情心、合作性、对人信任,相对应的是易怒、暴躁和充满敌意。
  • -
  • 神经质:善变、容易担忧、焦虑和愤怒的程度。
  • -
-

重要性从低到高排列的,大五特质中最重要的是那个有消极作用的特质:神经质(Malouff et al.,2010)。神经质的人容易发怒和焦虑,这些不良倾向往往会引起人际摩擦、悲观情绪和争执(Suls & Martin,2005)。

-

自尊

-

自尊就是人际交往中的自我评价,可以看作一种 “ 社会关系测量仪 ”,如果他人积极地对待我们并看重与我们的关系,自尊水平就高;如果我们不能吸引别人的关注,自尊水平就低。

-

我们人类是高度社会化的动物,如果他人不喜欢我们,我们要喜欢自己非常困难(的确,这样做很不现实)。大多数情况下,如果不能从他人那里获得足够的接纳和欣赏,长期处在低自尊的人就会形成负面的自我评价。

-

值得一提的是自尊在亲密关系中的影响:低自尊的人有时低估伴侣对他们的爱,以致损害亲密关系(Murray et al.,2001)。

-

我们都需要在与他人的联系和自我保护间保持平衡,但低自尊的人总把他们脆弱的自尊心置于亲密关系之上。低自尊者的自我怀疑和敏感脆弱使他们从无数的琐事中制造出堆积如山的问题。他们错误地以为爱情之路上的磕磕碰碰是伴侣拒绝承诺的不祥之兆。然后,又表现出令人反感、自我打击式的伤害和愤怒,完全隔断了自己渴望的伴侣的安慰。相形之下,高自尊者对同样的小磕绊完全不以为意,信心十足地期待伴侣对自己的接纳和正面评价。

-

人类本性的影响

-

数千年来人类生存所面临的环境压力遗留下了精神和情感的痕迹。某些遗留下的情感和行为反应源自我们的远祖,在现代已经没有必要了,但前人生存的这些遗迹不可磨灭地刻入了我们的性格,使得每个人都表现出一定的倾向性。

-

演化心理学的三个假设:

-
    -
  • 性选择使人类成为今天这样的物种。
  • -
  • 两性之所以存在差异,只是因为某种程度上他们在过去面临着不同的繁殖困境(养育投入)。
  • -
  • 文化影响决定了演化形成的行为模式是否具有适应性,并且文化的变化比演化快得多(父系不确定)。
  • -
-

人际互动的影响

-

人际关系常常大于它各部分相加的总和,这便是人际关系的最后一个构成要素 “ 互动 ” 的影响。

-

比如拿伴侣关系中的信任程度来说,信任是双向的过程,同时收到你和伴侣双方性情的影响,来源于你每天和伴侣不断付出以及不断接受的动态过程,换而言之,信任是流动的过程而非静止不变的事物,它在你所有的人际关系中时起时落。

-

人际关系的消极面

-

我们不得不承认人际关系也有一些潜在的代价,有时我们和他人打交道也会带来不幸和痛苦。

-

当人们与他人接近时,可能害怕自己最在乎的秘密被人揭露或利用。他们还可能担忧伴随相互依赖而来的自主性和自我控制的丧失(Baxter,2004)。他们或许还担心会被自己所依赖的人抛弃。他们认识到人际关系可能存在欺骗,人们有时还会混淆了性和爱(Firestone & Catlett,1999)。实际上,大多数人(56%)在过去的5年中人际关系都曾陷入困境(Levitt et al.,1996)。

-

那么为什么还要冒这种风险呢?书中的回答很是浪漫。

-
-

因为我们人类是社会化的动物,我们需要彼此。没有与他人的亲密联系,我们就会枯萎和死亡 。

-
]]>
+ <![CDATA[&lt;h1 id=&#34;写在前面&#34;&gt;写在前面&lt;/h1&gt; +&lt;p&gt;记录阅读 罗兰·米勒 的《亲密关系》过程中的收获。&lt;/p&gt; +&lt;p&gt;人际关系是由多种影响因素构成的,其范围从当前文化的流行时尚到人类种族的基本属性,非常广泛。在这些一般的影响因素之外,还有很多个体独有的影响因素如人格和经验,它们有些是习得的,有些是遗传的。最终,两个来自同一星球,但在很多方面存在一定程度差异的人,开始了他们的互动。互动的结果或许令人沮丧,或许令人满意。&lt;/p&gt; +&lt;h1 id=&#34;亲密关系&#34;&gt;亲密关系&lt;/h1&gt; +&lt;p&gt;人际关系种类多样,规格不齐,我们上有父母,还可能下有子女,我们还有朋友和爱人,《亲密关系》书中重点关注的是后两种伙伴关系,而且限制于成人之间。&lt;/p&gt; +&lt;h2 id=&#34;亲密关系的定义&#34;&gt;亲密关系的定义&lt;/h2&gt; +&lt;p&gt;亲密关系是一个复杂的概念,包含各种成分,很难去下定义,好在研究者和普通人都认为亲密关系和泛泛之交之间存在六个方面的程度差异。&lt;/p&gt; +&lt;ul&gt; +&lt;li&gt;了解:广泛而私密的了解&lt;/li&gt; +&lt;li&gt;关心:相互的关心&lt;/li&gt; +&lt;li&gt;相互依赖性:彼此需要和影响对方的程度时频繁、强烈、多样且持久的&lt;/li&gt; +&lt;li&gt;相互一致性:自我接纳他人的程度是否高度重合&lt;/li&gt; +&lt;li&gt;信任:相信对方会善待和尊重自己&lt;/li&gt; +&lt;li&gt;忠诚:希望关系能地老天长,并不惜时间、人力和物力的消耗&lt;/li&gt; +&lt;/ul&gt; +&lt;h2 id=&#34;归属需要&#34;&gt;归属需要&lt;/h2&gt; +&lt;p&gt;为什么人们需要亲密关系呢?&lt;/p&gt; +&lt;p&gt;亲密关系对于人类的重要性主要体现在归属需要上,归属需要是人类长期演化的产物,逐渐成为所有人类共同的自然倾向,让我们觉得与和自己有关联的人的正常社会交往是必不可少的,是对持续关爱和包容的一种需要。&lt;/p&gt; +&lt;h1 id=&#34;文化的影响&#34;&gt;文化的影响&lt;/h1&gt; +&lt;p&gt;文化标准是人们建立人际关系的基石,它影响着人们对人际关系的期望,限定了正常的人际关系模式。&lt;/p&gt; +&lt;p&gt;就拿最近普遍流行的同居现象来说,现在许多高中生认为情侣未婚同居是个“好主意”,因为他们能据此考察彼此是否真正能“和睦相处”(Bachman etal.,2001)。这种态度使未婚同居看上去很有道理,好像是个不错的选择。&lt;/p&gt; +&lt;div align=&#34;center&#34;&gt; +&lt;img src=https://imgoldjii.oss-cn-beijing.aliyuncs.com/iShot_2022-10-17_00.39.10.png width=600 height=350 /&gt; +&lt;/div&gt; +&lt;p&gt;然而,如果人们并没有切实的结婚计划,未婚同居并不能确保随后的婚姻幸福美满;相反,同居增加了夫妻离婚的危险。如上图的研究结果所示,随着时间的推移,同居情侣结婚的可能性逐渐降低,但分手的可能性却不下降。&lt;/p&gt; +&lt;p&gt;总的说来,草率同居原本用来测试伴侣能否和睦共处,却好像会损害人们对婚姻的积极态度和维持婚姻的决心,这种态度和决心是幸福婚姻的支柱(Rhoades et al.,2009)。&lt;/p&gt; +&lt;h1 id=&#34;个人经历的影响&#34;&gt;个人经历的影响&lt;/h1&gt; +&lt;p&gt;依恋类型不受基因影响,是在与生俱来的个体特征和养育水平的影响下塑造的。&lt;/p&gt; +&lt;ul&gt; +&lt;li&gt;安全型:对亲密关系和相互依赖安心、乐观、好交际&lt;/li&gt; +&lt;li&gt;痴迷型:对有损坏亲密关系的任何威胁都不安和警惕、嫉妒、贪婪&lt;/li&gt; +&lt;li&gt;恐惧型:自立、漠视亲密关系、冷淡、独立&lt;/li&gt; +&lt;li&gt;疏离型:害怕被遗弃、不信任他人、猜忌多疑、害羞&lt;/li&gt; +&lt;/ul&gt; +&lt;p&gt;我们幼时对人际交往价值和他人是否可信的观念,起源于我们与照料者的交往,由于运气的好坏,我们就此走向了信任或恐惧的亲密关系之路。这段历程永远不会停止,同行者随后给予的阻碍或帮助会改变我们亲密关系的方向和进程。视乎人际交往经验的不同,我们习得的依恋类型既可随时间发生变化,也可永久保持稳定。&lt;/p&gt; +&lt;h1 id=&#34;个体差异的影响&#34;&gt;个体差异的影响&lt;/h1&gt; +&lt;h2 id=&#34;性别差异&#34;&gt;性别差异&lt;/h2&gt; +&lt;p&gt;两性群体差异确实存在,但远低于同性不同个体间的差异程度。&lt;/p&gt; +&lt;p&gt;两性性别内的行为和观点差异通常远大于两性之间的平均差异。男性较女性更能接受随意、短暂的性关系(Peterson &amp;amp; Hyde,2010),这未必表示所有男性都喜欢随意的性关系。&lt;/p&gt; +&lt;p&gt;有些男人喜欢与陌生人发生性行为,但也有些男人根本不喜欢这样做,这两组男性在性生活上的相似程度远不如男性和女性的平均水平。换句话说,尽管在性放任上存在两性差异,一位非常性放任的男人与女性性放任的平均水平的差别,远低于他与另一位性保守的男人在性放任上的差别。&lt;/p&gt; +&lt;p&gt;书中一则描述刻板化印象的笑话很有趣。&lt;/p&gt; +&lt;p&gt;&lt;em&gt;怎样吸引女人:&lt;/em&gt;&lt;/p&gt; +&lt;p&gt;&lt;em&gt;夸赞她,依偎她,亲吻她,爱抚她,热爱她,安慰她,保护她,拥抱她,不惜千金买笑,奉出美酒佳肴,烦心的时候听她唠叨,微恙的时候悉心照料,永远和她待在一起,永远支持她的意见,为她走遍天涯海角。&lt;/em&gt;&lt;/p&gt; +&lt;p&gt;&lt;em&gt;怎样吸引男人:&lt;/em&gt;&lt;/p&gt; +&lt;p&gt;&lt;em&gt;脱光衣服,带上啤酒。&lt;/em&gt;&lt;/p&gt; +&lt;p&gt;实际上,两性对亲密关系的期望差异很小,他们根本不是“相反的”两类人(Hyde,2007)。如果认为男女两性差异很大,当面临冲突时(这无法避免)不太可能去努力修复自己与异性的亲密关系。认为异性是来自另一世界的外星人不仅是错误的,更是有害的,它阻碍了对伴侣观点的理解,妨碍了双方协作解决问题。&lt;/p&gt; +&lt;h2 id=&#34;性认同差异&#34;&gt;性认同差异&lt;/h2&gt; +&lt;p&gt;性认同差异指的是由文化和教育引起的两性在社会性和心理上的差异,或者叫社会性别(Wood &amp;amp; Eagly,2009)。&lt;/p&gt; +&lt;p&gt;性认同最好的例子是性别角色(gender roles),即社会文化所期待的男女两性应有的“正常”行为模式,下面是常见的性别角色分类,社会期望和鼓励男人具有工具性,女人具有表达性。&lt;/p&gt; +&lt;ul&gt; +&lt;li&gt;工具性:自信、独立、有抱负、领导力、果敢,约占 25%&lt;/li&gt; +&lt;li&gt;表达性:热情、温柔、有同情心、亲切、敏感,约占 25%&lt;/li&gt; +&lt;li&gt;跨类型:约占 35%&lt;/li&gt; +&lt;li&gt;未分化:约占 15%&lt;/li&gt; +&lt;/ul&gt; +&lt;h2 id=&#34;人格&#34;&gt;人格&lt;/h2&gt; +&lt;p&gt;大五人格特质可以很好地区分人们在诸多方面(如行为、思维和情感等)的差异(McCrae &amp;amp; Costa,2010)。&lt;/p&gt; +&lt;ul&gt; +&lt;li&gt;开放性:富有想象力、不墨守成规、艺术气质,相对应的是拘泥、僵化和教条。&lt;/li&gt; +&lt;li&gt;外倾性:开朗、合群、热情、喜欢社交,相对应的是谨慎、内敛及害羞。&lt;/li&gt; +&lt;li&gt;尽责性:勤劳、可依赖、有序,相对应的是不可靠、粗心大意。&lt;/li&gt; +&lt;li&gt;宜人性:同情心、合作性、对人信任,相对应的是易怒、暴躁和充满敌意。&lt;/li&gt; +&lt;li&gt;神经质:善变、容易担忧、焦虑和愤怒的程度。&lt;/li&gt; +&lt;/ul&gt; +&lt;p&gt;重要性从低到高排列的,大五特质中最重要的是那个有消极作用的特质:神经质(Malouff et al.,2010)。神经质的人容易发怒和焦虑,这些不良倾向往往会引起人际摩擦、悲观情绪和争执(Suls &amp;amp; Martin,2005)。&lt;/p&gt; +&lt;h2 id=&#34;自尊&#34;&gt;自尊&lt;/h2&gt; +&lt;p&gt;自尊就是人际交往中的自我评价,可以看作一种 “ 社会关系测量仪 ”,如果他人积极地对待我们并看重与我们的关系,自尊水平就高;如果我们不能吸引别人的关注,自尊水平就低。&lt;/p&gt; +&lt;p&gt;我们人类是高度社会化的动物,如果他人不喜欢我们,我们要喜欢自己非常困难(的确,这样做很不现实)。大多数情况下,如果不能从他人那里获得足够的接纳和欣赏,长期处在低自尊的人就会形成负面的自我评价。&lt;/p&gt; +&lt;p&gt;值得一提的是自尊在亲密关系中的影响:低自尊的人有时低估伴侣对他们的爱,以致损害亲密关系(Murray et al.,2001)。&lt;/p&gt; +&lt;p&gt;我们都需要在与他人的联系和自我保护间保持平衡,但低自尊的人总把他们脆弱的自尊心置于亲密关系之上。低自尊者的自我怀疑和敏感脆弱使他们从无数的琐事中制造出堆积如山的问题。他们错误地以为爱情之路上的磕磕碰碰是伴侣拒绝承诺的不祥之兆。然后,又表现出令人反感、自我打击式的伤害和愤怒,完全隔断了自己渴望的伴侣的安慰。相形之下,高自尊者对同样的小磕绊完全不以为意,信心十足地期待伴侣对自己的接纳和正面评价。&lt;/p&gt; +&lt;h1 id=&#34;人类本性的影响&#34;&gt;人类本性的影响&lt;/h1&gt; +&lt;p&gt;数千年来人类生存所面临的环境压力遗留下了精神和情感的痕迹。某些遗留下的情感和行为反应源自我们的远祖,在现代已经没有必要了,但前人生存的这些遗迹不可磨灭地刻入了我们的性格,使得每个人都表现出一定的倾向性。&lt;/p&gt; +&lt;p&gt;演化心理学的三个假设:&lt;/p&gt; +&lt;ul&gt; +&lt;li&gt;性选择使人类成为今天这样的物种。&lt;/li&gt; +&lt;li&gt;两性之所以存在差异,只是因为某种程度上他们在过去面临着不同的繁殖困境(养育投入)。&lt;/li&gt; +&lt;li&gt;文化影响决定了演化形成的行为模式是否具有适应性,并且文化的变化比演化快得多(父系不确定)。&lt;/li&gt; +&lt;/ul&gt; +&lt;h1 id=&#34;人际互动的影响&#34;&gt;人际互动的影响&lt;/h1&gt; +&lt;p&gt;人际关系常常大于它各部分相加的总和,这便是人际关系的最后一个构成要素 “ 互动 ” 的影响。&lt;/p&gt; +&lt;p&gt;比如拿伴侣关系中的信任程度来说,信任是双向的过程,同时收到你和伴侣双方性情的影响,来源于你每天和伴侣不断付出以及不断接受的动态过程,换而言之,信任是流动的过程而非静止不变的事物,它在你所有的人际关系中时起时落。&lt;/p&gt; +&lt;h1 id=&#34;人际关系的消极面&#34;&gt;人际关系的消极面&lt;/h1&gt; +&lt;p&gt;我们不得不承认人际关系也有一些潜在的代价,有时我们和他人打交道也会带来不幸和痛苦。&lt;/p&gt; +&lt;p&gt;当人们与他人接近时,可能害怕自己最在乎的秘密被人揭露或利用。他们还可能担忧伴随相互依赖而来的自主性和自我控制的丧失(Baxter,2004)。他们或许还担心会被自己所依赖的人抛弃。他们认识到人际关系可能存在欺骗,人们有时还会混淆了性和爱(Firestone &amp;amp; Catlett,1999)。实际上,大多数人(56%)在过去的5年中人际关系都曾陷入困境(Levitt et al.,1996)。&lt;/p&gt; +&lt;p&gt;那么为什么还要冒这种风险呢?书中的回答很是浪漫。&lt;/p&gt; +&lt;blockquote&gt; +&lt;p&gt;因为我们人类是社会化的动物,我们需要彼此。没有与他人的亲密联系,我们就会枯萎和死亡 。&lt;/p&gt; +&lt;/blockquote&gt;]]>
@@ -784,243 +784,243 @@ ssh -T git@github2.com <div align="center"> <img src=https://imgoldjii.oss-cn-beijing.aliyuncs.com/iShot2022-04-24_22.15.57.png width=400 height=400 /> </div> - 环形布局的定义 -

如果存在一个圆 A 和若干个圆 a,圆 a 皆于圆 A 相交,圆 a 的圆心皆位于圆 A 上,且圆 a 间的 圆心距 相等。

-
- -
-

即环形布局应当满足以下两个属性:

-
    -
  1. 子 widget的中点到容器圆心的距离保持一致。
  2. -
  3. 相邻子 widget 中点的间距保持一致。
  4. -
-

根据以上性质我们可以根据数学公式计算出 圆 a 相对于圆 A 的位置 ,这是实现环形布局的关键信息。

-

在上面的定义中并未提及圆 a 的半径关系,实际上圆 a 的半径是可以不一致的,把圆 a 看作子元素的 外切圆,在复杂的生产环境中子元素的外切圆半径往往是不一致的,所以我们还需要确定 圆 a 的最大半径

-

计算子元素的位置

-

数学推导

-

要确定圆 a 相对于圆 A 的位置,首先要计算 圆心 a 相对于圆心 A 的偏移量

-

设圆心 A 坐标为 $ (x_0, y_0) $ 、半径为 $ r $、圆心 a 坐标为 $ (x_1, y_1) $ ,圆心 A 和圆心 a 的连线和坐标系横轴的夹角角度为 $ \theta $ 。

-
- -
-

圆心 a 坐标 $ (x_1, y_1) $ 为圆心 A 坐标 $ (x_0, y_0) $ 加上相对坐标系轴上的偏移量。

-

$$ -x_1 = x_0 + r \times cos(\theta) -$$

-

$$ -y_1 = y_0 + r \times sin(\theta) -$$

-

代码实现

-
- -
-
 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
-10
-11
-12
-13
-14
-15
-16
-17
-18
-19
-20
-21
-22
-23
-24
-
-
/// 计算圆心a相对于圆心A的偏移量
-///
-/// @param centerPoint 圆心A的坐标
-/// @param radius 圆A的半径
-/// @param count 圆a的数量
-/// @param which 圆a的序号
-/// @param initAngle 起始位置
-/// @param direction 排列方向
-Offset _getChildCenterOffset({
-  Offset circleCenter,
-  double radius,
-  int count,
-  int which,
-  double firstAngle,
-  int direction,
-}) {
-  // 扇形弧度
-  double radian = _radian(360 / count);
-  // 处理起始位置偏移和排列方向
-  double radianOffset = _radian(firstAngle * direction);
-  double x = circleCenter.dx + radius * cos(radian * which + radianOffset);
-  double y = circleCenter.dy + radius * sin(radian * which + radianOffset);
-  return Offset(x, y);
-}
-
-
-

计算子元素的半径

-

数学推导

-

为了满足子元素环形排列的需要,最大子元素的外切圆上限需为 $ 90^\circ $ 扇形的 内切圆,如下图所示。

-
- -
-

设扇形半径为 $ R $、扇形圆心角为 $ \alpha $、扇形内切圆半径为 $ r $。

-
- -
-

最大子元素半径推导过程如下。

-

$$ + <![CDATA[&lt;h2 id=&#34;环形布局的定义&#34;&gt;环形布局的定义&lt;/h2&gt; +&lt;p&gt;如果存在一个圆 A 和若干个圆 a,圆 a 皆于圆 A 相交,圆 a 的圆心皆位于圆 A 上,且圆 a 间的 &lt;a href=&#34;https://baike.baidu.com/item/%E5%9C%86%E5%BF%83%E8%B7%9D/9556681?fr=aladdin&#34;&gt;圆心距&lt;/a&gt; 相等。&lt;/p&gt; +&lt;div align=&#34;center&#34;&gt; +&lt;img src=https://imgoldjii.oss-cn-beijing.aliyuncs.com/iShot2022-04-24_22.15.57.png width=400 height=400 /&gt; +&lt;/div&gt; +&lt;p&gt;即环形布局应当满足以下两个属性:&lt;/p&gt; +&lt;ol&gt; +&lt;li&gt;子 widget的中点到容器圆心的距离保持一致。&lt;/li&gt; +&lt;li&gt;相邻子 widget 中点的间距保持一致。&lt;/li&gt; +&lt;/ol&gt; +&lt;p&gt;根据以上性质我们可以根据数学公式计算出 &lt;strong&gt;圆 a 相对于圆 A 的位置&lt;/strong&gt; ,这是实现环形布局的关键信息。&lt;/p&gt; +&lt;p&gt;在上面的定义中并未提及圆 a 的半径关系,实际上圆 a 的半径是可以不一致的,把圆 a 看作子元素的 &lt;a href=&#34;https://baike.baidu.com/item/%E5%A4%96%E5%88%87%E5%9C%86/4186184?fr=aladdin&#34;&gt;外切圆&lt;/a&gt;,在复杂的生产环境中子元素的外切圆半径往往是不一致的,所以我们还需要确定 &lt;strong&gt;圆 a 的最大半径&lt;/strong&gt; 。&lt;/p&gt; +&lt;h2 id=&#34;计算子元素的位置&#34;&gt;计算子元素的位置&lt;/h2&gt; +&lt;h3 id=&#34;数学推导&#34;&gt;数学推导&lt;/h3&gt; +&lt;p&gt;要确定圆 a 相对于圆 A 的位置,首先要计算 &lt;strong&gt;圆心 a 相对于圆心 A 的偏移量&lt;/strong&gt;。&lt;/p&gt; +&lt;p&gt;设圆心 A 坐标为 $ (x_0, y_0) $ 、半径为 $ r $、圆心 a 坐标为 $ (x_1, y_1) $ ,圆心 A 和圆心 a 的连线和坐标系横轴的夹角角度为 $ \theta $ 。&lt;/p&gt; +&lt;div align=&#34;center&#34;&gt; +&lt;img src=https://imgoldjii.oss-cn-beijing.aliyuncs.com/iShot2022-04-25_15.56.56.png width=400 height=400/&gt; +&lt;/div&gt; +&lt;p&gt;圆心 a 坐标 $ (x_1, y_1) $ 为圆心 A 坐标 $ (x_0, y_0) $ 加上相对坐标系轴上的偏移量。&lt;/p&gt; +&lt;p&gt;$$ +x_1 = x_0 + r \times cos(\theta) +$$&lt;/p&gt; +&lt;p&gt;$$ +y_1 = y_0 + r \times sin(\theta) +$$&lt;/p&gt; +&lt;h3 id=&#34;代码实现&#34;&gt;代码实现&lt;/h3&gt; +&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt; +&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt; +&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24 +&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt; +&lt;td class=&#34;lntd&#34;&gt; +&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-dart&#34; data-lang=&#34;dart&#34;&gt;&lt;span class=&#34;c1&#34;&gt;/// 计算圆心a相对于圆心A的偏移量 +&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;/// +&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;/// @param centerPoint 圆心A的坐标 +&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;/// @param radius 圆A的半径 +&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;/// @param count 圆a的数量 +&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;/// @param which 圆a的序号 +&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;/// @param initAngle 起始位置 +&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;/// @param direction 排列方向 +&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Offset&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;_getChildCenterOffset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt; + &lt;span class=&#34;n&#34;&gt;Offset&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;circleCenter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; + &lt;span class=&#34;kt&#34;&gt;double&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;radius&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; + &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; + &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;which&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; + &lt;span class=&#34;kt&#34;&gt;double&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;firstAngle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; + &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;direction&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; +&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; + &lt;span class=&#34;c1&#34;&gt;// 扇形弧度 +&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;double&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;radian&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;_radian&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;360&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; + &lt;span class=&#34;c1&#34;&gt;// 处理起始位置偏移和排列方向 +&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;double&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;radianOffset&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;_radian&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;firstAngle&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;direction&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; + &lt;span class=&#34;kt&#34;&gt;double&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;circleCenter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dx&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;radius&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cos&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;radian&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;which&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;radianOffset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; + &lt;span class=&#34;kt&#34;&gt;double&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;y&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;circleCenter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dy&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;radius&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;radian&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;which&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;radianOffset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; + &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;y&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; +&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; +&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; +&lt;/div&gt; +&lt;/div&gt;&lt;h2 id=&#34;计算子元素的半径&#34;&gt;计算子元素的半径&lt;/h2&gt; +&lt;h3 id=&#34;数学推导-1&#34;&gt;数学推导&lt;/h3&gt; +&lt;p&gt;为了满足子元素环形排列的需要,最大子元素的外切圆上限需为 $ 90^\circ $ 扇形的 &lt;a href=&#34;https://baike.baidu.com/item/%E5%86%85%E5%88%87%E5%9C%86&#34;&gt;内切圆&lt;/a&gt;,如下图所示。&lt;/p&gt; +&lt;div align=&#34;center&#34;&gt; +&lt;img src=https://imgoldjii.oss-cn-beijing.aliyuncs.com/iShot2022-04-24_23.42.54.png width=400 height=400 /&gt; +&lt;/div&gt; +&lt;p&gt;设扇形半径为 $ R $、扇形圆心角为 $ \alpha $、扇形内切圆半径为 $ r $。&lt;/p&gt; +&lt;div align=&#34;center&#34;&gt; +&lt;img src=https://imgoldjii.oss-cn-beijing.aliyuncs.com/iShot2022-04-25_20.50.01.png width=400 height=400 /&gt; +&lt;/div&gt; +&lt;p&gt;最大子元素半径推导过程如下。&lt;/p&gt; +&lt;p&gt;$$ sin(\frac{\alpha}{2}) = \frac{r}{R - r} -$$

-

$$ +$$&lt;/p&gt; +&lt;p&gt;$$ r = (R - r) \times sin(\frac{\alpha}{2}) -$$

-

$$ +$$&lt;/p&gt; +&lt;p&gt;$$ r = R \times sin(\frac{\alpha}{2}) - r \times sin(\frac{\alpha}{2}) -$$

-

$$ -r + r \times sin(\frac{\alpha}{2}) = R \times sin(\frac{\alpha}{2}) -$$

-

$$ -r = \frac{R \times sin(\frac{\alpha}{2})}{1 + sin(\frac{\alpha}{2})} -$$

-

代码实现

-
- -
-
 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
-10
-11
-12
-13
-14
-15
-16
-17
-18
-19
-20
-
-
/// 计算圆a的半径
-///
-/// @param radius 圆A的半径
-/// @param angle 扇形的角度
-double _getChildRadius(double radius, double angle) {
-  // 扇形角度大于180度,只可以放置一个。
-  if (angle > 180) {
-    return radius;
-  }
+$$&lt;/p&gt;
+&lt;p&gt;$$
+r + r \times sin(\frac{\alpha}{2}) = R \times sin(\frac{\alpha}{2})
+$$&lt;/p&gt;
+&lt;p&gt;$$
+r = \frac{R \times sin(\frac{\alpha}{2})}{1 + sin(\frac{\alpha}{2})}
+$$&lt;/p&gt;
+&lt;h3 id=&#34;代码实现-1&#34;&gt;代码实现&lt;/h3&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-dart&#34; data-lang=&#34;dart&#34;&gt;&lt;span class=&#34;c1&#34;&gt;/// 计算圆a的半径
+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;///
+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;/// @param radius 圆A的半径
+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;/// @param angle 扇形的角度
+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;double&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;_getChildRadius&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;double&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;radius&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;double&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;angle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
+  &lt;span class=&#34;c1&#34;&gt;// 扇形角度大于180度,只可以放置一个。
+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;angle&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;180&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
+    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;radius&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
+  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
 
-  /// 扇形最大内切圆公式,见公式推导。
-  return radius * sin(_radian(angle / 2)) / (1 + sin(_radian(angle / 2)));
-}
+  &lt;span class=&#34;c1&#34;&gt;/// 扇形最大内切圆公式,见公式推导。
+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;radius&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_radian&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;angle&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_radian&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;angle&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)));&lt;/span&gt;
+&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
 
-/// 计算弧度
-///
-/// @param angle 角度
-double _radian(double angle) {
-  return pi / 180 * angle;
-}
-
-
-

实现

-

我们选择使用 CustomMultiChildLayout 实现环形布局的功能,看下官网的定义。

-

-
-

“ CustomMultiChildLayout is appropriate when there are complex relationships between the size and positioning of multiple widgets. ”

-
-

所以用 CustomMultiChildLayout 实现再合适不过,效果如下。

-
- -
-

完整代码已上传至 pub.dev,这里仅截取 RingLayout 的部分代码。

-
-

ring_layout: https://pub.dev/packages/ring_layout

-
-
- -
-
 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
-
-
class RingLayout extends StatelessWidget {
-  final List<Widget> children;
-  final double initAngle;
-  final bool reverse;
-  final double radiusRatio;
+&lt;span class=&#34;c1&#34;&gt;/// 计算弧度
+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;///
+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;/// @param angle 角度
+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;double&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;_radian&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;double&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;angle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
+  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pi&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;180&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;angle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
+&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;h2 id=&#34;实现&#34;&gt;实现&lt;/h2&gt;
+&lt;p&gt;我们选择使用 &lt;code&gt;CustomMultiChildLayout&lt;/code&gt; 实现环形布局的功能,看下官网的定义。&lt;/p&gt;
+&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/iShot2022-04-24_22.24.15.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
+&lt;blockquote&gt;
+&lt;p&gt;“ CustomMultiChildLayout is appropriate when there are complex relationships between the size and positioning of multiple widgets. ”&lt;/p&gt;
+&lt;/blockquote&gt;
+&lt;p&gt;所以用 &lt;code&gt;CustomMultiChildLayout&lt;/code&gt; 实现再合适不过,效果如下。&lt;/p&gt;
+&lt;div align=&#34;center&#34;&gt;
+&lt;img src=https://imgoldjii.oss-cn-beijing.aliyuncs.com/屏幕录制2022-06-15-上午10.41.11.gif height=400 /&gt;
+&lt;/div&gt;
+&lt;p&gt;完整代码已上传至 &lt;a href=&#34;https://pub.dev&#34;&gt;pub.dev&lt;/a&gt;,这里仅截取 &lt;code&gt;RingLayout&lt;/code&gt; 的部分代码。&lt;/p&gt;
+&lt;blockquote&gt;
+&lt;p&gt;ring_layout: &lt;a href=&#34;https://pub.dev/packages/ring_layout&#34;&gt;https://pub.dev/packages/ring_layout&lt;/a&gt;&lt;/p&gt;
+&lt;/blockquote&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-dart&#34; data-lang=&#34;dart&#34;&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;RingLayout&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;StatelessWidget&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
+  &lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Widget&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;children&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
+  &lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;double&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;initAngle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
+  &lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;reverse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
+  &lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;double&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;radiusRatio&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
 
-  const RingLayout({
-    Key? key,
-    required this.children,
-    this.reverse = false,
-    this.radiusRatio = 1.0,
-    this.initAngle = 0,
-  })  : assert(0.0 <= radiusRatio && radiusRatio <= 1.0),
-        assert(0 <= initAngle && initAngle <= 360),
-        super(key: key);
+  &lt;span class=&#34;kd&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RingLayout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
+    &lt;span class=&#34;n&#34;&gt;Key&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
+    &lt;span class=&#34;n&#34;&gt;required&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;children&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
+    &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;reverse&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
+    &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;radiusRatio&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1.0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
+    &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;initAngle&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
+  &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;assert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;0.0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;radiusRatio&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;radiusRatio&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1.0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
+        &lt;span class=&#34;k&#34;&gt;assert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;initAngle&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;initAngle&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;360&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
+        &lt;span class=&#34;k&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nl&#34;&gt;key:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
 
-  @override
-  Widget build(BuildContext context) {
-    return CustomMultiChildLayout(
-      delegate: _RingDelegate(
-          count: children.length,
-          initAngle: initAngle,
-          reverse: reverse,
-          radiusRatio: radiusRatio),
-      children: [
-        for (int i = 0; i < children.length; i++)
-          LayoutId(id: i, child: children[i])
-      ],
-    );
-  }
-}
-
-
-
]]>
+ &lt;span class=&#34;err&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;override&lt;/span&gt; + &lt;span class=&#34;n&#34;&gt;Widget&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;build&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;BuildContext&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; + &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;CustomMultiChildLayout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt; + &lt;span class=&#34;nl&#34;&gt;delegate:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;_RingDelegate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt; + &lt;span class=&#34;nl&#34;&gt;count:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;children&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;length&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; + &lt;span class=&#34;nl&#34;&gt;initAngle:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;initAngle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; + &lt;span class=&#34;nl&#34;&gt;reverse:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;reverse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; + &lt;span class=&#34;nl&#34;&gt;radiusRatio:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;radiusRatio&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; + &lt;span class=&#34;nl&#34;&gt;children:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt; + &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;children&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;length&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; + &lt;span class=&#34;n&#34;&gt;LayoutId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nl&#34;&gt;id:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;child:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;children&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt; + &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; + &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; + &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; +&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; +&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; +&lt;/div&gt; +&lt;/div&gt;]]>
@@ -1033,46 +1033,46 @@ $$

<img src=https://imgoldjii.oss-cn-beijing.aliyuncs.com/doujiaomenmian.jpg width=400 height=400 /> </div> <p>公司园区爆发疫情,被迫居家隔离,心念念外面的豆角焖面,在猪屁的指导下尝试了下,还算成功。</p> - - - -

公司园区爆发疫情,被迫居家隔离,心念念外面的豆角焖面,在猪屁的指导下尝试了下,还算成功。

-

原料

-

主料

-
    -
  • 猪五花肉
  • -
  • 鲜面条
  • -
  • 豆角
  • -
-

配料

-
    -
  • -
  • -
  • 蒜瓣
  • -
  • 生抽
  • -
  • 蚝油
  • -
  • 老抽
  • -
-

原料准备

-
    -
  1. 猪五花肉切片
  2. -
  3. 豆角用手掰断、去掉两头、清洗干净
  4. -
  5. 鲜面条蒸至8分熟
  6. -
  7. 一头蒜拍扁剥皮
  8. -
  9. 调制料汁(两勺生抽、一勺蚝油、半勺老抽、少许盐和糖)
  10. -
-

制作步骤

-
    -
  1. 热锅放油
  2. -
  3. 煸炒五花肉出油
  4. -
  5. 煸炒豆角至变色
  6. -
  7. 添加料汁、加水没过食材
  8. -
  9. 大火煮开后舀出一半汤汁备用
  10. -
  11. 放入面条煮5分钟
  12. -
  13. 放入蒜瓣
  14. -
  15. 备用汤汁浇上去
  16. -
  17. 小火焖10分钟
  18. -
]]>
+ <![CDATA[&lt;div align=&#34;center&#34;&gt; +&lt;img src=https://imgoldjii.oss-cn-beijing.aliyuncs.com/doujiaomenmian.jpg width=400 height=400 /&gt; +&lt;/div&gt; +&lt;p&gt;公司园区爆发疫情,被迫居家隔离,心念念外面的豆角焖面,在猪屁的指导下尝试了下,还算成功。&lt;/p&gt; +&lt;h2 id=&#34;原料&#34;&gt;原料&lt;/h2&gt; +&lt;h3 id=&#34;主料&#34;&gt;主料&lt;/h3&gt; +&lt;ul&gt; +&lt;li&gt;猪五花肉&lt;/li&gt; +&lt;li&gt;鲜面条&lt;/li&gt; +&lt;li&gt;豆角&lt;/li&gt; +&lt;/ul&gt; +&lt;h3 id=&#34;配料&#34;&gt;配料&lt;/h3&gt; +&lt;ul&gt; +&lt;li&gt;盐&lt;/li&gt; +&lt;li&gt;糖&lt;/li&gt; +&lt;li&gt;蒜瓣&lt;/li&gt; +&lt;li&gt;生抽&lt;/li&gt; +&lt;li&gt;蚝油&lt;/li&gt; +&lt;li&gt;老抽&lt;/li&gt; +&lt;/ul&gt; +&lt;h2 id=&#34;原料准备&#34;&gt;原料准备&lt;/h2&gt; +&lt;ol&gt; +&lt;li&gt;猪五花肉切片&lt;/li&gt; +&lt;li&gt;豆角用手掰断、去掉两头、清洗干净&lt;/li&gt; +&lt;li&gt;鲜面条蒸至8分熟&lt;/li&gt; +&lt;li&gt;一头蒜拍扁剥皮&lt;/li&gt; +&lt;li&gt;调制料汁(两勺生抽、一勺蚝油、半勺老抽、少许盐和糖)&lt;/li&gt; +&lt;/ol&gt; +&lt;h2 id=&#34;制作步骤&#34;&gt;制作步骤&lt;/h2&gt; +&lt;ol&gt; +&lt;li&gt;热锅放油&lt;/li&gt; +&lt;li&gt;煸炒五花肉出油&lt;/li&gt; +&lt;li&gt;煸炒豆角至变色&lt;/li&gt; +&lt;li&gt;添加料汁、加水没过食材&lt;/li&gt; +&lt;li&gt;大火煮开后舀出一半汤汁备用&lt;/li&gt; +&lt;li&gt;放入面条煮5分钟&lt;/li&gt; +&lt;li&gt;放入蒜瓣&lt;/li&gt; +&lt;li&gt;备用汤汁浇上去&lt;/li&gt; +&lt;li&gt;小火焖10分钟&lt;/li&gt; +&lt;/ol&gt;]]>
@@ -1082,22 +1082,22 @@ $$

https://folay.top/post/gene/ 初开始听说《自私的基因》这本书,是在 21 年末的时候,那时候刚从小米离职,与新公司约定的入职时间稍晚了些,计划的是来一场说走就走的离职旅行、散散 - 初开始听说《自私的基因》这本书,是在 21 年末的时候,那时候刚从小米离职,与新公司约定的入职时间稍晚了些,计划的是来一场说走就走的离职旅行、散散心,可事与愿违,年底疫情的爆发打乱了所有安排。

-

画地为牢的居家隔离生活总是漫长的,每天都要看几集《圆桌派》消磨时光,其中印象最深刻的是第五季的第十一期,这期的嘉宾是华大基因的 CEO 尹烨,一个理科生、三个文科生围绕 “ 基因 ” 展开了热火朝天的讨论,从 “ 体外胚胎技术 ” 到 “ 人体细胞更新 ”,从 “ 忒修斯之船 ” 到 “ 道金斯的自私的基因 ”,每个话题都充斥着思维碰撞的火花,节目的最后尹烨便推荐了这本书 -《自私的基因》。

-

断断续续几个月读完全书后,受益良多,所以在此简单记录下书中作者的主要观点和自己的一些思考。

-

“ 纵观历史,人类对自己的认识总是不断矮化的。 ”

-

从最初哥白尼提出 “ 日心说 ”,地球是宇宙中心的观念在人们心中崩塌,到达尔文提出 “ 进化论 ”,人们意识到自己是由猴子演变而来的,人类和猴子的差距并不大,再然后弗洛伊德提出 “ 性学三论 ” ,表示人类并没有所谓的自控能力,我们的行为只是受到了原始 “ 力比多 ” 的驱使,最后道金斯在《自私的基因》一书中更进一步,指出人类不过是基因的容器,不过是基因复制自己传宗接代的工具而已。

-

道金斯认为是基因在操纵人,或者说是基因在推动人类复制和传播自身,就像人在开车时,汽车是没有方向感的,是人在操控汽车,是人在选择方向,人类驾驶汽车是为了满足自身高速移动的需求,对于汽车而已,本身是盲目,是无意义的。同样,人类个体的生存也是盲目的、无意义的,不过是体内基因复制和传播自己的工具,存在的意义不过是为了满足基因复制更快、传播更远、更加长寿三个目的而已。

-

“ 明显的利他行为实际上是伪装起来的自私行为。 ”

-

动物养育自己的后代,站在生物个体的角度,这会是一种利他行为,但道金斯认为,这种行为是建立在 基因可以通过养育后代这种利他行为来达成复制和传播自身的目的 的前提下的。所有站在生物个体角度看起来属于利他行为的情况,都是基因自私的产物。

-

对基因来说唯一有意义的事情就是不断的复制自己、传播自己,以便在生物进化这场 “ 战争 ” 中获得更多的优势,获得更多活下来的可能性。

-

在书中看到这些,或多或少会有些失望,对人类失望、对自己失望,人类的诞生是偶然的,也是荒谬的,生命的意义可以说是微不足道的。人类世界里那些崇高而辉煌的舍生取义、视死如归,在基因的客观世界里是多么的不合情理。

-

但正如道金斯在书中写到,人们不能对事实视而不见,也不能因为事实而自暴自弃,书中的观点和结论,都只是对动物生物性的观察,是科学事实的陈述,并不代表道金斯本人的道德观。

-

相反,他认为如果我们想要建立一个人与人之间慷慨大度、无私奉献的社会,那我们就不能指望我们的生物学本性,我们必须设法通过教育,把慷慨大度和利他主义灌输到人们的头脑中去,因为我们生来就是自私的。

-

“ 我们具备足够的力量去抗拒我们那些与生俱来的自私基因。我们也可以抗拒那些灌输到我们脑子里的自私觅母。我们甚至可以讨论如何审慎地培植纯粹的、无私的利他主义,这种利他主义在自然界里是没有立足之地的,在世界整个历史上也是前所未有的。我们是作为基因机器而被建造的,是作为觅母机器而被培养的,但我们具备足够的力量去反对我们的缔造者。在这个世界上,只有我们,我们人类,能够反抗自私的复制基因的暴政。 ”

-

就像尹烨在节目结尾说道:“ 如果人类是一组代码,那我相信人类的代码中有爱 ”。

-

你我,共勉。

-]]>
+ <![CDATA[&lt;p&gt;初开始听说&lt;a href=&#34;https://zh.wikipedia.org/zh-hans/%E8%87%AA%E7%A7%81%E7%9A%84%E5%9F%BA%E5%9B%A0&#34;&gt;《自私的基因》&lt;/a&gt;这本书,是在 21 年末的时候,那时候刚从小米离职,与新公司约定的入职时间稍晚了些,计划的是来一场说走就走的离职旅行、散散心,可事与愿违,年底疫情的爆发打乱了所有安排。&lt;/p&gt; +&lt;p&gt;画地为牢的居家隔离生活总是漫长的,每天都要看几集&lt;a href=&#34;https://baike.baidu.com/item/%E5%9C%86%E6%A1%8C%E6%B4%BE/19543072?fr=aladdin&#34;&gt;《圆桌派》&lt;/a&gt;消磨时光,其中印象最深刻的是第五季的第十一期,这期的嘉宾是华大基因的 CEO 尹烨,一个理科生、三个文科生围绕 “ 基因 ” 展开了热火朝天的讨论,从 “ 体外胚胎技术 ” 到 “ 人体细胞更新 ”,从 “ 忒修斯之船 ” 到 “ 道金斯的自私的基因 ”,每个话题都充斥着思维碰撞的火花,节目的最后尹烨便推荐了这本书 -《自私的基因》。&lt;/p&gt; +&lt;p&gt;断断续续几个月读完全书后,受益良多,所以在此简单记录下书中作者的主要观点和自己的一些思考。&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;“ 纵观历史,人类对自己的认识总是不断矮化的。 ”&lt;/strong&gt;&lt;/p&gt; +&lt;p&gt;从最初哥白尼提出 “ 日心说 ”,地球是宇宙中心的观念在人们心中崩塌,到达尔文提出 “ 进化论 ”,人们意识到自己是由猴子演变而来的,人类和猴子的差距并不大,再然后弗洛伊德提出 “ 性学三论 ” ,表示人类并没有所谓的自控能力,我们的行为只是受到了原始 “ 力比多 ” 的驱使,最后道金斯在《自私的基因》一书中更进一步,指出人类不过是基因的容器,不过是基因复制自己传宗接代的工具而已。&lt;/p&gt; +&lt;p&gt;道金斯认为是基因在操纵人,或者说是基因在推动人类复制和传播自身,就像人在开车时,汽车是没有方向感的,是人在操控汽车,是人在选择方向,人类驾驶汽车是为了满足自身高速移动的需求,对于汽车而已,本身是盲目,是无意义的。同样,人类个体的生存也是盲目的、无意义的,不过是体内基因复制和传播自己的工具,存在的意义不过是为了满足基因复制更快、传播更远、更加长寿三个目的而已。&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;“ 明显的利他行为实际上是伪装起来的自私行为。 ”&lt;/strong&gt;&lt;/p&gt; +&lt;p&gt;动物养育自己的后代,站在生物个体的角度,这会是一种利他行为,但道金斯认为,这种行为是建立在 基因可以通过养育后代这种利他行为来达成复制和传播自身的目的 的前提下的。所有站在生物个体角度看起来属于利他行为的情况,都是基因自私的产物。&lt;/p&gt; +&lt;p&gt;对基因来说唯一有意义的事情就是不断的复制自己、传播自己,以便在生物进化这场 “ 战争 ” 中获得更多的优势,获得更多活下来的可能性。&lt;/p&gt; +&lt;p&gt;在书中看到这些,或多或少会有些失望,对人类失望、对自己失望,人类的诞生是偶然的,也是荒谬的,生命的意义可以说是微不足道的。人类世界里那些崇高而辉煌的舍生取义、视死如归,在基因的客观世界里是多么的不合情理。&lt;/p&gt; +&lt;p&gt;但正如道金斯在书中写到,人们不能对事实视而不见,也不能因为事实而自暴自弃,书中的观点和结论,都只是对动物生物性的观察,是科学事实的陈述,并不代表道金斯本人的道德观。&lt;/p&gt; +&lt;p&gt;相反,他认为如果我们想要建立一个人与人之间慷慨大度、无私奉献的社会,那我们就不能指望我们的生物学本性,我们必须设法通过教育,把慷慨大度和利他主义灌输到人们的头脑中去,因为我们生来就是自私的。&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;“ 我们具备足够的力量去抗拒我们那些与生俱来的自私基因。我们也可以抗拒那些灌输到我们脑子里的自私觅母。我们甚至可以讨论如何审慎地培植纯粹的、无私的利他主义,这种利他主义在自然界里是没有立足之地的,在世界整个历史上也是前所未有的。我们是作为基因机器而被建造的,是作为觅母机器而被培养的,但我们具备足够的力量去反对我们的缔造者。在这个世界上,只有我们,我们人类,能够反抗自私的复制基因的暴政。 ”&lt;/strong&gt;&lt;/p&gt; +&lt;p&gt;就像尹烨在节目结尾说道:“ 如果人类是一组代码,那我相信人类的代码中有爱 ”。&lt;/p&gt; +&lt;p&gt;你我,共勉。&lt;/p&gt; +]]>
@@ -1107,99 +1107,99 @@ $$

https://folay.top/post/hugo_mathJax/ Hugo 默认是不支持显示 LaTeX 风格的数学表达式的,Markdown 中的数学表达式语法在 Hugo 中默认并不会被识别。 在 Hugo 中引入 MathJax 即可解决该问题,MathJax - Hugo 默认是不支持显示 LaTeX 风格的数学表达式的,Markdown 中的数学表达式语法在 Hugo 中默认并不会被识别。

-

在 Hugo 中引入 MathJax 即可解决该问题,MathJax 是一个适用于所有浏览器的 JavaScript 数学显示引擎。

-

引入方法很简单,在文章的模版的 HTML 的中添加以下内容即可。

-
- -
-
 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
-10
-11
-12
-13
-14
-15
-16
-17
-18
-19
-20
-21
-22
-23
-24
-25
-26
-27
-28
-29
-30
-31
-32
-33
-34
-35
-
-
<script type="text/javascript"
-        async
-        src="https://cdn.bootcss.com/mathjax/2.7.3/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
-MathJax.Hub.Config({
-  tex2jax: {
-    inlineMath: [['$','$'], ['\\(','\\)']],
-    displayMath: [['$$','$$'], ['\[\[','\]\]']],
-    processEscapes: true,
-    processEnvironments: true,
-    skipTags: ['script', 'noscript', 'style', 'textarea', 'pre'],
-    TeX: { equationNumbers: { autoNumber: "AMS" },
-         extensions: ["AMSmath.js", "AMSsymbols.js"] }
-  }
-});
+      <![CDATA[&lt;p&gt;Hugo 默认是不支持显示 LaTeX 风格的数学表达式的,Markdown 中的数学表达式语法在 Hugo 中默认并不会被识别。&lt;/p&gt;
+&lt;p&gt;在 Hugo 中引入 MathJax 即可解决该问题,&lt;a href=&#34;https://www.mathjax.org&#34;&gt;MathJax&lt;/a&gt; 是一个适用于所有浏览器的 JavaScript 数学显示引擎。&lt;/p&gt;
+&lt;p&gt;引入方法很简单,在文章的模版的 HTML 的中添加以下内容即可。&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;script&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;text/javascript&amp;#34;&lt;/span&gt;
+        &lt;span class=&#34;nx&#34;&gt;async&lt;/span&gt;
+        &lt;span class=&#34;nx&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;https://cdn.bootcss.com/mathjax/2.7.3/MathJax.js?config=TeX-AMS-MML_HTMLorMML&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;
+&lt;span class=&#34;nx&#34;&gt;MathJax&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Hub&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Config&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
+  &lt;span class=&#34;nx&#34;&gt;tex2jax&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
+    &lt;span class=&#34;nx&#34;&gt;inlineMath&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;$&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;$&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;\\(&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;\\)&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]],&lt;/span&gt;
+    &lt;span class=&#34;nx&#34;&gt;displayMath&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;$$&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;$$&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;\[\[&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;\]\]&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]],&lt;/span&gt;
+    &lt;span class=&#34;nx&#34;&gt;processEscapes&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
+    &lt;span class=&#34;nx&#34;&gt;processEnvironments&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
+    &lt;span class=&#34;nx&#34;&gt;skipTags&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;script&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;noscript&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;style&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;textarea&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;pre&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
+    &lt;span class=&#34;nx&#34;&gt;TeX&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;equationNumbers&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;autoNumber&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;AMS&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
+         &lt;span class=&#34;nx&#34;&gt;extensions&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;AMSmath.js&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;AMSsymbols.js&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
+  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
+&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
 
-MathJax.Hub.Queue(function() {
-    // Fix <code> tags after MathJax finishes running. This is a
-    // hack to overcome a shortcoming of Markdown. Discussion at
-    // https://github.com/mojombo/jekyll/issues/199
-    var all = MathJax.Hub.getAllJax(), i;
-    for(i = 0; i < all.length; i += 1) {
-        all[i].SourceElement().parentNode.className += ' has-jax';
-    }
-});
-</script>
+&lt;span class=&#34;nx&#34;&gt;MathJax&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Hub&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Queue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
+    &lt;span class=&#34;c1&#34;&gt;// Fix &amp;lt;code&amp;gt; tags after MathJax finishes running. This is a
+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// hack to overcome a shortcoming of Markdown. Discussion at
+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// https://github.com/mojombo/jekyll/issues/199
+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;all&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;MathJax&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Hub&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getAllJax&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
+    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;all&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;length&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
+        &lt;span class=&#34;nx&#34;&gt;all&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;].&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;SourceElement&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;parentNode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;className&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39; has-jax&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
+    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
+&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;/script&amp;gt;&lt;/span&gt;
 
-<style>
-code.has-jax {
-    font: inherit;
-    font-size: 100%;
-    background: inherit;
-    border: inherit;
-    color: #515151;
-}
-</style>
-
-
-

要注意的是需要确保该 HTML 必定被 Hugo 框架加载,我这里是添加到了 layouts/partials/ 目录下的 footer.html文件中,因为我确定该文件必定包含在网站的每个页面中。

-

实际上单纯引入 MathJax 并不需要如此多行代码,多余的部分是为了解决 Markdown 和 LaTeX 中对于划线 _ 的不同定义带来的问题,详情可以参考 这篇文章

-

$$ +&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;style&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; +&lt;span class=&#34;nx&#34;&gt;code&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;has&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;jax&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; + &lt;span class=&#34;nx&#34;&gt;font&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;inherit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; + &lt;span class=&#34;nx&#34;&gt;font&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;%&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; + &lt;span class=&#34;nx&#34;&gt;background&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;inherit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; + &lt;span class=&#34;nx&#34;&gt;border&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;inherit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; + &lt;span class=&#34;nx&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;515151&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; +&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; +&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;/style&amp;gt;&lt;/span&gt; +&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; +&lt;/div&gt; +&lt;/div&gt;&lt;p&gt;要注意的是需要确保该 HTML 必定被 Hugo 框架加载,我这里是添加到了 &lt;code&gt;layouts/partials/&lt;/code&gt; 目录下的 &lt;code&gt;footer.html&lt;/code&gt;文件中,因为我确定该文件必定包含在网站的每个页面中。&lt;/p&gt; +&lt;p&gt;实际上单纯引入 MathJax 并不需要如此多行代码,多余的部分是为了解决 Markdown 和 LaTeX 中对于划线 _ 的不同定义带来的问题,详情可以参考 &lt;a href=&#34;https://www.gohugo.org/doc/tutorials/mathjax/&#34;&gt;这篇文章&lt;/a&gt; 。&lt;/p&gt; +&lt;p&gt;$$ D(x) = \begin{cases} -\lim\limits_{x \to 0} \frac{a^x}{b+c}, & x<3 \
-\pi, & x=3 \
-\int_a^{3b}x_{ij}+e^2 \mathrm{d}x,& x>3 \
+\lim\limits_{x \to 0} \frac{a^x}{b+c}, &amp;amp; x&amp;lt;3 \&lt;br&gt; +\pi, &amp;amp; x=3 \&lt;br&gt; +\int_a^{3b}x_{ij}+e^2 \mathrm{d}x,&amp;amp; x&amp;gt;3 \&lt;br&gt; \end{cases} -$$

-

$$ -\lim_{x \to \infty} x^2_{22} - \int_{1}^{5}x\mathrm{d}x + \sum_{n=1}^{20} n^{2} = \prod_{j=1}^{3} y_{j} + \lim_{x \to -2} \frac{x-2}{x} -$$

-

好了,开心的在 Hugo 中使用 LaTeX 吧。

-]]>
+$$&lt;/p&gt; +&lt;p&gt;$$ +\lim_{x \to \infty} x^2_{22} - \int_{1}^{5}x\mathrm{d}x + \sum_{n=1}^{20} n^{2} = \prod_{j=1}^{3} y_{j} + \lim_{x \to -2} \frac{x-2}{x} +$$&lt;/p&gt; +&lt;p&gt;好了,开心的在 Hugo 中使用 LaTeX 吧。&lt;/p&gt; +]]>
@@ -1209,158 +1209,158 @@ $$

https://folay.top/post/m3u8/ 为了方便理解,会按照“流媒体传输协议”、“HLS”、“M3U8”的顺序来介绍。 三者关系: HLS是一种流媒体传输协议 M3U8是HLS传输内容中 - 为了方便理解,会按照“流媒体传输协议”、“HLS”、“M3U8”的顺序来介绍。

-

三者关系:

-
    -
  • HLS是一种流媒体传输协议
  • -
  • M3U8是HLS传输内容中的一部分
  • -
-

流媒体传输协议

-

常见的流媒体传输协议

-

流媒体就是以数据流的方式,实时发布音频、视频多媒体内容的媒体形式,关键技术在于流式传输

-

流媒体传输协议就是用来定义如何流式传输的,设计、制定了流媒体服务器和客户端通讯的方式。

-

主流的流媒体传输协议:

-
    -
  • RTMP(Real Time Protocol):基于 TCP 的 FLV 分块 message 传输协议,用于 Flash 客户端。
  • -
  • HTTP-FLV:基于 HTTP 长连接的 FLV 分块 tag 传输协议,可用于点播和直播场景。
  • -
  • HLS(HTTP Live Streaming):基于 HTTP,由 Apple 推出的 MP4 分片传输协议,可以用于点播、直播,每次下载一次分片都需要发生一次 HTTP 请求。
  • -
-
-

本文只详细介绍 HLS,不涉及 RTMP 与 RTSP

-
-

流媒体加密原理

-

大多数流媒体传输协议都可以分为拆分、加密两部分。

-

拆分是 将完整的视频流拆分为连续的视频片段,不同的传输协议的区别在于拆分片段的大小、视频容器的格式不同。

-

加密是 对每段视频片段进行加密,使用对称加密算法,在服务端加密,在客户端解密,且通过一定手段限制解密密钥的获取。

-
-

一般使用 AES 加密算法

-
-

为什么是对称加密?

-

对称加密效率相对较高,非对称加密效率相对较低,但是更安全。流媒体场景对实时性的要求很高,而且数据量也很大,所以选用效率相对较高的对称加密算法。

-

类似的场景还有很多,比如 HTTPS 的请求过程,内容传输为了效率选用对称加密(TLS),证书校验为了安全选用非对称加密(SSL)。

-

HLS

-

HLS 全称 HTTP Live Streaming, 是由 Apple 提出的基于 HTTP 的流媒体传输协议,用于实时音视频流的传输,目前已被广泛应用与视频点播、直播场景。

-
-

参考资料:HTTP Live Streaming Document

-
-

工作原理

-

完整的 HLS 架构可以划分为 3 个部分:

-
    -
  • 服务器 Server:负责视频流的编码、切割为连续的 MPEG-TS 格式的视频片段,并提供配套的 M3U8 类型的媒体列表文件和索引文件。
  • -
  • 分发组件 CDN:由标准的网络服务器组成,负责接收客户端的请求并分发资源。
  • -
  • 客户端 Client:先下载 m3u8 索引文件,根据带宽等字段选择合适的 m3u8 媒体播放列表文件下载,按顺序下载列表中的所有 ts 视频片段文件。
  • -
-

完整的 HLS 的过程可以参考下图:

-

-

大体可以划分为 6 个阶段:

-
    -
  1. 采集媒体源
  2. -
  3. 媒体编码器(Media encoder) 对媒体源进行编码
  4. -
  5. 编码后以MPEG-2的传输串形式传递给切片器
  6. -
  7. 切片器(Steam Segmenter)将媒体切割为若干 Media Segment,并创建配套的媒体列表文件 Media Playlist 以及索引文件 Master Platlist
  8. -
  9. 上传:将资源上传到 HTTP 服务器。
  10. -
  11. 播放:客户端请求播放。
  12. -
-

组成结构

-

经过上面第 4 步骤的加工可以形成完整的结构,由 Master PlaylistMedia PlaylistMedia Segment 构成,关系结构如图。

-

-

完整的 HLS 结构由两部分组成:

-
    -
  • m3u8 类型的 Master Playlist 文件:其中会提供若干根据带宽等字段区分的 Media Playlist 的请求链接。
  • -
  • m3u8 类型的 Media Playlist 文件:其中会有视频的基本信息和若干 Media Segment 的请求链接,这些片段就组成了完整的视频。
  • -
-

Media Segment 就是单纯的 ts 格式的视频文件,并无任何描述信息,可以单独使用播放器进行播放。

-
-

M3U8 是 Unicode 版本的 M3U,8 代表使用的是 UTF-8 编码,M3U 和 M3U8 都是多媒体列表的文件格式。

-
-

M3U8

-

M3U8 描述文件中由各种描述字段构成,下面解释部分主要字段的含义。

-

我在网上随便找的一个 m3u8 视频的链接:https://mgtv-com.jjyl12349.com/20210519/fXE0kuJ7/index.m3u8

-

请求该链接的返回结果为一个 m3u8 文件,也就是 Master Playlist 文件。

-

Master Playlist

-
- -
-
1
-2
-3
-4
-5
-6
-7
-
-
#EXTM3U
-#EXT-X-STREAM-INF:BANDWIDTH=150000,RESOLUTION=416x234
-/20210519/fXE0kuJ7/150kb/hls/index.m3u8
-#EXT-X-STREAM-INF:BANDWIDTH=150000,RESOLUTION=416x234
-/20210519/fXE0kuJ7/150kb/hls/index.m3u8
-#EXT-X-STREAM-INF:BANDWIDTH=1000000,RESOLUTION=1280x720
-/20210519/fXE0kuJ7/1000kb/hls/index.m3u8
-
-
-

字节解释

-
    -
  • EXTM3U:表示该文件为m3u8文件,每个M3U文件都是以EXTM3U开头
  • -
  • EXT-X-STREAM-INF:表示一个备份源,并提供备份源的相关信息 -
      -
    • BANDWIDTH:表示每秒传输的比特数,即带宽
    • -
    • RESOLUTION:表示备份源的最佳像素方案
    • -
    -
  • -
-

我们根据 BANDWIDTH、RESOLUTION 等信息选取合适的 Media Playlist 的请求链接,并将链接与视频链接的域名结合,即可得到完整的链接。

-

比如,BANDWIDTH 为 1000kb、RESOLUTION 为 1280x720 的备用源的请求链接为:https://mgtv-com.jjyl12349.com/20210519/fXE0kuJ7/1000kb/hls/index.m3u8

-

请求该链接的返回结果也为一个 m3u8 文件,也就是 Media Playlist 文件。

-

Media Playlist

-
- -
-
 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
-10
-11
-12
-13
-14
-
-
#EXTM3U
-#EXT-X-VERSION:3
-#EXT-X-TARGETDURATION:6
-#EXT-X-PLAYLIST-TYPE:VOD
-#EXT-X-MEDIA-SEQUENCE:0
-#EXT-X-KEY:METHOD=AES-128,URI="https://mgtv-com.ycshengwang.com/20210519/fXE0kuJ7/1000kb/hls/key.key"
-#EXTINF:3,
-https://mgtv-com.ycshengwang.com/20210519/fXE0kuJ7/1000kb/hls/mDHy0Stk.ts
-#EXTINF:3,
-https://mgtv-com.ycshengwang.com/20210519/fXE0kuJ7/1000kb/hls/FWZjOCHy.ts
-...
-...
-...
-#EXT-X-ENDLIST
-
-
-

字节解释

-
    -
  • EXT-X-VERSION:表示 HLS 协议版本号
  • -
  • EXT-X-TARGETDURATION:表示 ts 视频片段允许最大的时长
  • -
  • EXT-X-PLAYLIST-TYPE:表示流媒体类型
  • -
  • EXT-X-MEDIA-SEQUENCE:表示播放列表第一个 ts 视频片段文件的序列号
  • -
  • EXT-X-KEY:表示 ts 视频文件的加密信息 -
      -
    • METHOD:加密方法,可选 NONEAES-128SAMPLE-AES
    • -
    • URI:密钥路径
    • -
    -
  • -
  • EXTINF:表示下面 url 对应的 ts 视频片段的时长
  • -
-]]>
+ <![CDATA[&lt;p&gt;为了方便理解,会按照“流媒体传输协议”、“HLS”、“M3U8”的顺序来介绍。&lt;/p&gt; +&lt;p&gt;三者关系:&lt;/p&gt; +&lt;ul&gt; +&lt;li&gt;HLS是一种流媒体传输协议&lt;/li&gt; +&lt;li&gt;M3U8是HLS传输内容中的一部分&lt;/li&gt; +&lt;/ul&gt; +&lt;h1 id=&#34;流媒体传输协议&#34;&gt;流媒体传输协议&lt;/h1&gt; +&lt;h2 id=&#34;常见的流媒体传输协议&#34;&gt;常见的流媒体传输协议&lt;/h2&gt; +&lt;p&gt;流媒体就是以数据流的方式,实时发布音频、视频多媒体内容的媒体形式,关键技术在于&lt;code&gt;流式传输&lt;/code&gt;。&lt;/p&gt; +&lt;p&gt;流媒体传输协议就是用来定义如何流式传输的,设计、制定了流媒体服务器和客户端通讯的方式。&lt;/p&gt; +&lt;p&gt;主流的流媒体传输协议:&lt;/p&gt; +&lt;ul&gt; +&lt;li&gt;RTMP(Real Time Protocol):基于 TCP 的 FLV 分块 message 传输协议,用于 Flash 客户端。&lt;/li&gt; +&lt;li&gt;HTTP-FLV:基于 HTTP 长连接的 FLV 分块 tag 传输协议,可用于点播和直播场景。&lt;/li&gt; +&lt;li&gt;HLS(HTTP Live Streaming):基于 HTTP,由 Apple 推出的 MP4 分片传输协议,可以用于点播、直播,每次下载一次分片都需要发生一次 HTTP 请求。&lt;/li&gt; +&lt;/ul&gt; +&lt;blockquote&gt; +&lt;p&gt;本文只详细介绍 HLS,不涉及 RTMP 与 RTSP&lt;/p&gt; +&lt;/blockquote&gt; +&lt;h2 id=&#34;流媒体加密原理&#34;&gt;流媒体加密原理&lt;/h2&gt; +&lt;p&gt;大多数流媒体传输协议都可以分为拆分、加密两部分。&lt;/p&gt; +&lt;p&gt;拆分是 将完整的视频流拆分为连续的视频片段,不同的传输协议的区别在于拆分片段的大小、视频容器的格式不同。&lt;/p&gt; +&lt;p&gt;加密是 对每段视频片段进行加密,使用对称加密算法,在服务端加密,在客户端解密,且通过一定手段限制解密密钥的获取。&lt;/p&gt; +&lt;blockquote&gt; +&lt;p&gt;一般使用 AES 加密算法&lt;/p&gt; +&lt;/blockquote&gt; +&lt;h2 id=&#34;为什么是对称加密&#34;&gt;为什么是对称加密?&lt;/h2&gt; +&lt;p&gt;对称加密效率相对较高,非对称加密效率相对较低,但是更安全。流媒体场景对实时性的要求很高,而且数据量也很大,所以选用效率相对较高的对称加密算法。&lt;/p&gt; +&lt;p&gt;类似的场景还有很多,比如 HTTPS 的请求过程,内容传输为了效率选用对称加密(TLS),证书校验为了安全选用非对称加密(SSL)。&lt;/p&gt; +&lt;h1 id=&#34;hls&#34;&gt;HLS&lt;/h1&gt; +&lt;p&gt;HLS 全称 HTTP Live Streaming, 是由 Apple 提出的基于 HTTP 的流媒体传输协议,用于实时音视频流的传输,目前已被广泛应用与视频点播、直播场景。&lt;/p&gt; +&lt;blockquote&gt; +&lt;p&gt;参考资料:&lt;a href=&#34;https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/StreamingMediaGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008332-CH1-SW1&#34;&gt;HTTP Live Streaming Document&lt;/a&gt;&lt;/p&gt; +&lt;/blockquote&gt; +&lt;h2 id=&#34;工作原理&#34;&gt;工作原理&lt;/h2&gt; +&lt;p&gt;完整的 HLS 架构可以划分为 3 个部分:&lt;/p&gt; +&lt;ul&gt; +&lt;li&gt;服务器 Server:负责视频流的编码、切割为连续的 MPEG-TS 格式的视频片段,并提供配套的 M3U8 类型的媒体列表文件和索引文件。&lt;/li&gt; +&lt;li&gt;分发组件 CDN:由标准的网络服务器组成,负责接收客户端的请求并分发资源。&lt;/li&gt; +&lt;li&gt;客户端 Client:先下载 m3u8 索引文件,根据带宽等字段选择合适的 m3u8 媒体播放列表文件下载,按顺序下载列表中的所有 ts 视频片段文件。&lt;/li&gt; +&lt;/ul&gt; +&lt;p&gt;完整的 HLS 的&lt;strong&gt;过程&lt;/strong&gt;可以参考下图:&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://i.loli.net/2021/09/16/6Bp37ZFcPfqbAvy.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt; +&lt;p&gt;大体可以划分为 6 个阶段:&lt;/p&gt; +&lt;ol&gt; +&lt;li&gt;采集媒体源&lt;/li&gt; +&lt;li&gt;媒体编码器(Media encoder) 对媒体源进行编码&lt;/li&gt; +&lt;li&gt;编码后以MPEG-2的传输串形式传递给切片器&lt;/li&gt; +&lt;li&gt;切片器(Steam Segmenter)将媒体切割为若干 &lt;code&gt;Media Segment&lt;/code&gt;,并创建配套的媒体列表文件 &lt;code&gt;Media Playlist&lt;/code&gt; 以及索引文件 &lt;code&gt;Master Platlist&lt;/code&gt;&lt;/li&gt; +&lt;li&gt;上传:将资源上传到 HTTP 服务器。&lt;/li&gt; +&lt;li&gt;播放:客户端请求播放。&lt;/li&gt; +&lt;/ol&gt; +&lt;h2 id=&#34;组成结构&#34;&gt;组成结构&lt;/h2&gt; +&lt;p&gt;经过上面第 4 步骤的加工可以形成完整的结构,由 &lt;code&gt;Master Playlist&lt;/code&gt;、&lt;code&gt;Media Playlist&lt;/code&gt;、&lt;code&gt;Media Segment&lt;/code&gt; 构成,关系结构如图。&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://i.loli.net/2021/09/16/F7fcQpGuyiNUYH8.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt; +&lt;p&gt;完整的 HLS 结构由两部分组成:&lt;/p&gt; +&lt;ul&gt; +&lt;li&gt;m3u8 类型的 Master Playlist 文件:其中会提供若干根据带宽等字段区分的 Media Playlist 的请求链接。&lt;/li&gt; +&lt;li&gt;m3u8 类型的 Media Playlist 文件:其中会有视频的基本信息和若干 Media Segment 的请求链接,这些片段就组成了完整的视频。&lt;/li&gt; +&lt;/ul&gt; +&lt;p&gt;Media Segment 就是单纯的 ts 格式的视频文件,并无任何描述信息,可以单独使用播放器进行播放。&lt;/p&gt; +&lt;blockquote&gt; +&lt;p&gt;M3U8 是 Unicode 版本的 M3U,8 代表使用的是 UTF-8 编码,M3U 和 M3U8 都是多媒体列表的文件格式。&lt;/p&gt; +&lt;/blockquote&gt; +&lt;h1 id=&#34;m3u8&#34;&gt;M3U8&lt;/h1&gt; +&lt;p&gt;M3U8 描述文件中由各种描述字段构成,下面解释部分主要字段的含义。&lt;/p&gt; +&lt;p&gt;我在网上随便找的一个 m3u8 视频的链接:&lt;code&gt;https://mgtv-com.jjyl12349.com/20210519/fXE0kuJ7/index.m3u8&lt;/code&gt;&lt;/p&gt; +&lt;p&gt;请求该链接的返回结果为一个 m3u8 文件,也就是 Master Playlist 文件。&lt;/p&gt; +&lt;h2 id=&#34;master-playlist&#34;&gt;Master Playlist&lt;/h2&gt; +&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt; +&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt; +&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7 +&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt; +&lt;td class=&#34;lntd&#34;&gt; +&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;EXTM3U&lt;/span&gt; +&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;EXT&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;X&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;STREAM&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;INF&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;BANDWIDTH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;150000&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;RESOLUTION&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;416x234&lt;/span&gt; +&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;20210519&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fXE0kuJ7&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;150kb&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hls&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;index&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;m3u8&lt;/span&gt; +&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;EXT&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;X&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;STREAM&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;INF&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;BANDWIDTH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;150000&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;RESOLUTION&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;416x234&lt;/span&gt; +&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;20210519&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fXE0kuJ7&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;150kb&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hls&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;index&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;m3u8&lt;/span&gt; +&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;EXT&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;X&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;STREAM&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;INF&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;BANDWIDTH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;1000000&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;RESOLUTION&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;1280x720&lt;/span&gt; +&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;20210519&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fXE0kuJ7&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;1000kb&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hls&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;index&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;m3u8&lt;/span&gt; +&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; +&lt;/div&gt; +&lt;/div&gt;&lt;p&gt;&lt;strong&gt;字节解释&lt;/strong&gt;:&lt;/p&gt; +&lt;ul&gt; +&lt;li&gt;EXTM3U:表示该文件为m3u8文件,每个M3U文件都是以EXTM3U开头&lt;/li&gt; +&lt;li&gt;EXT-X-STREAM-INF:表示一个备份源,并提供备份源的相关信息 +&lt;ul&gt; +&lt;li&gt;BANDWIDTH:表示每秒传输的比特数,即带宽&lt;/li&gt; +&lt;li&gt;RESOLUTION:表示备份源的最佳像素方案&lt;/li&gt; +&lt;/ul&gt; +&lt;/li&gt; +&lt;/ul&gt; +&lt;p&gt;我们根据 BANDWIDTH、RESOLUTION 等信息选取合适的 Media Playlist 的请求链接,并将链接与视频链接的域名结合,即可得到完整的链接。&lt;/p&gt; +&lt;p&gt;比如,BANDWIDTH 为 1000kb、RESOLUTION 为 1280x720 的备用源的请求链接为:&lt;code&gt;https://mgtv-com.jjyl12349.com/20210519/fXE0kuJ7/1000kb/hls/index.m3u8&lt;/code&gt;&lt;/p&gt; +&lt;p&gt;请求该链接的返回结果也为一个 m3u8 文件,也就是 Media Playlist 文件。&lt;/p&gt; +&lt;h2 id=&#34;media-playlist&#34;&gt;Media Playlist&lt;/h2&gt; +&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt; +&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt; +&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14 +&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt; +&lt;td class=&#34;lntd&#34;&gt; +&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;EXTM3U&lt;/span&gt; +&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;EXT&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;X&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;VERSION&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;3&lt;/span&gt; +&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;EXT&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;X&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TARGETDURATION&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;6&lt;/span&gt; +&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;EXT&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;X&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PLAYLIST&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TYPE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;VOD&lt;/span&gt; +&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;EXT&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;X&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MEDIA&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SEQUENCE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;0&lt;/span&gt; +&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;EXT&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;X&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;METHOD&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AES&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;128&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;URI&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;https://mgtv-com.ycshengwang.com/20210519/fXE0kuJ7/1000kb/hls/key.key&amp;#34;&lt;/span&gt; +&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;EXTINF&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; +&lt;span class=&#34;nl&#34;&gt;https:&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;//mgtv-com.ycshengwang.com/20210519/fXE0kuJ7/1000kb/hls/mDHy0Stk.ts +&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;EXTINF&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; +&lt;span class=&#34;nl&#34;&gt;https:&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;//mgtv-com.ycshengwang.com/20210519/fXE0kuJ7/1000kb/hls/FWZjOCHy.ts +&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt; +&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt; +&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt; +&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;EXT&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;X&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ENDLIST&lt;/span&gt; +&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; +&lt;/div&gt; +&lt;/div&gt;&lt;p&gt;&lt;strong&gt;字节解释&lt;/strong&gt;:&lt;/p&gt; +&lt;ul&gt; +&lt;li&gt;EXT-X-VERSION:表示 HLS 协议版本号&lt;/li&gt; +&lt;li&gt;EXT-X-TARGETDURATION:表示 ts 视频片段允许最大的时长&lt;/li&gt; +&lt;li&gt;EXT-X-PLAYLIST-TYPE:表示流媒体类型&lt;/li&gt; +&lt;li&gt;EXT-X-MEDIA-SEQUENCE:表示播放列表第一个 ts 视频片段文件的序列号&lt;/li&gt; +&lt;li&gt;EXT-X-KEY:表示 ts 视频文件的加密信息 +&lt;ul&gt; +&lt;li&gt;METHOD:加密方法,可选 &lt;code&gt;NONE&lt;/code&gt;、&lt;code&gt;AES-128&lt;/code&gt;、&lt;code&gt;SAMPLE-AES&lt;/code&gt;&lt;/li&gt; +&lt;li&gt;URI:密钥路径&lt;/li&gt; +&lt;/ul&gt; +&lt;/li&gt; +&lt;li&gt;EXTINF:表示下面 url 对应的 ts 视频片段的时长&lt;/li&gt; +&lt;/ul&gt; +]]>
@@ -1370,74 +1370,74 @@ $$

https://folay.top/post/algorithm/ 对于每一位求职的 Coder 来说,在 LeetCode 上熟练刷数据结构和算法题的重要性不言而喻,甚至某一程度上直接决定了面试的成败。 最近有在准备跳槽,所以重新把 剑指O - 对于每一位求职的 Coder 来说,在 LeetCode 上熟练刷数据结构和算法题的重要性不言而喻,甚至某一程度上直接决定了面试的成败。

-

最近有在准备跳槽,所以重新把 剑指Offer 的题目写了一遍,并每道题目都整理了对应的讲解文章,希望对您有所帮助。

- -]]>
+ <![CDATA[&lt;p&gt;对于每一位求职的 Coder 来说,在 LeetCode 上熟练刷数据结构和算法题的重要性不言而喻,甚至某一程度上直接决定了面试的成败。&lt;/p&gt; +&lt;p&gt;最近有在准备跳槽,所以重新把 &lt;a href=&#34;https://leetcode-cn.com/study-plan/lcof/&#34;&gt;剑指Offer&lt;/a&gt; 的题目写了一遍,并每道题目都整理了对应的讲解文章,希望对您有所帮助。&lt;/p&gt; +&lt;ul&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/anox4u&#34;&gt;剑指 Offer 03. 数组中重复的数字&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/sdmmhc&#34;&gt;剑指 Offer 04. 二维数组中的查找&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/ng33ly&#34;&gt;剑指 Offer 05. 替换空格&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/kl5l4w&#34;&gt;剑指 Offer 06. 从尾到头打印链表&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/iip4rb&#34;&gt;剑指 Offer 07. 重建二叉树&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/dwgt9d&#34;&gt;剑指 Offer 09. 用两个栈实现队列&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/dxsfl8&#34;&gt;剑指 Offer 10- I. 斐波那契数列&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/ymqkcd&#34;&gt;剑指 Offer 10- II. 青蛙跳台阶问题&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/kpylzm&#34;&gt;剑指 Offer 11. 旋转数组的最小数字&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/dd632h&#34;&gt;剑指 Offer 12. 矩阵中的路径&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/ag7286&#34;&gt;剑指 Offer 13. 机器人的运动范围&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/gr8e4r&#34;&gt;剑指 Offer 14- I. 剪绳子&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/qhl610&#34;&gt;剑指 Offer 14- II. 剪绳子 II&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/riidbe&#34;&gt;剑指 Offer 15. 二进制中1的个数&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/fq4h36&#34;&gt;剑指 Offer 16. 数值的整数次方&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/qgc7mz&#34;&gt;剑指 Offer 17. 打印从1到最大的n位数&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/do8gy1&#34;&gt;剑指 Offer 18. 删除链表的节点&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/iu8mgk&#34;&gt;剑指 Offer 21. 调整数组顺序使奇数位于偶数前面&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/sm5sza&#34;&gt;剑指 Offer 22. 链表中倒数第k个节点&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/dkxkb3&#34;&gt;剑指 Offer 24. 反转链表&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/xdzhgg&#34;&gt;剑指 Offer 25. 合并两个排序的链表&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/gdpguv&#34;&gt;剑指 Offer 26. 树的子结构&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/iu12l1&#34;&gt;剑指 Offer 27. 二叉树的镜像&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/iw54ns&#34;&gt;剑指 Offer 28. 对称的二叉树&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/pnpfu2&#34;&gt;剑指 Offer 29. 顺时针打印矩阵&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/fc9ucp&#34;&gt;剑指 Offer 30. 包含min函数的栈&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/rkv7rc&#34;&gt;剑指 Offer 31. 栈的压入、弹出序列&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/qe7nm7&#34;&gt;剑指 Offer 32 - I. 从上到下打印二叉树&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/esgcos&#34;&gt;剑指 Offer 32 - II. 从上到下打印二叉树 II&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/rc5wut&#34;&gt;剑指 Offer 32 - III. 从上到下打印二叉树 III&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/ml2bgf&#34;&gt;剑指 Offer 33. 二叉搜索树的后序遍历序列&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/ar7myv&#34;&gt;剑指 Offer 34. 二叉树中和为某一值的路径&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/he3wwe&#34;&gt;剑指 Offer 35. 复杂链表的复制&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/ewferc&#34;&gt;剑指 Offer 36. 二叉搜索树与双向链表&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/he1h9d&#34;&gt;剑指 Offer 38. 字符串的排列&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/co4xrt&#34;&gt;剑指 Offer 39. 数组中出现次数超过一半的数字&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/ngyxmw&#34;&gt;剑指 Offer 40. 最小的k个数&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/gtthvb&#34;&gt;剑指 Offer 42. 连续子数组的最大和&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/dbw1qi&#34;&gt;剑指 Offer 46. 把数字翻译成字符串&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/edri5d&#34;&gt;剑指 Offer 47. 礼物的最大价值&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/nzv1m8&#34;&gt;剑指 Offer 48. 最长不含重复字符的子字符串&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/yepxt0&#34;&gt;剑指 Offer 49. 丑数&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/xq6n9b&#34;&gt;剑指 Offer 50. 第一个只出现一次的字符&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/bcvfcl&#34;&gt;剑指 Offer 52. 两个链表的第一个公共节点&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/xm8w4c&#34;&gt;剑指 Offer 53 - I. 在排序数组中查找数字 I&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/ke562e&#34;&gt;剑指 Offer 53 - II. 0~n-1中缺失的数字&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/lh1f1a&#34;&gt;剑指 Offer 54. 二叉搜索树的第k大节点&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/zuf9kd&#34;&gt;剑指 Offer 55 - I. 二叉树的深度&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/mmxu37&#34;&gt;剑指 Offer 55 - II. 平衡二叉树&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/wbma5w&#34;&gt;剑指 Offer 56 - I. 数组中数字出现的次数&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/arglgv&#34;&gt;剑指 Offer 56 - II. 数组中数字出现的次数 II&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/pl9bt8&#34;&gt;剑指 Offer 57. 和为s的两个数字&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/hrkhzg&#34;&gt;剑指 Offer 57 - II. 和为s的连续正数序列&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/ziwtzv&#34;&gt;剑指 Offer 58 - I. 翻转单词顺序&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/lzt4dg&#34;&gt;剑指 Offer 58 - II. 左旋转字符串&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/dzdmcm&#34;&gt;剑指 Offer 59 - I. 滑动窗口的最大值&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/bw5huk&#34;&gt;剑指 Offer 59 - II. 队列的最大值&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/vzyygh&#34;&gt;剑指 Offer 60. n个骰子的点数&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/glu1kz&#34;&gt;剑指 Offer 61. 扑克牌中的顺子&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/rvc25h&#34;&gt;剑指 Offer 62. 圆圈中最后剩下的数字&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/yfgaqk&#34;&gt;剑指 Offer 63. 股票的最大利润&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/myfeqh&#34;&gt;剑指 Offer 64. 求1+2+…+n&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a href=&#34;https://www.yuque.com/oldjii/android/lmmuch&#34;&gt;剑指 Offer 68 - II. 二叉树的最近公共祖先&lt;/a&gt;&lt;/li&gt; +&lt;/ul&gt; +]]>
@@ -1447,66 +1447,66 @@ $$

https://folay.top/post/android_ipc/ 什么是IPC IPC是Interprocess communication的缩写,即进程间通讯。 Linux现有IPC方式 管道:在创建时分配一个p - 什么是IPC -

IPC是Interprocess communication的缩写,即进程间通讯。

-

Linux现有IPC方式

-

管道:在创建时分配一个page大小的内存,缓存区大小比较有限

-

消息队列:信息复制两次,额外的CPU消耗;不适合频繁或信息量大的通讯

-

共享内存:无需复制,共享缓冲区直接附加到进程虚拟地址空间,速度快,但进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决

-

套接字:作为更通用的接口,传输效率低,主要用于不同机器或跨网络的通讯

-

信号量:常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源,因此,主要作为进程间以及同一进程内不同线程之间的同步手段

-

信号:不适用与信息交换,更适合与进程中断控制,比如非法内存访问、杀死某个进程等

-

Linux中传统IPC通讯原理

-

首先,我们要知道,Linux中进程之间是有隔离的,而且每个进程的进程空间都会分为“用户空间”和“内核空间”,对应着“用户态”和“内核态”,而“系统调用”则是用户空间访问内核空间的唯一方式,系统调用主要通过如下两个函数实现:

-
    -
  • copy_from_user() //将数据从用户空间拷贝到内核空间
  • -
  • copy_to_user() //将数据从内核空间拷贝到用户空间
  • -
-

接下来,我们就可以研究传统IPC通讯的原理了,如下图所示: -

-

消息方将要发送的数据存放在内存缓存区,通过系统调用进入内核态,然后内核程序在内核空间分配内存,开辟一块内存缓存区,调用copy_from_user()将数据从用户空间的内存缓存区拷贝到内核空间的内核缓存区中。同样的,接收方进程在接受数据时在自己的用户空间开辟一块内存缓存区,然后内核程序调用copy_to_user()将数据从内核缓存区拷贝到接受进程的内存缓存区。这时两个进程间就完成了一次数据传输,我们称完成了一次进程间通讯

-

Android中的IPC

-

通常,一个App只有一个进程,但Android是可以实现多进程的,比如某些通讯App会单独开辟一个常驻后台的进程。发展迅速,这种做法越来越常见

-

Android中如何多进程

-

通常,只有一种方法,即在AndroidMenifest中指定新的android:process属性

-

具体如下:

-
- -
-
 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
-10
-11
-12
-13
-14
-15
-16
-17
-18
-19
-20
-21
-22
-23
-
-
<activity android:name=".Activity1">
-    <intent-filter>
-        <action android:name="android.intent.action.MAIN" />
-        <category android:name="android.intent.category.LAUNCHER" />
-    </intent-filter>
-</activity>
-<activity android:name=".Activity2"
-    android:process=":remote"/>
-<activity android:name=".Activity3"
-    android:process="com.example.myapplication.remote"/>
+      <![CDATA[&lt;h1 id=&#34;什么是ipc&#34;&gt;什么是IPC&lt;/h1&gt;
+&lt;p&gt;IPC是Interprocess communication的缩写,即进程间通讯。&lt;/p&gt;
+&lt;h1 id=&#34;linux现有ipc方式&#34;&gt;Linux现有IPC方式&lt;/h1&gt;
+&lt;p&gt;&lt;strong&gt;管道&lt;/strong&gt;:在创建时分配一个page大小的内存,缓存区大小比较有限&lt;/p&gt;
+&lt;p&gt;&lt;strong&gt;消息队列&lt;/strong&gt;:信息复制两次,额外的CPU消耗;不适合频繁或信息量大的通讯&lt;/p&gt;
+&lt;p&gt;&lt;strong&gt;共享内存&lt;/strong&gt;:无需复制,共享缓冲区直接附加到进程虚拟地址空间,速度快,但进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决&lt;/p&gt;
+&lt;p&gt;&lt;strong&gt;套接字&lt;/strong&gt;:作为更通用的接口,传输效率低,主要用于不同机器或跨网络的通讯&lt;/p&gt;
+&lt;p&gt;&lt;strong&gt;信号量&lt;/strong&gt;:常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源,因此,主要作为进程间以及同一进程内不同线程之间的同步手段&lt;/p&gt;
+&lt;p&gt;&lt;strong&gt;信号&lt;/strong&gt;:不适用与信息交换,更适合与进程中断控制,比如非法内存访问、杀死某个进程等&lt;/p&gt;
+&lt;h2 id=&#34;linux中传统ipc通讯原理&#34;&gt;Linux中传统IPC通讯原理&lt;/h2&gt;
+&lt;p&gt;首先,我们要知道,Linux中进程之间是有隔离的,而且每个进程的进程空间都会分为“用户空间”和“内核空间”,对应着“用户态”和“内核态”,而“系统调用”则是用户空间访问内核空间的唯一方式,系统调用主要通过如下两个函数实现:&lt;/p&gt;
+&lt;ul&gt;
+&lt;li&gt;copy_from_user() //将数据从用户空间拷贝到内核空间&lt;/li&gt;
+&lt;li&gt;copy_to_user() //将数据从内核空间拷贝到用户空间&lt;/li&gt;
+&lt;/ul&gt;
+&lt;p&gt;接下来,我们就可以研究传统IPC通讯的原理了,如下图所示:
+&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/5b391412f26f4a4aa5d68a1db0dd0a04.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
+&lt;p&gt;消息方将要发送的数据存放在内存缓存区,通过系统调用进入内核态,然后内核程序在内核空间分配内存,开辟一块内存缓存区,调用copy_from_user()将数据从用户空间的内存缓存区拷贝到内核空间的内核缓存区中。同样的,接收方进程在接受数据时在自己的用户空间开辟一块内存缓存区,然后内核程序调用copy_to_user()将数据从内核缓存区拷贝到接受进程的内存缓存区。这时两个进程间就完成了一次数据传输,我们称完成了一次进程间通讯&lt;/p&gt;
+&lt;h1 id=&#34;android中的ipc&#34;&gt;Android中的IPC&lt;/h1&gt;
+&lt;p&gt;通常,一个App只有一个进程,但Android是可以实现多进程的,比如某些通讯App会单独开辟一个常驻后台的进程。发展迅速,这种做法越来越常见&lt;/p&gt;
+&lt;h2 id=&#34;android中如何多进程&#34;&gt;Android中如何多进程&lt;/h2&gt;
+&lt;p&gt;通常,只有一种方法,即在AndroidMenifest中指定新的&lt;code&gt;android:process&lt;/code&gt;属性&lt;/p&gt;
+&lt;p&gt;具体如下:&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;activity&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;android:name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;.Activity1&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;
+    &lt;span class=&#34;nt&#34;&gt;&amp;lt;intent-filter&amp;gt;&lt;/span&gt;
+        &lt;span class=&#34;nt&#34;&gt;&amp;lt;action&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;android:name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;android.intent.action.MAIN&amp;#34;&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;
+        &lt;span class=&#34;nt&#34;&gt;&amp;lt;category&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;android:name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;android.intent.category.LAUNCHER&amp;#34;&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;
+    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/intent-filter&amp;gt;&lt;/span&gt;
+&lt;span class=&#34;nt&#34;&gt;&amp;lt;/activity&amp;gt;&lt;/span&gt;
+&lt;span class=&#34;nt&#34;&gt;&amp;lt;activity&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;android:name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;.Activity2&amp;#34;&lt;/span&gt;
+    &lt;span class=&#34;na&#34;&gt;android:process=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;:remote&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;
+&lt;span class=&#34;nt&#34;&gt;&amp;lt;activity&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;android:name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;.Activity3&amp;#34;&lt;/span&gt;
+    &lt;span class=&#34;na&#34;&gt;android:process=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;com.example.myapplication.remote&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;/&amp;gt;&lt;/span&gt;
 
 ------------------------------
 上述代码创建了三个进程:
@@ -1515,181 +1515,181 @@ $$

3. com.example.myapplication.remote 后两个进程的区别: -- 用":"创建的是私有进程,其他应用不可以和他在一个进程共存 +- 用&amp;#34;:&amp;#34;创建的是私有进程,其他应用不可以和他在一个进程共存 - 写全包名的是全局进程,其他应用可以通过shareUID的方式共存在同一进程 解释一下什么是UID: Android系统会给每一个应用分配一个唯一的UID,具有相同UID且签名相同的应用才能共享数据 -
-
-

还有一种非常规的方法:通过JNI在native层中去fork新进程

-

Android多进程引发的问题

-

在介绍AndroidIPC之前,先说一下为什么需要这些方式,原有的Android通讯机制或者LinuxIPC满足不了Android多进程吗?

-

Android系统会给每一个进程分配一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,这就导致了很多问题:

-
    -
  • 静态成员和单例模式失效
  • -
  • 线程同步机制失效
  • -
  • SP并发操作导致数据可靠性下降
  • -
  • Application多次创建(相当于重启了App)
  • -
-

为了解决上述问题,引出了如下AndroidIPC的方式

-
    -
  • 文件共享
  • -
  • ContentProvider
  • -
  • Bundle
  • -
  • Messager
  • -
  • AIDL
  • -
  • Binder
  • -
-

除文件共享外,这些IPC的方式,底层其实都是使用的Binder机制

-
-

文件共享

-

显而易见,就是通过两个进程读/写同一个文件来实现交换数据

-

缺点:并发操作可能会导致文件数据的有效性

-

场景:适用于对数据同步要求不高的进程之间通讯,并且要妥善解决并发读写的问题

-
-

ContentProvider

-

主要是以表格的形式来组织数据,包含多张表,一行对应一条记录、一列对应一条记录中的一个字段,与数据库类似。除去表格形式还支持图片、视频等文件数据

-

组织封装完数据后会提供统一的存取接口,使得其他进程可以忽略底层数据存储的方式,仅通过统一接口来操作数据

-

通常与他的辅助工具类一起实现通讯,可以实现进程间通讯或进程内通讯

-

原理:Binder机制,外部调用CURD方法的话是运行在ContentProvider进程的Binder线程池中(不是主线程)

-

优点:对数据进行安全的封装;提供统一的存取数据的接口供其他进程调用(无需考虑底层测数据存储方式是SQLite还是内存存储之类的)

-

场景:一对多的进程间共享数据,比如获取/修改系统亮度

-

用法Android校招面试指南-ContentProvider全方位分析

-
-

Bundle

-

Bundle中数据以key-value键值对的形式存在,我们通常在Activity、线程之间使用它,其实由于Bundle实现了Parcelable接口,所以也可以在进程之间使用,只需配置一下目标包名信息即可

-

参考如下:

-
- -
-
1
-2
-3
-4
-5
-6
-7
-8
-
-
Bundle bundle = new Bundle();
-bundle.puString("test",  "来自A");
-Intent intent = new Intent(Intent.ACTION_MAIN);
-intent.addCategory(Intent.CATEGORY_LAUNCHER);
-ComponentName cn = new ComponentName("com.test", "com.test.MainActivity");
-intent.setComponent(cn);
-intent.puExtras(bundle);
-startActivity(intent);
-
-
-

当要使用Bundle传递对象时,必须序列化,即实现Serializable/Parcelable接口

-

关于序列化Oldjii的笔记-Android序列化

-

原理:Bundle只是一个信息的载体,内部维护了一个Map<String, Object>

-

场景:四大组件间的进程间通讯

-
-

Messager

-

Messager可以翻译为“信使”,通过它可以在不同进程间传递Message对象,在Message中放如数据,就可以实现进程间通讯了,这是一种轻量级的IPC方案

-

与Handler的关系:Messager底层使用了AIDL的方式,但和普通的AIDL不同的是,它是利用Handler进行处理的,其实这就是它不支持并发的原因;Handler是线程间通讯的一种机制,其本身是不支持进程间通讯(IPC)的

-

想了解Handler机制,可以参考:Oldjii的笔记-Handler机制解析

-

原理:底层实现是AIDL

-

优点:安全,不支持并发(既可以说是优点也可以说是缺点);封装AIDL,使用简单,不需要AIDL文件;支持实时通讯

-

缺点:串行处理客户端发来的消息,服务端不存在并发情况;数据通过Message传递所以只能传递Bundle支持的数据类型

-

场景:低并发的一对多即使通讯

-

使用Messenger轻量级IPC方案

-
-

AIDL

-

AIDL是Android Interface Description Language的缩写,即Android接口定义语言,用于定义跨进程通讯中双方皆认可的编程接口

-

与Messager的关系: -Messager是基于AIDL封装的,但服务端仅支持串行处理消息,如果有大量的并发请求,那么Messager就不合适了;而且,使用Messager的目标主要是传递消息,但IPC不仅仅如此,还可能会跨进程调用服务端的方法,这种情况Messager就无法满足了,而直接使用AIDL是没有问题的

-

场景:一对多进程间通讯

-

原理:基于Binder封装

-

使用:通过编写AIDL文件来设计想要暴露的接口,编译后会自动生成响应的java文件,服务器将接口的具体实现写在Stub中,用iBinder对象传递给客户端,客户端bindService时,用asInterface的形式将iBinder还原成接口,再调用其中的方法

-

详情参考:Android校招面试指南-Binder机制及AIDL使用

-
-

Binder

-

Binder机制是Android独有的一种跨进程通讯的方式,是Linux中没有的

-

Android系统内部本身就是使用Binder来实现IPC,Framework层中XXXManager和XXXManagetService之间等等都是利用的Binder机制,其中XXXManager是Client端、XXXManagetService是Server端,比如ActivityManager、ActivityManagerService、WindowManager、WindowManagerService、PackageManager、PackageManagerService,乃至于Native Framework层的MediaPlay与MediaPlayService也是一样的

-

详情请参考:Android手机从开机到APP启动经过的流程

-

在应用层,开发者也可以利用Binder实现IPC,其实Messager、AIDL这些方式底层基于Binder的,Binder也可以直接使用,下面是三者使用的场景

-
    -
  • AIDL:需要不同应用的客户端通过IPC通讯访问你的服务,并且需要支持多线程的情况
  • -
  • 直接使用Binder:不需要同时对几个应用进行IPC操作的情况
  • -
  • Messager:需要实现IPC,但不需要处理多线程的情况
  • -
-

直接使用Android Binder的极简使用

-

Binder底层原理

-

在看这部分时,建议你先翻到上面去回顾一下“传统IPC的原理”,这样可以更好的理解Binder的通讯原理

-

Binder与传统IPC不同的地方主要在于使用了“动态内核可加载模块”和“内存映射”

-

动态内核可加载模块

-

Linux的动态内核可加载模块(LKM)是一段具有独立功能的程序,它可以被单独编译,但是不能单独运行,该模块在运行时被链接到内核作为内核的一部分运行。这样Android系统就可以通过动态添加一个内核模块运行在内核空间,用户进程之间则通过这个内核模块作为桥梁来实现通讯

-

这个负责各用户进程Binder通讯的内核模块就是Binder驱动(Binder Dirver)

-

内存映射

-

内存映射简单来说就是,将用户空间的一块内存区域映射到内核空间,映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间,反之亦然。这样利用内存映射就可以减少数据拷贝的次数

-

BinderIPC中的内存映射是通过mmap()来实现的,mmap()是操作系统中一种内存映射的方式,mmap()通常是用在物理介质的文件系统中,但Binder并不存在物理介质,也无法实现利用mmap()在物理介质和用户空间之间建立映射关系。mmap()在Binder的作用是用来在内核空间创建数据接受的缓存空间

-

完整流程

-

了解了上面的概念,就可以理解完整BinderIPC的通讯原理了

-

一次完整的BinderIPC通讯过程:

-
    -
  1. 首先Binder驱动在内核空间创建一个数据接受缓存区
  2. -
  3. 接着在内核空间开辟一块内核缓存区,并建立内核缓存区与数据接收缓存区之间的映射关系,以及内核中的数据接收缓存区与接收进程用户空间的映射关系
  4. -
  5. 发送方通过系统调用copy_from_user()将数据copy到内核中的内核缓存区,由于内核缓存区与接收进程的用户空间存在内存映射,所以也就相当于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通讯
  6. -
-

如下图所示: -

-

Binder通讯流程

-

Client/Server/ServiceManager/Binder Driver

-

Binder 是基于 C/S 架构的。由一系列的组件组成,包括 Client、Server、ServiceManager、Binder Driver,其中 Client、Server、ServiceManager 运行在用户空间,Binder Driver运行在内核空间。其中ServiceManager和Binder Driver由系统提供,而Client、server由应用程序来实现。Client -、Server、ServiceManager均是通过系统调用open()、mmap()、ioctl()来访问设备文件”/dev/binder”,从而实现与Binder Driver的交互来间接的实现跨进程通讯

-

-

这里需要你理解一下ServiceManager这个组件

-

Client、Server、ServiceManager、Binder Driver这几个组件在通信过程中扮演的角色就如同互联网中服务器(Server)、客户端(Client)、DNS域名服务器(ServiceManager)以及路由器(Binder Driver)之前的关系。以下内存摘自《Android Binder 设计与实现》 -

-
-

ServiceManager 与实名 Binder ServiceManager 和 DNS 类似,作用是将字符形式的 Binder 名字转化成 Client 中对该 Binder 的引用,使得 Client 能够通过 Binder 的名字获得对 Binder 实体的引用。注册了名字的 Binder 叫实名 Binder,就像网站一样除了除了有 IP 地址以外还有自己的网址。Server 创建了 Binder,并为它起一个字符形式,可读易记得名字,将这个 Binder 实体连同名字一起以数据包的形式通过 Binder 驱动发送给 ServiceManager ,通知 ServiceManager 注册一个名为“张三”的 Binder,它位于某个 Server 中。驱动为这个穿越进程边界的 Binder 创建位于内核中的实体节点以及 ServiceManager 对实体的引用,将名字以及新建的引用打包传给 ServiceManager。ServiceManger 收到数据后从中取出名字和引用填入查找表

-
-

binder_xxx()函数介绍

-

init():创建/dev/binder设备节点

-

open():获取Binder Driver的文件描述符

-

mmap():在内核分配一块内存,用于存放数据

-

ioctl():将IPC数据作为参数传递给Binder Driver

-

ioctl命令

-
- -
-
1
-2
-3
-4
-5
-6
-7
-
-
BINDER_WRITE_READ           -->     收发Binder IPC数据
-BINDER_SET_MAX_THREADS      -->     设置Binder线程最大个数
-BINDER_SET_CONTEXT_MGR      -->     设置Service Manager节点
-BINDER_THREAD_EXIT          -->     释放Binder线程
-BINDER_VERSION              -->     获取Binder版本信息
-BINDER_SET_IDLE_TIMEOUT     -->     没有使用
-BINDER_SET_IDLE_PRIORITY    -->     没有使用
-
-
-

更多关于Binder源码的内容请参考:Binder系列1—Binder Driver初探

-

完整的Binder通讯过程

-

一次完整的Binder通讯过程如下:

-
    -
  1. 首先,一个进程使用ioctl()命令(BINDER_SET_CONTEXT_MGR)通过Binder将自己注册成为ServiceManager
  2. -
  3. Server通过Binder Driver向ServiceManager注册Binder(Server中的Binder实体),表明可以对外提供服务,驱动为这个Binder创建位于内核中的实体节点以及ServiceManager对实体的引用,将名字以及新建的引用打包给ServiceManager、ServiceManager将其填入查找表
  4. -
  5. Client通过名字,在Binder Driver的帮助下从ServiceManager中获取到对Binder实体的引用,通过这个引用就能实现和Server的通讯
  6. -
-

-
-

为什么Android系统选用Binder机制作为IPC的方式?

-

可以从性能、安全两个方面来回答这个问题

-
性能方面
-

在移动设备上,广泛的使用跨进程通讯对通讯机制的性能有很严格的要求,Binder相对于传统的Socket方式,更加高效。因为Binder数据拷贝只需一次,而管道、消息队列、Socket等需要2此,虽然共享内容方式一次内存拷贝都不要,但是实现方式过于复杂,不适合该场景。

-
安全方面
-

传统的进程通讯方式对于通讯双方的身份并没有作出严格的验证,比如Socket通讯的IP地址是客户端手动填入,很容易进行伪造。然而,Binder机制从协议本身就支持对通讯双方做身份校验,从而大大的提高了安全性

-]]>
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; +&lt;/div&gt; +&lt;/div&gt;&lt;p&gt;还有一种非常规的方法:通过JNI在native层中去fork新进程&lt;/p&gt; +&lt;h2 id=&#34;android多进程引发的问题&#34;&gt;Android多进程引发的问题&lt;/h2&gt; +&lt;p&gt;在介绍AndroidIPC之前,先说一下为什么需要这些方式,原有的Android通讯机制或者LinuxIPC满足不了Android多进程吗?&lt;/p&gt; +&lt;p&gt;Android系统会给每一个进程分配一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,这就导致了很多问题:&lt;/p&gt; +&lt;ul&gt; +&lt;li&gt;静态成员和单例模式失效&lt;/li&gt; +&lt;li&gt;线程同步机制失效&lt;/li&gt; +&lt;li&gt;SP并发操作导致数据可靠性下降&lt;/li&gt; +&lt;li&gt;Application多次创建(相当于重启了App)&lt;/li&gt; +&lt;/ul&gt; +&lt;p&gt;为了解决上述问题,引出了如下AndroidIPC的方式&lt;/p&gt; +&lt;ul&gt; +&lt;li&gt;文件共享&lt;/li&gt; +&lt;li&gt;ContentProvider&lt;/li&gt; +&lt;li&gt;Bundle&lt;/li&gt; +&lt;li&gt;Messager&lt;/li&gt; +&lt;li&gt;AIDL&lt;/li&gt; +&lt;li&gt;Binder&lt;/li&gt; +&lt;/ul&gt; +&lt;p&gt;除文件共享外,这些IPC的方式,底层其实都是使用的Binder机制&lt;/p&gt; +&lt;hr&gt; +&lt;h2 id=&#34;文件共享&#34;&gt;文件共享&lt;/h2&gt; +&lt;p&gt;显而易见,就是通过两个进程读/写同一个文件来实现交换数据&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;缺点&lt;/strong&gt;:并发操作可能会导致文件数据的有效性&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;场景&lt;/strong&gt;:适用于对数据同步要求不高的进程之间通讯,并且要妥善解决并发读写的问题&lt;/p&gt; +&lt;hr&gt; +&lt;h2 id=&#34;contentprovider&#34;&gt;ContentProvider&lt;/h2&gt; +&lt;p&gt;主要是以表格的形式来组织数据,包含多张表,一行对应一条记录、一列对应一条记录中的一个字段,与数据库类似。除去表格形式还支持图片、视频等文件数据&lt;/p&gt; +&lt;p&gt;组织封装完数据后会提供统一的存取接口,使得其他进程可以忽略底层数据存储的方式,仅通过统一接口来操作数据&lt;/p&gt; +&lt;p&gt;通常与他的辅助工具类一起实现通讯,可以实现进程间通讯或进程内通讯&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;原理&lt;/strong&gt;:Binder机制,外部调用CURD方法的话是运行在ContentProvider进程的Binder线程池中(不是主线程)&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;优点&lt;/strong&gt;:对数据进行安全的封装;提供统一的存取数据的接口供其他进程调用(无需考虑底层测数据存储方式是SQLite还是内存存储之类的)&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;场景&lt;/strong&gt;:一对多的进程间共享数据,比如获取/修改系统亮度&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;用法&lt;/strong&gt;:&lt;a href=&#34;https://lrh1993.gitbooks.io/android_interview_guide/content/android/basis/ContentProvider.html&#34;&gt;Android校招面试指南-ContentProvider全方位分析&lt;/a&gt;&lt;/p&gt; +&lt;hr&gt; +&lt;h2 id=&#34;bundle&#34;&gt;Bundle&lt;/h2&gt; +&lt;p&gt;Bundle中数据以key-value键值对的形式存在,我们通常在Activity、线程之间使用它,其实由于Bundle实现了Parcelable接口,所以也可以在进程之间使用,只需配置一下目标包名信息即可&lt;/p&gt; +&lt;p&gt;参考如下:&lt;/p&gt; +&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt; +&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt; +&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8 +&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt; +&lt;td class=&#34;lntd&#34;&gt; +&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;n&#34;&gt;Bundle&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;bundle&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Bundle&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;();&lt;/span&gt; +&lt;span class=&#34;n&#34;&gt;bundle&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;puString&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;test&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;来自A&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt; +&lt;span class=&#34;n&#34;&gt;Intent&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;intent&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Intent&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Intent&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;ACTION_MAIN&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt; +&lt;span class=&#34;n&#34;&gt;intent&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;addCategory&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Intent&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;CATEGORY_LAUNCHER&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt; +&lt;span class=&#34;n&#34;&gt;ComponentName&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ComponentName&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;com.test&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;com.test.MainActivity&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt; +&lt;span class=&#34;n&#34;&gt;intent&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setComponent&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cn&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt; +&lt;span class=&#34;n&#34;&gt;intent&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;puExtras&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bundle&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt; +&lt;span class=&#34;n&#34;&gt;startActivity&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;intent&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt; +&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; +&lt;/div&gt; +&lt;/div&gt;&lt;p&gt;当要使用Bundle传递对象时,必须序列化,即实现Serializable/Parcelable接口&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;关于序列化&lt;/strong&gt;:&lt;a href=&#34;http://note.youdao.com/noteshare?id=5521000160fbda9125b03909587fb2b6&#34;&gt;Oldjii的笔记-Android序列化&lt;/a&gt;&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;原理&lt;/strong&gt;:Bundle只是一个信息的载体,内部维护了一个Map&amp;lt;String, Object&amp;gt;&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;场景&lt;/strong&gt;:四大组件间的进程间通讯&lt;/p&gt; +&lt;hr&gt; +&lt;h2 id=&#34;messager&#34;&gt;Messager&lt;/h2&gt; +&lt;p&gt;Messager可以翻译为“信使”,通过它可以在不同进程间传递Message对象,在Message中放如数据,就可以实现进程间通讯了,这是一种轻量级的IPC方案&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;与Handler的关系&lt;/strong&gt;:Messager底层使用了AIDL的方式,但和普通的AIDL不同的是,它是利用Handler进行处理的,其实这就是它不支持并发的原因;Handler是线程间通讯的一种机制,其本身是不支持进程间通讯(IPC)的&lt;/p&gt; +&lt;p&gt;想了解Handler机制,可以参考:&lt;a href=&#34;http://note.youdao.com/noteshare?id=5eb43fdb54d3c64cb933cb5280ba5201&#34;&gt;Oldjii的笔记-Handler机制解析&lt;/a&gt;&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;原理&lt;/strong&gt;:底层实现是AIDL&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;优点&lt;/strong&gt;:安全,不支持并发(既可以说是优点也可以说是缺点);封装AIDL,使用简单,不需要AIDL文件;支持实时通讯&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;缺点&lt;/strong&gt;:串行处理客户端发来的消息,服务端不存在并发情况;数据通过Message传递所以只能传递Bundle支持的数据类型&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;场景&lt;/strong&gt;:低并发的一对多即使通讯&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;使用&lt;/strong&gt;:&lt;a href=&#34;https://www.jianshu.com/p/52ec31fcf0a6&#34;&gt;Messenger轻量级IPC方案&lt;/a&gt;&lt;/p&gt; +&lt;hr&gt; +&lt;h2 id=&#34;aidl&#34;&gt;AIDL&lt;/h2&gt; +&lt;p&gt;AIDL是Android Interface Description Language的缩写,即Android接口定义语言,用于定义跨进程通讯中双方皆认可的编程接口&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;与Messager的关系&lt;/strong&gt;: +Messager是基于AIDL封装的,但服务端仅支持串行处理消息,如果有大量的并发请求,那么Messager就不合适了;而且,使用Messager的目标主要是传递消息,但IPC不仅仅如此,还可能会跨进程调用服务端的方法,这种情况Messager就无法满足了,而直接使用AIDL是没有问题的&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;场景&lt;/strong&gt;:一对多进程间通讯&lt;/p&gt; +&lt;p&gt;原理:基于Binder封装&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;使用&lt;/strong&gt;:通过编写AIDL文件来设计想要暴露的接口,编译后会自动生成响应的java文件,服务器将接口的具体实现写在Stub中,用iBinder对象传递给客户端,客户端bindService时,用asInterface的形式将iBinder还原成接口,再调用其中的方法&lt;/p&gt; +&lt;p&gt;详情参考:&lt;a href=&#34;https://lrh1993.gitbooks.io/android_interview_guide/content/android/advance/binder.html&#34;&gt;Android校招面试指南-Binder机制及AIDL使用&lt;/a&gt;&lt;/p&gt; +&lt;hr&gt; +&lt;h2 id=&#34;binder&#34;&gt;Binder&lt;/h2&gt; +&lt;p&gt;Binder机制是Android独有的一种跨进程通讯的方式,是Linux中没有的&lt;/p&gt; +&lt;p&gt;Android系统内部本身就是使用Binder来实现IPC,Framework层中XXXManager和XXXManagetService之间等等都是利用的Binder机制,其中XXXManager是Client端、XXXManagetService是Server端,比如ActivityManager、ActivityManagerService、WindowManager、WindowManagerService、PackageManager、PackageManagerService,乃至于Native Framework层的MediaPlay与MediaPlayService也是一样的&lt;/p&gt; +&lt;p&gt;详情请参考:&lt;a href=&#34;http://jiguankai.cn/2019/09/17/Android%E6%89%8B%E6%9C%BA%E4%BB%8E%E5%BC%80%E6%9C%BA%E5%88%B0APP%E5%90%AF%E5%8A%A8%E7%BB%8F%E8%BF%87%E7%9A%84%E6%B5%81%E7%A8%8B/&#34;&gt;Android手机从开机到APP启动经过的流程&lt;/a&gt;&lt;/p&gt; +&lt;p&gt;在应用层,开发者也可以利用Binder实现IPC,其实Messager、AIDL这些方式底层基于Binder的,Binder也可以直接使用,下面是三者使用的场景&lt;/p&gt; +&lt;ul&gt; +&lt;li&gt;AIDL:需要不同应用的客户端通过IPC通讯访问你的服务,并且需要支持多线程的情况&lt;/li&gt; +&lt;li&gt;直接使用Binder:不需要同时对几个应用进行IPC操作的情况&lt;/li&gt; +&lt;li&gt;Messager:需要实现IPC,但不需要处理多线程的情况&lt;/li&gt; +&lt;/ul&gt; +&lt;p&gt;&lt;strong&gt;直接使用&lt;/strong&gt;:&lt;a href=&#34;https://www.jianshu.com/p/2d6ddd6a3399&#34;&gt;Android Binder的极简使用&lt;/a&gt;&lt;/p&gt; +&lt;h3 id=&#34;binder底层原理&#34;&gt;Binder底层原理&lt;/h3&gt; +&lt;p&gt;在看这部分时,建议你先翻到上面去回顾一下“传统IPC的原理”,这样可以更好的理解Binder的通讯原理&lt;/p&gt; +&lt;p&gt;Binder与传统IPC不同的地方主要在于使用了“动态内核可加载模块”和“内存映射”&lt;/p&gt; +&lt;h4 id=&#34;动态内核可加载模块&#34;&gt;动态内核可加载模块&lt;/h4&gt; +&lt;p&gt;Linux的动态内核可加载模块(LKM)是一段具有独立功能的程序,它可以被单独编译,但是不能单独运行,该模块在运行时被链接到内核作为内核的一部分运行。这样Android系统就可以通过动态添加一个内核模块运行在内核空间,用户进程之间则通过这个内核模块作为桥梁来实现通讯&lt;/p&gt; +&lt;p&gt;这个负责各用户进程Binder通讯的内核模块就是Binder驱动(Binder Dirver)&lt;/p&gt; +&lt;h4 id=&#34;内存映射&#34;&gt;内存映射&lt;/h4&gt; +&lt;p&gt;内存映射简单来说就是,将用户空间的一块内存区域映射到内核空间,映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间,反之亦然。这样利用内存映射就可以减少数据拷贝的次数&lt;/p&gt; +&lt;p&gt;BinderIPC中的内存映射是通过mmap()来实现的,mmap()是操作系统中一种内存映射的方式,mmap()通常是用在物理介质的文件系统中,但Binder并不存在物理介质,也无法实现利用mmap()在物理介质和用户空间之间建立映射关系。mmap()在Binder的作用是用来在内核空间创建数据接受的缓存空间&lt;/p&gt; +&lt;h4 id=&#34;完整流程&#34;&gt;完整流程&lt;/h4&gt; +&lt;p&gt;了解了上面的概念,就可以理解完整BinderIPC的通讯原理了&lt;/p&gt; +&lt;p&gt;一次完整的BinderIPC通讯过程:&lt;/p&gt; +&lt;ol&gt; +&lt;li&gt;首先Binder驱动在内核空间创建一个数据接受缓存区&lt;/li&gt; +&lt;li&gt;接着在内核空间开辟一块内核缓存区,并建立内核缓存区与数据接收缓存区之间的映射关系,以及内核中的数据接收缓存区与接收进程用户空间的映射关系&lt;/li&gt; +&lt;li&gt;发送方通过系统调用copy_from_user()将数据copy到内核中的内核缓存区,由于内核缓存区与接收进程的用户空间存在内存映射,所以也就相当于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通讯&lt;/li&gt; +&lt;/ol&gt; +&lt;p&gt;如下图所示: +&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/20200108145347.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt; +&lt;h3 id=&#34;binder通讯流程&#34;&gt;Binder通讯流程&lt;/h3&gt; +&lt;h4 id=&#34;clientserverservicemanagerbinder-driver&#34;&gt;Client/Server/ServiceManager/Binder Driver&lt;/h4&gt; +&lt;p&gt;Binder 是基于 C/S 架构的。由一系列的组件组成,包括 Client、Server、ServiceManager、Binder Driver,其中 Client、Server、ServiceManager 运行在用户空间,Binder Driver运行在内核空间。其中ServiceManager和Binder Driver由系统提供,而Client、server由应用程序来实现。Client +、Server、ServiceManager均是通过系统调用open()、mmap()、ioctl()来访问设备文件&amp;rdquo;/dev/binder&amp;rdquo;,从而实现与Binder Driver的交互来间接的实现跨进程通讯&lt;/p&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/20200108150804.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt; +&lt;p&gt;这里需要你理解一下ServiceManager这个组件&lt;/p&gt; +&lt;p&gt;Client、Server、ServiceManager、Binder Driver这几个组件在通信过程中扮演的角色就如同互联网中服务器(Server)、客户端(Client)、DNS域名服务器(ServiceManager)以及路由器(Binder Driver)之前的关系。以下内存摘自《Android Binder 设计与实现》 +&lt;/p&gt; +&lt;blockquote&gt; +&lt;p&gt;ServiceManager 与实名 Binder ServiceManager 和 DNS 类似,作用是将字符形式的 Binder 名字转化成 Client 中对该 Binder 的引用,使得 Client 能够通过 Binder 的名字获得对 Binder 实体的引用。注册了名字的 Binder 叫实名 Binder,就像网站一样除了除了有 IP 地址以外还有自己的网址。Server 创建了 Binder,并为它起一个字符形式,可读易记得名字,将这个 Binder 实体连同名字一起以数据包的形式通过 Binder 驱动发送给 ServiceManager ,通知 ServiceManager 注册一个名为“张三”的 Binder,它位于某个 Server 中。驱动为这个穿越进程边界的 Binder 创建位于内核中的实体节点以及 ServiceManager 对实体的引用,将名字以及新建的引用打包传给 ServiceManager。ServiceManger 收到数据后从中取出名字和引用填入查找表&lt;/p&gt; +&lt;/blockquote&gt; +&lt;h4 id=&#34;binder_xxx函数介绍&#34;&gt;binder_xxx()函数介绍&lt;/h4&gt; +&lt;p&gt;&lt;strong&gt;init()&lt;/strong&gt;:创建/dev/binder设备节点&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;open()&lt;/strong&gt;:获取Binder Driver的文件描述符&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;mmap()&lt;/strong&gt;:在内核分配一块内存,用于存放数据&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;ioctl()&lt;/strong&gt;:将IPC数据作为参数传递给Binder Driver&lt;/p&gt; +&lt;p&gt;&lt;strong&gt;ioctl命令&lt;/strong&gt;:&lt;/p&gt; +&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt; +&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt; +&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6 +&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7 +&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt; +&lt;td class=&#34;lntd&#34;&gt; +&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-markdown&#34; data-lang=&#34;markdown&#34;&gt;BINDER_WRITE_READ --&amp;gt; 收发Binder IPC数据 +BINDER_SET_MAX_THREADS --&amp;gt; 设置Binder线程最大个数 +BINDER_SET_CONTEXT_MGR --&amp;gt; 设置Service Manager节点 +BINDER_THREAD_EXIT --&amp;gt; 释放Binder线程 +BINDER_VERSION --&amp;gt; 获取Binder版本信息 +BINDER_SET_IDLE_TIMEOUT --&amp;gt; 没有使用 +BINDER_SET_IDLE_PRIORITY --&amp;gt; 没有使用 +&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; +&lt;/div&gt; +&lt;/div&gt;&lt;p&gt;更多关于Binder源码的内容请参考:&lt;a href=&#34;http://gityuan.com/2015/11/01/binder-driver/&#34;&gt;Binder系列1—Binder Driver初探&lt;/a&gt;&lt;/p&gt; +&lt;h4 id=&#34;完整的binder通讯过程&#34;&gt;完整的Binder通讯过程&lt;/h4&gt; +&lt;p&gt;一次完整的Binder通讯过程如下:&lt;/p&gt; +&lt;ol&gt; +&lt;li&gt;首先,一个进程使用ioctl()命令(BINDER_SET_CONTEXT_MGR)通过Binder将自己注册成为ServiceManager&lt;/li&gt; +&lt;li&gt;Server通过Binder Driver向ServiceManager注册Binder(Server中的Binder实体),表明可以对外提供服务,驱动为这个Binder创建位于内核中的实体节点以及ServiceManager对实体的引用,将名字以及新建的引用打包给ServiceManager、ServiceManager将其填入查找表&lt;/li&gt; +&lt;li&gt;Client通过名字,在Binder Driver的帮助下从ServiceManager中获取到对Binder实体的引用,通过这个引用就能实现和Server的通讯&lt;/li&gt; +&lt;/ol&gt; +&lt;p&gt;&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/20200108154444.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt; +&lt;hr&gt; +&lt;h2 id=&#34;为什么android系统选用binder机制作为ipc的方式&#34;&gt;为什么Android系统选用Binder机制作为IPC的方式?&lt;/h2&gt; +&lt;p&gt;可以从性能、安全两个方面来回答这个问题&lt;/p&gt; +&lt;h5 id=&#34;性能方面&#34;&gt;性能方面&lt;/h5&gt; +&lt;p&gt;在移动设备上,广泛的使用跨进程通讯对通讯机制的性能有很严格的要求,Binder相对于传统的Socket方式,更加高效。因为Binder数据拷贝只需一次,而管道、消息队列、Socket等需要2此,虽然共享内容方式一次内存拷贝都不要,但是实现方式过于复杂,不适合该场景。&lt;/p&gt; +&lt;h5 id=&#34;安全方面&#34;&gt;安全方面&lt;/h5&gt; +&lt;p&gt;传统的进程通讯方式对于通讯双方的身份并没有作出严格的验证,比如Socket通讯的IP地址是客户端手动填入,很容易进行伪造。然而,Binder机制从协议本身就支持对通讯双方做身份校验,从而大大的提高了安全性&lt;/p&gt; +]]>
@@ -1706,1183 +1706,1183 @@ BINDER_SET_IDLE_PRIORITY --> 没有使用 <a href="https://github.com/ReactiveX/RxJava">Rxjava</a><br/> <a href="http://gank.io/post/560e15be2dca930e00da1083">给Android开发者的RxJava详解</a></p> <p>本文会逐一解析Rxjava的create()、subscribe()、操作符、subscribeOn()、obsweveOn()的源码,模式是先给出一段模版代码,然后逐渐深入分析</p> - - - -

本文不对Rxjava的基本使用进行讲解,仅对源码做分析,如果你对Rxjava的基本使用还有不清楚的,建议学习官方文档之后再阅读本文

-

ReactiveX文档中文翻译
-Rxjava
-给Android开发者的RxJava详解

-

本文会逐一解析Rxjava的create()、subscribe()、操作符、subscribeOn()、obsweveOn()的源码,模式是先给出一段模版代码,然后逐渐深入分析

-

正文

-

Create()方法

-

这里给出一个最简单的Rxjava的实例

-
- -
-
 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
-10
-11
-12
-13
-14
-15
-16
-17
-18
-19
-20
-21
-22
-23
-24
-
-
Observable.create(new ObservableOnSubscribe<String>() {
-			@Override
-			public void subscribe(ObservableEmitter<String> e) throws Exception {
-				e.onNext("next");
-				e.onComplete();
-			}
-		}).subscribe(new Observer<String>() {
-			@Override
-			public void onSubscribe(Disposable d) {
-				Log.d(TAG, "onSubscribe: " + d);
-			}
-			@Override
-			public void onNext(String value) {
-				Log.d(TAG, "onNext: " + value);
-			}
-			@Override
-			public void onError(Throwable e) {
-				Log.d(TAG, "onError: " + e);
-			}
-			@Override
-			public void onComplete() {
-				Log.d(TAG, "onComplete: ");
-			}
-		});
-
-
-

直接看create()方法主体

-
- -
-
1
-2
-3
-4
-
-
public static <T> Observable<T> create(ObservableOnSubscribe<T> source) {
-		ObjectHelper.requireNonNull(source, "source is null");
-		return RxJavaPlugins.onAssembly(new ObservableCreate<T>(source));
-	}
-
-
-

调用对象和返回对象都为Observable,而传入参数为ObservableOnSubscribe

-
- -
-
1
-2
-3
-
-
public interface ObservableOnSubscribe<T> {
-		void subscribe(@NonNull ObservableEmitter<T> e) throws Exception;
-	}
-
-
-

这是一个接口,仅包含一个方法,就是上面我们在new ObservableOnSubscribe时候需要重写的那个方法。 -再看subscribe()的形参类型ObservableEmitter

-
- -
-
 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
-10
-
-
public interface ObservableEmitter<T> extends Emitter<T> {
-		void setDisposable(@Nullable Disposable d);
-		void setCancellable(@Nullable Cancellable c);
-		boolean isDisposed();
+      <![CDATA[&lt;div align=&#34;center&#34;&gt;
+&lt;img src=https://imgoldjii.oss-cn-beijing.aliyuncs.com/rxjava0022.png width=80%/&gt;
+&lt;/div&gt;
+&lt;p&gt;本文不对Rxjava的基本使用进行讲解,仅对源码做分析,如果你对Rxjava的基本使用还有不清楚的,建议学习官方文档之后再阅读本文&lt;/p&gt;
+&lt;p&gt;&lt;a href=&#34;https://mcxiaoke.gitbooks.io/rxdocs/content/Operators.html&#34;&gt;ReactiveX文档中文翻译&lt;/a&gt;&lt;br/&gt;
+&lt;a href=&#34;https://github.com/ReactiveX/RxJava&#34;&gt;Rxjava&lt;/a&gt;&lt;br/&gt;
+&lt;a href=&#34;http://gank.io/post/560e15be2dca930e00da1083&#34;&gt;给Android开发者的RxJava详解&lt;/a&gt;&lt;/p&gt;
+&lt;p&gt;本文会逐一解析Rxjava的create()、subscribe()、操作符、subscribeOn()、obsweveOn()的源码,模式是先给出一段模版代码,然后逐渐深入分析&lt;/p&gt;
+&lt;h1 id=&#34;正文&#34;&gt;正文&lt;/h1&gt;
+&lt;h2 id=&#34;create方法&#34;&gt;Create()方法&lt;/h2&gt;
+&lt;p&gt;这里给出一个最简单的Rxjava的实例&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;n&#34;&gt;Observable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;create&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ObservableOnSubscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+			&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+			&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;subscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ObservableEmitter&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;throws&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Exception&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+				&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;onNext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;next&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+				&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;onComplete&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;();&lt;/span&gt;
+			&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+		&lt;span class=&#34;o&#34;&gt;}).&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;subscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Observer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+			&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+			&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onSubscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Disposable&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+				&lt;span class=&#34;n&#34;&gt;Log&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TAG&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;onSubscribe: &amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+			&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+			&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+			&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onNext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+				&lt;span class=&#34;n&#34;&gt;Log&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TAG&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;onNext: &amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+			&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+			&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+			&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onError&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Throwable&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+				&lt;span class=&#34;n&#34;&gt;Log&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TAG&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;onError: &amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+			&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+			&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+			&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onComplete&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+				&lt;span class=&#34;n&#34;&gt;Log&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TAG&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;onComplete: &amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+			&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+		&lt;span class=&#34;o&#34;&gt;});&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;直接看create()方法主体&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Observable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;create&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ObservableOnSubscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+		&lt;span class=&#34;n&#34;&gt;ObjectHelper&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;requireNonNull&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;source is null&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+		&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RxJavaPlugins&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;onAssembly&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ObservableCreate&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;));&lt;/span&gt;
+	&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;调用对象和返回对象都为Observable,而传入参数为ObservableOnSubscribe&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;ObservableOnSubscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+		&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;subscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@NonNull&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ObservableEmitter&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;throws&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Exception&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
+	&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;这是一个接口,仅包含一个方法,就是上面我们在new ObservableOnSubscribe时候需要重写的那个方法。
+再看subscribe()的形参类型ObservableEmitter&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;ObservableEmitter&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Emitter&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+		&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;setDisposable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Nullable&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Disposable&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+		&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;setCancellable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Nullable&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Cancellable&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+		&lt;span class=&#34;kt&#34;&gt;boolean&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;isDisposed&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;();&lt;/span&gt;
 
-		@NonNull
-		ObservableEmitter<T> serialize();
-		@Experimental
-		boolean tryOnError(@NonNull Throwable t);
-	}
-
-
-

发现这也是一个接口,不需要太过关注,值得关注的是他的上层,由接口特性我们知道Emitter肯定也是一个接口,我们来看下它定义了什么方法

-
- -
-
1
-2
-3
-4
-5
-6
-7
-
-
public interface Emitter<T> {
-		void onNext(@NonNull T value);
+		&lt;span class=&#34;nd&#34;&gt;@NonNull&lt;/span&gt;
+		&lt;span class=&#34;n&#34;&gt;ObservableEmitter&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;serialize&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;();&lt;/span&gt;
+		&lt;span class=&#34;nd&#34;&gt;@Experimental&lt;/span&gt;
+		&lt;span class=&#34;kt&#34;&gt;boolean&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;tryOnError&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@NonNull&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Throwable&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+	&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;发现这也是一个接口,不需要太过关注,值得关注的是他的上层,由接口特性我们知道Emitter肯定也是一个接口,我们来看下它定义了什么方法&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Emitter&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+		&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onNext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@NonNull&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
 
-		void onError(@NonNull Throwable error);
+		&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onError&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@NonNull&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Throwable&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
 
-		void onComplete();
-	}
-
-
-

看到这三个熟悉的方法,你就知道为什么我们实例化的ObservableEmitter对象e可以调用onNext()、onError()、onComplete()这三个方法了

-

create()的参数已经看完了,下面看下create()的内容

-

第一句ObjectHelper.requireNonNull(source, "source is null");是判空代码。 -返回值是RxJavaPlugins.onAssembly(new ObservableCreate<T>(source)),我看看下这个方法。

-
- -
-
1
-2
-3
-4
-
-
public static <T> Observable<T> onAssembly(@NonNull Observable<T> source) {
-		...
-		return source;
-	}
-
-
-

具体的内容我们不需要去解读,我们只看他的返回值和传入参数,经过观察发现都是Observable类型,乍看好像没什么问题,但是看上面,源码中传入的是一个ObservableCreate类型,所以这里ObservableCreate有适配器的作用,将ObservableOnSubscribe适配为Observable类型。 -下面我们就看看这个适配器ObservableCreate

-
- -
-
 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
-10
-11
-12
-13
-14
-15
-16
-17
-18
-19
-
-
public final class ObservableCreate<T> extends Observable<T> {
-		final ObservableOnSubscribe<T> source;
+		&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onComplete&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;();&lt;/span&gt;
+	&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;看到这三个熟悉的方法,你就知道为什么我们实例化的ObservableEmitter对象e可以调用onNext()、onError()、onComplete()这三个方法了&lt;/p&gt;
+&lt;p&gt;&lt;strong&gt;create()的参数已经看完了,下面看下create()的内容&lt;/strong&gt;&lt;/p&gt;
+&lt;p&gt;第一句&lt;code&gt;ObjectHelper.requireNonNull(source, &amp;quot;source is null&amp;quot;);&lt;/code&gt;是判空代码。
+返回值是&lt;code&gt;RxJavaPlugins.onAssembly(new ObservableCreate&amp;lt;T&amp;gt;(source))&lt;/code&gt;,我看看下这个方法。&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Observable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onAssembly&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@NonNull&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Observable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+		&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;
+		&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
+	&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;具体的内容我们不需要去解读,我们只看他的返回值和传入参数,经过观察发现都是Observable类型,乍看好像没什么问题,但是看上面,源码中传入的是一个ObservableCreate类型,所以这里ObservableCreate有适配器的作用,将ObservableOnSubscribe适配为Observable类型。
+下面我们就看看这个适配器ObservableCreate&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;ObservableCreate&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Observable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+		&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ObservableOnSubscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
 		
-		public ObservableCreate(ObservableOnSubscribe<T> source) {
-			this.source = source;
-		}
+		&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ObservableCreate&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ObservableOnSubscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+			&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;source&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
+		&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
 		
-		@Override
-		protected void subscribeActual(Observer<? super T> observer) {
+		&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+		&lt;span class=&#34;kd&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;subscribeActual&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Observer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;super&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;observer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
 			
-			CreateEmitter<T> parent = new CreateEmitter<T>(observer);
+			&lt;span class=&#34;n&#34;&gt;CreateEmitter&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;parent&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;CreateEmitter&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;observer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
 			
-			observer.onSubscribe(parent);
+			&lt;span class=&#34;n&#34;&gt;observer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;onSubscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parent&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
 			
-			source.subscribe(parent);
+			&lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;subscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parent&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
 			
-			...
-		}
-	}
-
-
-

成员变量、构造方法略过,我们先看看这里频频出现的观察者observer

-
- -
-
1
-2
-3
-4
-5
-6
-
-
public interface Observer<T> {
-    void onSubscribe(@NonNull Disposable d);
-    void onNext(@NonNull T t);
-    void onError(@NonNull Throwable e);
-    void onComplete();
-}
-
-
-

同样的也是一个接口,定义的这个四个方法就是我们在订阅时,观察者需要重写的四个方法,注意与上面的Emitter接口及其三个方法进行区分。 -看这行observer.onSubscribe(parent);,由上面我们知道observer.onSubscribe()是接受Disposable类型,而这里的parent是CreateEmitter类型,你可能已经猜出来了,没错,这里的CreateEmitter也是一个适配器,前面的ObservableCreate对被观察者进行了适配,CreateEmitter则对观察者进行了适配,将observer类型转化为Disposable类型,下面看下他的源码

-
- -
-
 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
-
-
static final class CreateEmitter<T> extends AtomicReference<Disposable> implements ObservableEmitter<T>, Disposable {
+			&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;
+		&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+	&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;成员变量、构造方法略过,我们先看看这里频频出现的观察者observer&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Observer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+    &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onSubscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@NonNull&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Disposable&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+    &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onNext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@NonNull&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+    &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onError&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@NonNull&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Throwable&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+    &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onComplete&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;();&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;同样的也是一个接口,定义的这个四个方法就是我们在订阅时,观察者需要重写的四个方法,注意与上面的Emitter接口及其三个方法进行区分。
+看这行&lt;code&gt;observer.onSubscribe(parent);&lt;/code&gt;,由上面我们知道observer.onSubscribe()是接受Disposable类型,而这里的parent是CreateEmitter类型,你可能已经猜出来了,没错,这里的CreateEmitter也是一个适配器,前面的ObservableCreate对被观察者进行了适配,CreateEmitter则对观察者进行了适配,将observer类型转化为Disposable类型,下面看下他的源码&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;CreateEmitter&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;AtomicReference&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Disposable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ObservableEmitter&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Disposable&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
 
-		...
+		&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;
 		
-		@Override
-		public void onNext(T t) {
-			if (t == null) {
-				onError(new NullPointerException("..."));
-				return;
-			}
-			if (!isDisposed()) {
-				observer.onNext(t);
-			}
-		}
+		&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+		&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onNext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+			&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+				&lt;span class=&#34;n&#34;&gt;onError&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;NullPointerException&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;...&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;));&lt;/span&gt;
+				&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
+			&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+			&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;isDisposed&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;())&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+				&lt;span class=&#34;n&#34;&gt;observer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;onNext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+			&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+		&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
 
-		@Override
-		public void onError(Throwable t) {
-			if (!tryOnError(t)) {
+		&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+		&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onError&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Throwable&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+			&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tryOnError&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
 				
-				RxJavaPlugins.onError(t);
-			}
-		}
+				&lt;span class=&#34;n&#34;&gt;RxJavaPlugins&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;onError&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+			&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+		&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
 
-		@Override
-		public void onComplete() {
-			if (!isDisposed()) {
-				try {
-					observer.onComplete();
-				} finally {
-					dispose();
-				}
-			}
-		}
+		&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+		&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onComplete&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+			&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;isDisposed&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;())&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+				&lt;span class=&#34;k&#34;&gt;try&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+					&lt;span class=&#34;n&#34;&gt;observer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;onComplete&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;();&lt;/span&gt;
+				&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;finally&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+					&lt;span class=&#34;n&#34;&gt;dispose&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;();&lt;/span&gt;
+				&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+			&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+		&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
 
-		@Override
-		public void dispose() {
-			DisposableHelper.dispose(this);
-		}
+		&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+		&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;dispose&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+			&lt;span class=&#34;n&#34;&gt;DisposableHelper&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;dispose&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+		&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
 		
-		...
-	}
-
-
-

主要就是重写了那四个方法,定义了规则,比如:

-
    -
  • onComplete()与onError()互斥,切CreateEmitter在回调他们两中任意一个后,都会自动dispose()
  • -
  • Observable和Observer的关系没有被dispose,才会回调Observer的onXXXX()方法
  • -
-
-

并且,到这里你对onCreate()中的数据流动也一定有了一定的理解:
-–> e.onNext("next") e是ObservableEmitter,是一个接口
-–> ObservableCreate.CreateEmitter.onNext("next") ObservableCreate是Observable装饰类,CreateEmitter使其内部类也是Observer的装饰类并实现了上面的这个接口
-–> Observer.onNext("next")
-–> Log.d(TAG, "onNext: "+value)

-
-

我们再回到ObservableCreate的subscribeActual()中。

-

source.subscribe(parent);,最重要的是这一行,调用者是被观察者,传入的参数为观察者,基本可以猜出来了,这里是订阅的作用,真正将被观察者与观察者联系起来的地方

-

subscribe()方法

-
- -
-
1
-2
-3
-4
-5
-6
-7
-8
-
-
public final void subscribe(Observer<? super T> observer) {
-		ObjectHelper.requireNonNull(observer, "observer is null");
+		&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;
+	&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;主要就是重写了那四个方法,定义了规则,比如:&lt;/p&gt;
+&lt;ul&gt;
+&lt;li&gt;onComplete()与onError()互斥,切CreateEmitter在回调他们两中任意一个后,都会自动dispose()&lt;/li&gt;
+&lt;li&gt;Observable和Observer的关系没有被dispose,才会回调Observer的onXXXX()方法&lt;/li&gt;
+&lt;/ul&gt;
+&lt;blockquote&gt;
+&lt;p&gt;并且,到这里你对onCreate()中的数据流动也一定有了一定的理解:&lt;br/&gt;
+&amp;ndash;&amp;gt; &lt;code&gt;e.onNext(&amp;quot;next&amp;quot;)&lt;/code&gt; e是ObservableEmitter,是一个接口 &lt;br/&gt;
+&amp;ndash;&amp;gt; &lt;code&gt;ObservableCreate.CreateEmitter.onNext(&amp;quot;next&amp;quot;)&lt;/code&gt; ObservableCreate是Observable装饰类,CreateEmitter使其内部类也是Observer的装饰类并实现了上面的这个接口 &lt;br/&gt;
+&amp;ndash;&amp;gt; &lt;code&gt;Observer.onNext(&amp;quot;next&amp;quot;)&lt;/code&gt; &lt;br/&gt;
+&amp;ndash;&amp;gt; &lt;code&gt;Log.d(TAG, &amp;quot;onNext: &amp;quot;+value)&lt;/code&gt;&lt;/p&gt;
+&lt;/blockquote&gt;
+&lt;p&gt;我们再回到ObservableCreate的subscribeActual()中。&lt;/p&gt;
+&lt;p&gt;&lt;code&gt;source.subscribe(parent);&lt;/code&gt;,最重要的是这一行,调用者是被观察者,传入的参数为观察者,基本可以猜出来了,这里是订阅的作用,真正将被观察者与观察者联系起来的地方&lt;/p&gt;
+&lt;h2 id=&#34;subscribe方法&#34;&gt;subscribe()方法&lt;/h2&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;subscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Observer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;super&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;observer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+		&lt;span class=&#34;n&#34;&gt;ObjectHelper&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;requireNonNull&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;observer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;observer is null&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
 
-		observer = RxJavaPlugins.onSubscribe(this, observer);
+		&lt;span class=&#34;n&#34;&gt;observer&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RxJavaPlugins&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;onSubscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;observer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
 
-		subscribeActual(observer);
-		...
-	}
-
-
-

第一句的作用同样是判空,接下来先获取了传入的observer并进行了相关配置,然后调用subscribeActual(observer);,细心的同学可能注意到了,subscribeActual()正是在上面ObservableCreate中被重写的方法,而具有“订阅”意义的那行代码也包含其中,结合subscribe()的本意,这行代码的作用也很明朗了

-

如果你只是想对Rxjava基本的数据传输流程、订阅的原理感兴趣,那么就不用看下去了,下面的内容主要是Rxjava操作符线程调度背包的源码分析

-
-

操作符(Map)

-

开始分析Rxjava的操作符部分

-

我们以Map操作符为例展开分析,首先,还是给出一个最简单的实例

-
- -
-
 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
-
-
Observable.create(new ObservableOnSubscribe<String>() {
-			@Override
-			public void subscribe(ObservableEmitter<String> e) throws Exception {
-				e.onNext("next");
-				e.onComplete();
-			}
-		}).map(new Function<String, Integer>() {
-			@Override
-			public Integer apply(String s) throws Exception {
-				return Integer.parseInt(s);
-			}
-		}).subscribe(new Observer<Integer>() {
-			@Override
-			public void onSubscribe(Disposable d) {
-				Log.d(TAG, "onSubscribe: " + d);
-			}
-			@Override
-			public void onNext(Integer value) {
-				Log.d(TAG, "onNext: " + value);
-			}
-			@Override
-			public void onError(Throwable e) {
-				Log.d(TAG, "onError: " + e);
-			}
-			@Override
-			public void onComplete() {
-				Log.d(TAG, "onComplete: ");
-			}
-		});
-
-
-

先看map()方法整体

-
- -
-
1
-2
-3
-4
-
-
public final <R > Observable < R > map(Function < ? super T, ? extends R > mapper){
-			ObjectHelper.requireNonNull(mapper, "mapper is null");
-			return RxJavaPlugins.onAssembly(new ObservableMap<T, R>(this, mapper));
-		}
-
-
-

返回值肯定是Observable,参数是一个泛型接口,我们看下这个接口

-
- -
-
1
-2
-3
-
-
public interface Function<T, R> {
-    R apply(@NonNull T t) throws Exception;
-}
-
-
-

传入T,返回R,符合Map操作符传入两个数据类型进行转换的效果,在意料之中。 -继续看map()的方法内容,第一行按照惯例是判空语句,我们发现map()的return语句与create()极为相似,都是调用了RxJavaPlugins.onAssembly(),仅是传入的参数不同,其实不只是Map操作符,大多操作符都是这样的,他们的不同仅仅是传入参数的不同,也就是适配器的不同,这说明,操作符的具体实现(比如Map的类型转换)都是在各自的适配器中做的。

-
-

小结:create以及对大多数操作符的retun语句都是RxJavaPlugins.onAssembly(),仅是传入参数不同

-
-

进入ObservableMap的部分

-
- -
-
 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
-10
-11
-12
-13
-
-
public final class ObservableMap<T, U> extends AbstractObservableWithUpstream<T, U> {
-			final Function<? super T, ? extends U> function;
+		&lt;span class=&#34;n&#34;&gt;subscribeActual&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;observer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+		&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;
+	&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;第一句的作用同样是判空,接下来先获取了传入的observer并进行了相关配置,然后调用&lt;code&gt;subscribeActual(observer);&lt;/code&gt;,细心的同学可能注意到了,subscribeActual()正是在上面ObservableCreate中被重写的方法,而具有“&lt;strong&gt;订阅&lt;/strong&gt;”意义的那行代码也包含其中,结合subscribe()的本意,这行代码的作用也很明朗了&lt;/p&gt;
+&lt;p&gt;如果你只是想对Rxjava基本的数据传输流程、订阅的原理感兴趣,那么就不用看下去了,下面的内容主要是&lt;strong&gt;Rxjava操作符&lt;/strong&gt;、&lt;strong&gt;线程调度&lt;/strong&gt;、&lt;strong&gt;背包&lt;/strong&gt;的源码分析&lt;/p&gt;
+&lt;hr&gt;
+&lt;h2 id=&#34;操作符map&#34;&gt;操作符(Map)&lt;/h2&gt;
+&lt;p&gt;开始分析Rxjava的操作符部分&lt;/p&gt;
+&lt;p&gt;我们以Map操作符为例展开分析,首先,还是给出一个最简单的实例&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;n&#34;&gt;Observable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;create&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ObservableOnSubscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+			&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+			&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;subscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ObservableEmitter&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;throws&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Exception&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+				&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;onNext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;next&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+				&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;onComplete&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;();&lt;/span&gt;
+			&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+		&lt;span class=&#34;o&#34;&gt;}).&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Function&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Integer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+			&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+			&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Integer&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;apply&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;throws&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Exception&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+				&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Integer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;parseInt&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+			&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+		&lt;span class=&#34;o&#34;&gt;}).&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;subscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Observer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Integer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+			&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+			&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onSubscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Disposable&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+				&lt;span class=&#34;n&#34;&gt;Log&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TAG&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;onSubscribe: &amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+			&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+			&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+			&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onNext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Integer&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+				&lt;span class=&#34;n&#34;&gt;Log&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TAG&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;onNext: &amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+			&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+			&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+			&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onError&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Throwable&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+				&lt;span class=&#34;n&#34;&gt;Log&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TAG&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;onError: &amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+			&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+			&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+			&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onComplete&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+				&lt;span class=&#34;n&#34;&gt;Log&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TAG&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;onComplete: &amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+			&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+		&lt;span class=&#34;o&#34;&gt;});&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;先看map()方法整体&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;R&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Observable&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;R&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Function&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;super&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;R&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mapper&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;){&lt;/span&gt;
+			&lt;span class=&#34;n&#34;&gt;ObjectHelper&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;requireNonNull&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mapper&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;mapper is null&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+			&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RxJavaPlugins&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;onAssembly&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ObservableMap&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mapper&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;));&lt;/span&gt;
+		&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;返回值肯定是Observable,参数是一个泛型接口,我们看下这个接口&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Function&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+    &lt;span class=&#34;n&#34;&gt;R&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;apply&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@NonNull&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;throws&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Exception&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;传入T,返回R,符合Map操作符传入两个数据类型进行转换的效果,在意料之中。
+继续看map()的方法内容,第一行按照惯例是判空语句,我们发现map()的return语句与create()极为相似,都是调用了RxJavaPlugins.onAssembly(),仅是传入的参数不同,其实不只是Map操作符,大多操作符都是这样的,他们的不同仅仅是传入参数的不同,也就是适配器的不同,这说明,操作符的具体实现(比如Map的类型转换)都是在各自的适配器中做的。&lt;/p&gt;
+&lt;blockquote&gt;
+&lt;p&gt;小结:create以及对大多数操作符的retun语句都是RxJavaPlugins.onAssembly(),仅是传入参数不同&lt;/p&gt;
+&lt;/blockquote&gt;
+&lt;p&gt;进入ObservableMap的部分&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;ObservableMap&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;U&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;AbstractObservableWithUpstream&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;U&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+			&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Function&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;super&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;U&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
 
-			public ObservableMap(ObservableSource<T> source, Function<? super T, ? extends U> function) {
-				super(source);
-				this.function = function;
-			}
+			&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ObservableMap&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ObservableSource&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Function&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;super&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;U&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+				&lt;span class=&#34;kd&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+				&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
+			&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
 
-			@Override
-			public void subscribeActual(Observer<? super U> t) {
-				source.subscribe(new MapObserver<T, U>(t, function));
-			}
-		}
-
-
-

我们发现,ObservableMap做的事情很少,就三件事,第一:在构造方法中,将传入的Observable也就是本身抛给父类(ObservableSource是Observable的父类,所以可以接受);第二:对转换逻辑funtion进行保存;第三:重写subscribeActual()方法并在其中实现订阅,这里与ObservableCreate是一样的,只是传递的参数不同

-
-

小结:create以及对大多数操作符的第一层适配器中都会重写subscribeActual()并实现订阅逻辑

-
-

我们并没有在ObservableMap的代码中发现进行类型转换的代码,不要心急,有的同学估计已经发现了,这里的进行订阅操作的source.subscribe()传入的参数类型改变了 ,之前是CreateEmitter,现在变为了一个叫MapObserver的类,我们知道CreateEmitter中实现了那四个常用的方法并制定了相关规则,所以你推测MapObserver中做了同样的操作,其实不是的,但也差不了太多,除onNext()之外的三个方法是在它的父类BasicFuseableObserver中重写的,MapObserver中只对onNext()进行的重写,而且在其中进行了数据类型转换的工作,我们看一下源码(这里我们只看onNext()部分就可以了)

-
- -
-
 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
-10
-11
-12
-13
-14
-15
-16
-17
-18
-19
-20
-21
-
-
static final class MapObserver<T, U> extends BasicFuseableObserver<T, U> {
-			@Override
-			public void onNext(T t) {
-				if (done) {
-					return;
-				}
-				if (sourceMode != NONE) {
-					actual.onNext(null);
-					return;
-				}
-				U v;
-				try {
-					v = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper function returned a null value.");
-				} catch (Throwable ex) {
-					fail(ex);
-					return;
-				}
-				actual.onNext(v);
-			}
-			...
-		}
-
-
-

可以看到再代码中利用ObjectHelper将上游传过来的T,转换成了下游需要的U

-
-

到这里你对.map()下的数据流动也一定有了一定的理解:
-–> e.onNext("next")
-–> ObservableMap.MapObserver.onNext ("next")
-–> Observer.onNext("next")
-–> Log.d(TAG, "onNext: "+value)
-订阅的发送顺序:
-–> .subscribe(observer)
-–> ObservableMap.subscribeActual(observer)
-–> ObservableCreate.subscribeActual(new MapObserver(observer,function))

-
-
-

下面进入线程调度源码分析的阶段,先看subscribeOn()。

-

线程调度-subscribeOn()

-

老规矩,先来一个参考代码

-
- -
-
 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
-
-
Observable.create(new ObservableOnSubscribe<String>() {
-			@Override
-			public void subscribe(ObservableEmitter<String> e) throws Exception {
-				e.onNext("next");
-				e.onComplete();
-			}
-		}).subscribeOn(Schedulers.io())
-				.subscribe(new Observer<String>() {
-					@Override
-					public void onSubscribe(Disposable d) {
-						Log.d(TAG, "onSubscribe: " + d);
-					}
-					@Override
-					public void onNext(String value) {
-						Log.d(TAG, "onNext: " + value);
-					}
-					@Override
-					public void onError(Throwable e) {
-						Log.d(TAG, "onError: " + e);
-					}
-					@Override
-					public void onComplete() {
-						Log.d(TAG, "onComplete");
-					}
-				});
-
-
-

还是一样,直接看SubscribeOn()

-
- -
-
1
-2
-3
-4
-
-
public final Observable<T> subscribeOn(Scheduler scheduler) {
-			ObjectHelper.requireNonNull(scheduler, "scheduler is null");
-			return RxJavaPlugins.onAssembly(new ObservableSubscribeOn<T>(this, scheduler));
-		}
-
-
-

返回值Observable情理之中,return返回RxJavaPlugins.onAssembly()也是一样,两点不同:

-
    -
  • 装饰类(也就是上文说的适配器)是ObservableSubscribeOn
  • -
  • 传入参数为一个Scheduler
  • -
-

进入ObservableSubscribeOn

-
- -
-
 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
-10
-11
-12
-13
-14
-15
-
-
public final class ObservableSubscribeOn<T> extends AbstractObservableWithUpstream<T, T> {
-	final Scheduler scheduler;
+			&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+			&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;subscribeActual&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Observer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;super&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;U&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+				&lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;subscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MapObserver&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;U&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;));&lt;/span&gt;
+			&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+		&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;我们发现,ObservableMap做的事情很少,就三件事,第一:在构造方法中,将传入的Observable也就是本身抛给父类(ObservableSource是Observable的父类,所以可以接受);第二:对转换逻辑funtion进行保存;第三:重写subscribeActual()方法并在其中实现&lt;strong&gt;订阅&lt;/strong&gt;,这里与ObservableCreate是一样的,只是传递的参数不同&lt;/p&gt;
+&lt;blockquote&gt;
+&lt;p&gt;小结:create以及对大多数操作符的第一层适配器中都会重写subscribeActual()并实现&lt;strong&gt;订阅&lt;/strong&gt;逻辑&lt;/p&gt;
+&lt;/blockquote&gt;
+&lt;p&gt;我们并没有在ObservableMap的代码中发现进行类型转换的代码,不要心急,有的同学估计已经发现了,这里的进行订阅操作的&lt;code&gt;source.subscribe()&lt;/code&gt;传入的参数类型改变了 ,之前是CreateEmitter,现在变为了一个叫MapObserver的类,我们知道CreateEmitter中实现了那四个常用的方法并制定了相关规则,所以你推测MapObserver中做了同样的操作,其实不是的,但也差不了太多,除onNext()之外的三个方法是在它的父类BasicFuseableObserver中重写的,MapObserver中只对onNext()进行的重写,而且在其中&lt;strong&gt;进行了数据类型转换&lt;/strong&gt;的工作,我们看一下源码(这里我们只看onNext()部分就可以了)&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;MapObserver&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;U&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;BasicFuseableObserver&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;U&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+			&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+			&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onNext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+				&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;done&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+					&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
+				&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+				&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sourceMode&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;NONE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+					&lt;span class=&#34;n&#34;&gt;actual&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;onNext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+					&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
+				&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+				&lt;span class=&#34;n&#34;&gt;U&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
+				&lt;span class=&#34;k&#34;&gt;try&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+					&lt;span class=&#34;n&#34;&gt;v&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ObjectHelper&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;requireNonNull&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mapper&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;apply&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;The mapper function returned a null value.&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+				&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;catch&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Throwable&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ex&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+					&lt;span class=&#34;n&#34;&gt;fail&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ex&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+					&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
+				&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+				&lt;span class=&#34;n&#34;&gt;actual&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;onNext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+			&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+			&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;
+		&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;可以看到再代码中利用ObjectHelper将上游传过来的T,转换成了下游需要的U&lt;/p&gt;
+&lt;blockquote&gt;
+&lt;p&gt;到这里你对.map()下的数据流动也一定有了一定的理解:&lt;br/&gt;
+&amp;ndash;&amp;gt; &lt;code&gt;e.onNext(&amp;quot;next&amp;quot;)&lt;/code&gt;&lt;br/&gt;
+&amp;ndash;&amp;gt; &lt;code&gt;ObservableMap.MapObserver.onNext (&amp;quot;next&amp;quot;)&lt;/code&gt;&lt;br/&gt;
+&amp;ndash;&amp;gt; &lt;code&gt;Observer.onNext(&amp;quot;next&amp;quot;)&lt;/code&gt; &lt;br/&gt;
+&amp;ndash;&amp;gt; &lt;code&gt;Log.d(TAG, &amp;quot;onNext: &amp;quot;+value)&lt;/code&gt;&lt;br/&gt;
+订阅的发送顺序:&lt;br/&gt;
+&amp;ndash;&amp;gt; &lt;code&gt;.subscribe(observer)&lt;/code&gt;&lt;br/&gt;
+&amp;ndash;&amp;gt; &lt;code&gt;ObservableMap.subscribeActual(observer)&lt;/code&gt;&lt;br/&gt;
+&amp;ndash;&amp;gt; &lt;code&gt;ObservableCreate.subscribeActual(new MapObserver(observer,function))&lt;/code&gt;&lt;/p&gt;
+&lt;/blockquote&gt;
+&lt;hr&gt;
+&lt;p&gt;下面进入&lt;strong&gt;线程调度&lt;/strong&gt;源码分析的阶段,先看subscribeOn()。&lt;/p&gt;
+&lt;h2 id=&#34;线程调度-subscribeon&#34;&gt;线程调度-subscribeOn()&lt;/h2&gt;
+&lt;p&gt;老规矩,先来一个参考代码&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;n&#34;&gt;Observable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;create&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ObservableOnSubscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+			&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+			&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;subscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ObservableEmitter&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;throws&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Exception&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+				&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;onNext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;next&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+				&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;onComplete&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;();&lt;/span&gt;
+			&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+		&lt;span class=&#34;o&#34;&gt;}).&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;subscribeOn&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Schedulers&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;io&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;())&lt;/span&gt;
+				&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;subscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Observer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+					&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+					&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onSubscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Disposable&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+						&lt;span class=&#34;n&#34;&gt;Log&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TAG&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;onSubscribe: &amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+					&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+					&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+					&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onNext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+						&lt;span class=&#34;n&#34;&gt;Log&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TAG&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;onNext: &amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+					&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+					&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+					&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onError&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Throwable&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+						&lt;span class=&#34;n&#34;&gt;Log&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TAG&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;onError: &amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+					&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+					&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+					&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onComplete&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+						&lt;span class=&#34;n&#34;&gt;Log&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TAG&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;onComplete&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+					&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+				&lt;span class=&#34;o&#34;&gt;});&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;还是一样,直接看SubscribeOn()&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Observable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;subscribeOn&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Scheduler&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;scheduler&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+			&lt;span class=&#34;n&#34;&gt;ObjectHelper&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;requireNonNull&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;scheduler&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;scheduler is null&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+			&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RxJavaPlugins&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;onAssembly&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ObservableSubscribeOn&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;scheduler&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;));&lt;/span&gt;
+		&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;返回值Observable情理之中,return返回RxJavaPlugins.onAssembly()也是一样,两点不同:&lt;/p&gt;
+&lt;ul&gt;
+&lt;li&gt;装饰类(也就是上文说的适配器)是ObservableSubscribeOn&lt;/li&gt;
+&lt;li&gt;传入参数为一个Scheduler&lt;/li&gt;
+&lt;/ul&gt;
+&lt;p&gt;进入ObservableSubscribeOn&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;ObservableSubscribeOn&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;AbstractObservableWithUpstream&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+	&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Scheduler&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;scheduler&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
 
-	public ObservableSubscribeOn(ObservableSource<T> source, Scheduler scheduler) {
-		super(source);
-		this.scheduler = scheduler;
-	}
+	&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ObservableSubscribeOn&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ObservableSource&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Scheduler&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;scheduler&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+		&lt;span class=&#34;kd&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+		&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;scheduler&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;scheduler&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
+	&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
 
-	@Override
-	public void subscribeActual(final Observer<? super T> s) {
-		final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(s);
-		s.onSubscribe(parent);
-		parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));
-	}
-}
-
-
-

根据经验,构造中进行调用父类、存值一些操作,没什么可看的,直接看订阅的实现subscribeActual()方法,可以看见,这次对下游观察者进行封装的适配器是SubscribeOnObserver类,根据CreateEmitter、MapObserver的经验,我们可以猜测出它或它的父类肯定实现了那四个方法,下面我们看一下

-
- -
-
 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
-
-
static final class SubscribeOnObserver<T> extends AtomicReference<Disposable> implements Observer<T>, Disposable {
+	&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+	&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;subscribeActual&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Observer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;super&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+		&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SubscribeOnObserver&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;parent&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SubscribeOnObserver&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+		&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;onSubscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parent&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+		&lt;span class=&#34;n&#34;&gt;parent&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setDisposable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;scheduler&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;scheduleDirect&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SubscribeTask&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parent&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)));&lt;/span&gt;
+	&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;根据经验,构造中进行调用父类、存值一些操作,没什么可看的,直接看订阅的实现subscribeActual()方法,可以看见,这次对下游观察者进行封装的适配器是SubscribeOnObserver类,根据CreateEmitter、MapObserver的经验,我们可以猜测出它或它的父类肯定实现了那四个方法,下面我们看一下&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;44
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;45
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;46
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;47
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;48
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;SubscribeOnObserver&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;AtomicReference&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Disposable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Observer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Disposable&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
 
-	private static final long serialVersionUID = 8094547886072529208L;
+	&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;serialVersionUID&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;8094547886072529208L&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
 
-	final Observer<? super T> actual;
+	&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Observer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;super&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;actual&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
 
-	final AtomicReference<Disposable> s;
+	&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;AtomicReference&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Disposable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
 
-	SubscribeOnObserver(Observer<? super T> actual) {
-		this.actual = actual;
-		this.s = new AtomicReference<Disposable>();
-	}
+	&lt;span class=&#34;n&#34;&gt;SubscribeOnObserver&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Observer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;super&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;actual&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+		&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;actual&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;actual&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
+		&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;AtomicReference&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Disposable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;();&lt;/span&gt;
+	&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
 
-	@Override
-	public void onSubscribe(Disposable s) {
-		DisposableHelper.setOnce(this.s, s);
-	}
+	&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+	&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onSubscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Disposable&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+		&lt;span class=&#34;n&#34;&gt;DisposableHelper&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setOnce&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+	&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
 
-	@Override
-	public void onNext(T t) {
-		actual.onNext(t);
-	}
+	&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+	&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onNext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+		&lt;span class=&#34;n&#34;&gt;actual&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;onNext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+	&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
 
-	@Override
-	public void onError(Throwable t) {
-		actual.onError(t);
-	}
+	&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+	&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onError&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Throwable&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+		&lt;span class=&#34;n&#34;&gt;actual&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;onError&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+	&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
 
-	@Override
-	public void onComplete() {
-		actual.onComplete();
-	}
+	&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+	&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onComplete&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+		&lt;span class=&#34;n&#34;&gt;actual&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;onComplete&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;();&lt;/span&gt;
+	&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
 
-	@Override
-	public void dispose() {
-		DisposableHelper.dispose(s);
-		DisposableHelper.dispose(this);
-	}
+	&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+	&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;dispose&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+		&lt;span class=&#34;n&#34;&gt;DisposableHelper&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;dispose&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+		&lt;span class=&#34;n&#34;&gt;DisposableHelper&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;dispose&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+	&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
 
-	@Override
-	public boolean isDisposed() {
-		return DisposableHelper.isDisposed(get());
-	}
+	&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+	&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;boolean&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;isDisposed&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+		&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;DisposableHelper&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;isDisposed&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;());&lt;/span&gt;
+	&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
 
-	void setDisposable(Disposable d) {
-		DisposableHelper.setOnce(this, d);
-	}
-}
-
-
-

除去构造、四个方法、基本的存储语句就剩下一个setDisposable()方法了,如果你对Scheduler有研究,你就知道在Scheduler中真正处理线程调用逻辑的是Worker类,这里setDisposable()的作用就是将你传入的Scheduler返回的worker加入管理。

-

目光回到subscribeActual()中,调用观察者的onSubscribe()之后,马上调用了parent.setDisposable(),这里停一下,你可以翻上去观察一下其他方法的subscribeActual()部分,都是在这时候执行订阅操作,但是我们在这里并没有发现,订阅操作不可能没有发生,那么是不是发生在了parent.setDisposable()这个方法里面呢?我们之前只关注了这个方法的内容,对于传入的参数还没有解析,我们现在看一下,希望有新的发现。

-

传入的参数是scheduler.scheduleDirect(new SubscribeTask(parent))。 -先看SubscribeTask这个类

-
- -
-
 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
-10
-11
-12
-
-
final class SubscribeTask implements Runnable {
-	private final SubscribeOnObserver<T> parent;
+	&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;setDisposable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Disposable&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+		&lt;span class=&#34;n&#34;&gt;DisposableHelper&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setOnce&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+	&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;除去构造、四个方法、基本的存储语句就剩下一个setDisposable()方法了,如果你对Scheduler有研究,你就知道在Scheduler中真正处理线程调用逻辑的是Worker类,这里setDisposable()的作用就是将你传入的Scheduler返回的worker加入管理。&lt;/p&gt;
+&lt;p&gt;目光回到subscribeActual()中,调用观察者的onSubscribe()之后,马上调用了parent.setDisposable(),这里停一下,你可以翻上去观察一下其他方法的subscribeActual()部分,都是在这时候执行&lt;strong&gt;订阅&lt;/strong&gt;操作,但是我们在这里并没有发现,订阅操作不可能没有发生,那么是不是发生在了parent.setDisposable()这个方法里面呢?我们之前只关注了这个方法的内容,对于传入的参数还没有解析,我们现在看一下,希望有新的发现。&lt;/p&gt;
+&lt;p&gt;传入的参数是&lt;code&gt;scheduler.scheduleDirect(new SubscribeTask(parent))&lt;/code&gt;。
+先看SubscribeTask这个类&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;SubscribeTask&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Runnable&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+	&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SubscribeOnObserver&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;parent&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
 
-	SubscribeTask(SubscribeOnObserver<T> parent) {
-		this.parent = parent;
-	}
+	&lt;span class=&#34;n&#34;&gt;SubscribeTask&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SubscribeOnObserver&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;parent&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+		&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;parent&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;parent&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
+	&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
 
-	@Override
-	public void run() {
-		source.subscribe(parent);
-	}
-}
-
-
-

这个类继承Runnable,所以实现了一个子线程,在run()中执行操作,没错,source.subscribe(parent);,熟悉的语句,这就证明了这里的订阅操作发生在了Scheduler的线程中。

-

我们继续看scheduleDirect()这个方法

-
- -
-
1
-2
-3
-
-
public Disposable scheduleDirect(@NonNull Runnable run) {
-	return scheduleDirect(run, 0L, TimeUnit.NANOSECONDS);
-}
-
-
-

继续

-
- -
-
 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
-10
-11
-
-
public Disposable scheduleDirect(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) {
-	final Worker w = createWorker();
+	&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+	&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+		&lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;subscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parent&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+	&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;这个类继承Runnable,所以实现了一个子线程,在run()中执行操作,没错,&lt;code&gt;source.subscribe(parent);&lt;/code&gt;,熟悉的语句,这就证明了这里的&lt;strong&gt;订阅&lt;/strong&gt;操作发生在了Scheduler的线程中。&lt;/p&gt;
+&lt;p&gt;我们继续看scheduleDirect()这个方法&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Disposable&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;scheduleDirect&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@NonNull&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Runnable&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+	&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;scheduleDirect&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;0L&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TimeUnit&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;NANOSECONDS&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;继续&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Disposable&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;scheduleDirect&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@NonNull&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Runnable&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;delay&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nd&#34;&gt;@NonNull&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TimeUnit&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;unit&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+	&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Worker&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;w&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;createWorker&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;();&lt;/span&gt;
 
-	final Runnable decoratedRun = RxJavaPlugins.onSchedule(run);
+	&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Runnable&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;decoratedRun&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RxJavaPlugins&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;onSchedule&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
 
-	DisposeTask task = new DisposeTask(decoratedRun, w);
+	&lt;span class=&#34;n&#34;&gt;DisposeTask&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;task&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;DisposeTask&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;decoratedRun&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
 
-	w.schedule(task, delay, unit);
+	&lt;span class=&#34;n&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;schedule&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;task&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;delay&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;unit&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
 
-	return task;
-}
-
-
-

我们可以发现,传入的子线程被包装配置之后,开始在Worker也就是Scheduler线程中执行 -我们继续看DisposeTask这个类,具体的订阅子线程的启动就在这里

-
- -
-
 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
-
-
static final class DisposeTask implements Runnable, Disposable {
-	final Runnable decoratedRun;
-	final Worker w;
+	&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;task&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;我们可以发现,传入的子线程被包装配置之后,开始在Worker也就是Scheduler线程中执行
+我们继续看DisposeTask这个类,具体的订阅子线程的启动就在这里&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;DisposeTask&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Runnable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Disposable&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+	&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Runnable&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;decoratedRun&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
+	&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Worker&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
 
-	Thread runner;
+	&lt;span class=&#34;n&#34;&gt;Thread&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;runner&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
 
-	DisposeTask(Runnable decoratedRun, Worker w) {
-		this.decoratedRun = decoratedRun;
-		this.w = w;
-	}
+	&lt;span class=&#34;n&#34;&gt;DisposeTask&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Runnable&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;decoratedRun&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Worker&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+		&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;decoratedRun&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;decoratedRun&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
+		&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;w&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
+	&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
 
-	@Override
-	public void run() {
-		runner = Thread.currentThread();
-		try {
-			decoratedRun.run();
-		} finally {
-			dispose();
-			runner = null;
-		}
-	}
+	&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+	&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+		&lt;span class=&#34;n&#34;&gt;runner&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Thread&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;currentThread&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;();&lt;/span&gt;
+		&lt;span class=&#34;k&#34;&gt;try&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+			&lt;span class=&#34;n&#34;&gt;decoratedRun&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;();&lt;/span&gt;
+		&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;finally&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+			&lt;span class=&#34;n&#34;&gt;dispose&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;();&lt;/span&gt;
+			&lt;span class=&#34;n&#34;&gt;runner&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
+		&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+	&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
 
-	@Override
-	public void dispose() {
-		if (runner == Thread.currentThread() && w instanceof NewThreadWorker) {
-			((NewThreadWorker) w).shutdown();
-		} else {
-			w.dispose();
-		}
-	}
+	&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+	&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;dispose&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+		&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;runner&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Thread&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;currentThread&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;w&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;instanceof&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;NewThreadWorker&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+			&lt;span class=&#34;o&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;NewThreadWorker&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;shutdown&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;();&lt;/span&gt;
+		&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+			&lt;span class=&#34;n&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;dispose&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;();&lt;/span&gt;
+		&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+	&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
 
-	@Override
-	public boolean isDisposed() {
-		return w.isDisposed();
-	}
-}
-
-
-

可以看到run()中调用了ecoratedRun.run();来启动线程,注意这里是使用的run()而不是start(),而且整个rxjava流程走完后会自己调用dispose();关闭线程。

-
-

到这里,你应该明白了subscribeOn()线程调度的过程,正如它的效果描述一样:将观察者的操作运行在Scheduler.io()线程中
-–> subscribeOn(Schedulers.io())
-–> 返回一个ObservableSubscribeOn的包装类
-–> 当上游的被观察者被订阅之后,回调ObservableSubscribeOn包装类中的subscribeActual()
-–> 线程切换至Schedulers.io(),并进行订阅操作source.subscribe(parent)

-
-
-

理顺思路之后我们发现,这里订阅,模式与之前相同,还是下游观察者对上游被观察者进行订阅,依旧是自下向上的,但是我们通过之前的源码分析知道,上游发送数据时调用的那个四个方法实际是调用下游观察者对应重写的四个方法,所以这边满足了线程调度的目的:将观察者所做的操作置与Schedulers.io()线程中

-
-
-

并且,我们这里还可以解释一个问题 -为什么subscribeOn(Schedulers.xxx())切换线程N次,总是以第一次为准? -我们知道使用subscribeOn()进行线程调度时订阅的顺序是从下往上,所以有多个subscribeOn()时,从最后一个开始执行,一直执行到第一个,最后的结果还是以第一个为准

-
-
-

然后看obsweveOn(),有了上面subscribeOn()的经验,分析obsweveOn()就快了

-

线程调度-observeOn()

-

实例

-
- -
-
 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
-
-
Observable.create(new ObservableOnSubscribe<String>() {
-			@Override
-			public void subscribe(ObservableEmitter<String> e) throws Exception {
-				e.onNext("next");
-				e.onComplete();
-			}
-		}).subscribeOn(Schedulers.io())
-				.observeOn(AndroidSchedulers.mainThread())
-				.subscribe(new Observer<String>() {
-					@Override
-					public void onSubscribe(Disposable d) {
-						Log.d(TAG, "onSubscribe: " + d);
-					}
+	&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+	&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;boolean&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;isDisposed&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+		&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;isDisposed&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;();&lt;/span&gt;
+	&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;可以看到run()中调用了&lt;code&gt;ecoratedRun.run();&lt;/code&gt;来启动线程,注意&lt;strong&gt;这里是使用的run()而不是start()&lt;/strong&gt;,而且整个rxjava流程走完后会自己调用&lt;code&gt;dispose();&lt;/code&gt;关闭线程。&lt;/p&gt;
+&lt;blockquote&gt;
+&lt;p&gt;到这里,你应该明白了subscribeOn()线程调度的过程,正如它的效果描述一样:将观察者的操作运行在Scheduler.io()线程中&lt;br/&gt;
+&amp;ndash;&amp;gt; subscribeOn(Schedulers.io())&lt;br/&gt;
+&amp;ndash;&amp;gt; 返回一个ObservableSubscribeOn的包装类&lt;br/&gt;
+&amp;ndash;&amp;gt; 当上游的被观察者被订阅之后,回调ObservableSubscribeOn包装类中的subscribeActual()&lt;br/&gt;
+&amp;ndash;&amp;gt; 线程切换至Schedulers.io(),并进行&lt;strong&gt;订阅操作&lt;/strong&gt;&lt;code&gt;source.subscribe(parent)&lt;/code&gt;&lt;/p&gt;
+&lt;/blockquote&gt;
+&lt;blockquote&gt;
+&lt;p&gt;理顺思路之后我们发现,这里订阅,模式与之前相同,还是下游观察者对上游被观察者进行订阅,依旧是自下向上的,但是我们通过之前的源码分析知道,上游发送数据时调用的那个四个方法实际是调用下游观察者对应重写的四个方法,所以这边满足了线程调度的目的:将观察者所做的操作置与Schedulers.io()线程中&lt;/p&gt;
+&lt;/blockquote&gt;
+&lt;blockquote&gt;
+&lt;p&gt;并且,我们这里还可以解释一个问题
+&lt;strong&gt;为什么subscribeOn(Schedulers.xxx())切换线程N次,总是以第一次为准&lt;/strong&gt;?
+我们知道使用subscribeOn()进行线程调度时订阅的顺序是从下往上,所以有多个subscribeOn()时,从最后一个开始执行,一直执行到第一个,最后的结果还是以第一个为准&lt;/p&gt;
+&lt;/blockquote&gt;
+&lt;hr&gt;
+&lt;p&gt;然后看obsweveOn(),有了上面subscribeOn()的经验,分析obsweveOn()就快了&lt;/p&gt;
+&lt;h2 id=&#34;线程调度-observeon&#34;&gt;线程调度-observeOn()&lt;/h2&gt;
+&lt;p&gt;实例&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;n&#34;&gt;Observable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;create&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ObservableOnSubscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+			&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+			&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;subscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ObservableEmitter&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;throws&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Exception&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+				&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;onNext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;next&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+				&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;onComplete&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;();&lt;/span&gt;
+			&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+		&lt;span class=&#34;o&#34;&gt;}).&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;subscribeOn&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Schedulers&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;io&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;())&lt;/span&gt;
+				&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;observeOn&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AndroidSchedulers&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;mainThread&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;())&lt;/span&gt;
+				&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;subscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Observer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+					&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+					&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onSubscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Disposable&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+						&lt;span class=&#34;n&#34;&gt;Log&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TAG&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;onSubscribe: &amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+					&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
 
-					@Override
-					public void onNext(String value) {
-						Log.d(TAG, "onNext: " + value);
-					}
+					&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+					&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onNext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+						&lt;span class=&#34;n&#34;&gt;Log&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TAG&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;onNext: &amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+					&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
 
-					@Override
-					public void onError(Throwable e) {
-						Log.d(TAG, "onError: " + e);
-					}
+					&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+					&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onError&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Throwable&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+						&lt;span class=&#34;n&#34;&gt;Log&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TAG&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;onError: &amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+					&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
 
-					@Override
-					public void onComplete() {
-						Log.d(TAG, "onComplete: ");
-					}
-				});
-
-
-

observeOn()

-
- -
-
1
-2
-3
-
-
public final Observable<T> observeOn (Scheduler scheduler){
-	return observeOn(scheduler, false, bufferSize());
-}
-
-
-

没看见RxJavaPlugins.onAssembly(),担心不一样?不存在的,被包了一层而已

-
- -
-
1
-2
-3
-4
-5
-
-
public final Observable<T> observeOn (Scheduler scheduler,boolean delayError, int bufferSize){
-	ObjectHelper.requireNonNull(scheduler, "scheduler is null");
-	ObjectHelper.verifyPositive(bufferSize, "bufferSize");
-	return RxJavaPlugins.onAssembly(new ObservableObserveOn<T>(this, scheduler, delayError, bufferSize));
-}
-
-
-

还是那个顺序,ObservableObserveOn

-
- -
-
 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
-10
-11
-12
-13
-14
-15
-16
-17
-18
-19
-20
-21
-22
-
-
public final class ObservableObserveOn<T> extends AbstractObservableWithUpstream<T, T> {
-	final Scheduler scheduler;
-	final boolean delayError;
-	final int bufferSize;
+					&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+					&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onComplete&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+						&lt;span class=&#34;n&#34;&gt;Log&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TAG&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;onComplete: &amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+					&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+				&lt;span class=&#34;o&#34;&gt;});&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;observeOn()&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Observable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;observeOn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Scheduler&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;scheduler&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;){&lt;/span&gt;
+	&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;observeOn&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;scheduler&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;bufferSize&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;());&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;没看见RxJavaPlugins.onAssembly(),担心不一样?不存在的,被包了一层而已&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Observable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;observeOn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Scheduler&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;scheduler&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;boolean&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;delayError&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;bufferSize&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;){&lt;/span&gt;
+	&lt;span class=&#34;n&#34;&gt;ObjectHelper&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;requireNonNull&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;scheduler&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;scheduler is null&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+	&lt;span class=&#34;n&#34;&gt;ObjectHelper&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;verifyPositive&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bufferSize&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;bufferSize&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+	&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RxJavaPlugins&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;onAssembly&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ObservableObserveOn&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;scheduler&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;delayError&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;bufferSize&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;));&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;还是那个顺序,ObservableObserveOn&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;ObservableObserveOn&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;AbstractObservableWithUpstream&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+	&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Scheduler&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;scheduler&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
+	&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;boolean&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;delayError&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
+	&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;bufferSize&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
 
-	public ObservableObserveOn(ObservableSource<T> source, Scheduler scheduler, boolean delayError, int bufferSize) {
-		super(source);
-		this.scheduler = scheduler;
-		this.delayError = delayError;
-		this.bufferSize = bufferSize;
-	}
+	&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ObservableObserveOn&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ObservableSource&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Scheduler&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;scheduler&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;boolean&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;delayError&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;bufferSize&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+		&lt;span class=&#34;kd&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+		&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;scheduler&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;scheduler&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
+		&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;delayError&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;delayError&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
+		&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;bufferSize&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;bufferSize&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
+	&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
 
-	@Override
-	protected void subscribeActual(Observer<? super T> observer) {
-		if (scheduler instanceof TrampolineScheduler) {
-			source.subscribe(observer);
-		} else {
-			Scheduler.Worker w = scheduler.createWorker();
-			source.subscribe(new ObserveOnObserver<T>(observer, w, delayError, bufferSize));
-		}
-	}
-}
-
-
-

看subscribeActual(),很好理解,就是先判断是不是在主线程,是的话,直接订阅完事,不是的话跳到主线程去,在订阅,切换线程依旧是使用的Worker那一套,与subscribeOn()中类似,先创建一个主线程的Worker,然后把Worker放进观察者的包装类ObserveOnObserver中,不用多说,里面肯定有对那四个方法的实现,我这里简化一下他的代码

-
- -
-
 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
-10
-11
-12
-13
-14
-15
-16
-17
-18
-19
-20
-21
-
-
static final class ObserveOnObserver<T> extends BasicIntQueueDisposable<T> implements Observer<T>, Runnable {
+	&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+	&lt;span class=&#34;kd&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;subscribeActual&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Observer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;super&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;observer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+		&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;scheduler&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;instanceof&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TrampolineScheduler&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+			&lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;subscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;observer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+		&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+			&lt;span class=&#34;n&#34;&gt;Scheduler&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;Worker&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;w&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;scheduler&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;createWorker&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;();&lt;/span&gt;
+			&lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;subscribe&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ObserveOnObserver&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;observer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;delayError&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;bufferSize&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;));&lt;/span&gt;
+		&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+	&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;看subscribeActual(),很好理解,就是先判断是不是在主线程,是的话,直接&lt;strong&gt;订阅&lt;/strong&gt;完事,不是的话跳到主线程去,在&lt;strong&gt;订阅&lt;/strong&gt;,切换线程依旧是使用的Worker那一套,与subscribeOn()中类似,先创建一个主线程的Worker,然后把Worker放进观察者的包装类ObserveOnObserver中,不用多说,里面肯定有对那四个方法的实现,我这里简化一下他的代码&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;ObserveOnObserver&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;BasicIntQueueDisposable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Observer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Runnable&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
 
-	@Override
-	public void onNext(T t) {
-		if (done) {
-			return;
-		}
-		if (sourceMode != QueueDisposable.ASYNC) {
-			queue.offer(t);
-		}
-		schedule();
-	}
+	&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;
+	&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;onNext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+		&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;done&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+			&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;
+		&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+		&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sourceMode&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;QueueDisposable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;ASYNC&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+			&lt;span class=&#34;n&#34;&gt;queue&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;offer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+		&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+		&lt;span class=&#34;n&#34;&gt;schedule&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;();&lt;/span&gt;
+	&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
 	
-	...
+	&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;
 	
-	void schedule() {
-		if (getAndIncrement() == 0) {
-			worker.schedule(this);
-		}
-	}
-}
-
-
-

其他那三个方法与onNext()大致相同,只看这一个就可以了,schedule();这行代码上面都是取数据的操作,并没有对数据进行发送,所以说即使使用线程调用将被观察者的操作放在主线程,他的数据准备阶段仍然是在原线程执行的,当schedule();执行后,进入上面传入Workder线程,也就是主线程,然后才将queue中的T取出,继而发送给下游的观察者。其他方法也是一样的流程,比如onError()、onComplete(),都是将错误或完成的信息先保存,等待切换线程后在执行发送操作。

-
-

由此,我们可知ObserverOn()是向下作用的,每次调用都对下游的代码产生作用,所以多次调用ObserverOn(),是最后一次生效的

-
-
-

好了,Rxjava的源码分析到这里结束了,文中有很多没有讲到的地方,日后有时间的会继续讲解剩余部分。

-

总结

-

我们来整理一下文中出现的各个装饰者

-
- -
-
 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
-10
-11
-12
-13
-14
-15
-16
-17
-18
-19
-20
-
-
Observable.create()
+	&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;schedule&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+		&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;getAndIncrement&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
+			&lt;span class=&#34;n&#34;&gt;worker&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;schedule&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;
+		&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+	&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+&lt;/div&gt;
+&lt;/div&gt;&lt;p&gt;其他那三个方法与onNext()大致相同,只看这一个就可以了,&lt;code&gt;schedule();&lt;/code&gt;这行代码上面都是取数据的操作,并没有对数据进行发送,所以说即使使用线程调用将被观察者的操作放在主线程,他的数据准备阶段仍然是在原线程执行的,当&lt;code&gt;schedule();&lt;/code&gt;执行后,进入上面传入Workder线程,也就是主线程,然后才将queue中的T取出,继而发送给下游的观察者。其他方法也是一样的流程,比如onError()、onComplete(),都是将错误或完成的信息先保存,等待切换线程后在执行发送操作。&lt;/p&gt;
+&lt;blockquote&gt;
+&lt;p&gt;由此,我们可知ObserverOn()是向下作用的,每次调用都对下游的代码产生作用,所以多次调用ObserverOn(),是最后一次生效的&lt;/p&gt;
+&lt;/blockquote&gt;
+&lt;hr&gt;
+&lt;p&gt;好了,Rxjava的源码分析到这里结束了,文中有很多没有讲到的地方,日后有时间的会继续讲解剩余部分。&lt;/p&gt;
+&lt;h1 id=&#34;总结&#34;&gt;总结&lt;/h1&gt;
+&lt;p&gt;我们来整理一下文中出现的各个装饰者&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;Observable.create()
 	- ObservableCreate
 	- CreateEmitter
 Observable.map()
@@ -2902,18 +2902,18 @@ Observable.observeOn():
 第二层装饰者的作用:
 	- 对观察者进行适配
 	- 根据自己的需求实现onNext()、onError()、onComplete()...等上层定义的方法
-
-
-

本文中,我们对create()、subscribe()、map()、subscribeOn()、observeOn()的源码进行的阅读,想你已经可以从源码的角度回答以下问题:

-
    -
  • 被观察者如何发送数据?
  • -
  • 观察者如何接受数据?
  • -
  • 操作符的实现原理是什么?
  • -
  • Map关键字是如何实现类型转换的?
  • -
  • 线程调度是如何实现的?
  • -
  • 为什么多次调用subscribeOn(),只有第一次生效?
  • -
  • 为什么多次调用observeOn(),只有最后一次生效?
  • -
]]>
+&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; +&lt;/div&gt; +&lt;/div&gt;&lt;p&gt;本文中,我们对create()、subscribe()、map()、subscribeOn()、observeOn()的源码进行的阅读,想你已经可以从源码的角度回答以下问题:&lt;/p&gt; +&lt;ul&gt; +&lt;li&gt;被观察者如何发送数据?&lt;/li&gt; +&lt;li&gt;观察者如何接受数据?&lt;/li&gt; +&lt;li&gt;操作符的实现原理是什么?&lt;/li&gt; +&lt;li&gt;Map关键字是如何实现类型转换的?&lt;/li&gt; +&lt;li&gt;线程调度是如何实现的?&lt;/li&gt; +&lt;li&gt;为什么多次调用subscribeOn(),只有第一次生效?&lt;/li&gt; +&lt;li&gt;为什么多次调用observeOn(),只有最后一次生效?&lt;/li&gt; +&lt;/ul&gt;]]>
@@ -2923,163 +2923,163 @@ Observable.observeOn(): https://folay.top/post/app_start_process/ 本文讲解从开机到app显示画面的流程,但不分析源码,如果想阅读源码请到参考文章中查阅。 本文把这段流程分为三部分: 从开机到显示应用列表 从点击应 - 本文讲解从开机到app显示画面的流程,但不分析源码,如果想阅读源码请到参考文章中查阅。

-

本文把这段流程分为三部分:

-
    -
  • 从开机到显示应用列表
  • -
  • 从点击应用图标到Activity创建成功
  • -
  • 从Activity创建成功到显示画面
  • -
-

从开机到显示应用列表

-

先看流程图: -

-

开机加电后,CPU先执行预设代码、加载ROM中的引导程序Bootloader和Linux内核到RAM内存中去,然后初始化各种软硬件环境、加载驱动程序、挂载根文件系统,执行init进程

-

init进程会启动各种系统本地服务,如SM(ServiceManager)、MS(Media Server)、bootanim(开机动画)等,然后init进程会在解析init.rc文件后fork()出Zygote进程

-

Zygote会启动Java虚拟机,通过jni进入Zygote的java代码中,并创建socket实现IPC进程通讯,然后启动SS(SystemServer)进程。

-

SS进程负责启动和管理整个framework,包括AMS(ActivityManagerService)、WMS(WindowManagerService)、PMS(PowerManagerService)等服务、同时启动binder线程池,当SS进程将系统服务启动就绪以后,就会通知AMS启动Home。

-

AMS通过Intent隐式启动的方式启动Launcher,Launcher根据已安装应用解析对应的xml、通过findViewById()获得一个RecycleView、加载应用图标、最后成功展示App列表。

-

解释

-
    -
  • 预设代码:cpu制造厂商会预设一个地址,这个地址是各厂家约定统一的,Android手机会将固态存储设备ROM预先映射到该地址上;
  • -
  • Bootloader:类似BIOS,在系统加载前,用以初始化硬件设备,建立内存空间的映像图,为最终调用系统内核准备好环境;
  • -
  • init进程:init进程时Android系统中用户进程的鼻祖进程,主要作用是启动系统本地服务、fork出Zygote进程;
  • -
  • SM:ServiceManager是一个守护进程,它维护着系统服务和客户端的binder通信;
  • -
  • Zygote进程:Zygote进程是所有Java进程的父进程,我们的APP都是由Zygote进程fork出来的;
  • -
  • socket:一种独立于协议用于两个应用程序之间的数据传输的网络编程接口,是IPC中的一种;(但是在Android中一般使用Binder来实现IPC,这里使用socket的原因后面有写到)
  • -
  • SS:Framework两大重要进程之一(另一个是Zygote),载着framework的核心服务,系统里面重要的服务都是SS开启的;
  • -
  • AMS:服务端对象,负责系统中所有Activity的生命周期,打开App、Activity的开启、暂停、关闭都需要AMS来控制;
  • -
  • WMS:窗口管理服务,窗口的启动、添加、删除、大小、层级都是由WMS管理;(下面会解释什么是窗口)
  • -
  • Launcher:Launcher就是系统桌面,主要用来启动应用桌面,同时管理快捷方式和其他组件,本质上也是一个应用程序,和我们的App一样,也是继承自Activity,有自己的AndroidManifest;(所以才可以被AMS用Intent启动)
  • -
-

Question 1: Zygote进程为什么使用Socket而不是Binder? -fork不允许存在多线程,而Binder通讯恰巧就是多线程;

-

Question 2:什么是窗口? -Android系统中的窗体是屏幕上的一块用于绘制各种UI元素并能够响应应用户输入的一个矩形区域,从原理上来讲,窗体的概念是独自占有一个Surface实例的显示区域,比如Dialog、Activity的界面、壁纸、状态栏以及Toast等都是窗体;

-

从点击应用图标到Activity创建成功

-

先看流程图: -

-
- -
-
 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
-
-
//然后点击应用图标后,先检查要打卡的Activity是否存在
---> Launcher.startActivitySafely()
---> Launcher.startActivity()
---> Activity.startActivity()
---> Activity.startActivityForResult()
+      <![CDATA[&lt;p&gt;本文讲解从开机到app显示画面的流程,但不分析源码,如果想阅读源码请到参考文章中查阅。&lt;/p&gt;
+&lt;p&gt;本文把这段流程分为三部分:&lt;/p&gt;
+&lt;ul&gt;
+&lt;li&gt;从开机到显示应用列表&lt;/li&gt;
+&lt;li&gt;从点击应用图标到Activity创建成功&lt;/li&gt;
+&lt;li&gt;从Activity创建成功到显示画面&lt;/li&gt;
+&lt;/ul&gt;
+&lt;h1 id=&#34;从开机到显示应用列表&#34;&gt;从开机到显示应用列表&lt;/h1&gt;
+&lt;p&gt;先看流程图:
+&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/16d3acbbbba41520.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
+&lt;p&gt;开机加电后,CPU先执行&lt;strong&gt;预设代码&lt;/strong&gt;、加载ROM中的引导程序&lt;strong&gt;Bootloader&lt;/strong&gt;和Linux内核到RAM内存中去,然后初始化各种软硬件环境、加载驱动程序、挂载根文件系统,执行&lt;strong&gt;init进程&lt;/strong&gt;。&lt;/p&gt;
+&lt;p&gt;init进程会启动各种系统本地服务,如&lt;strong&gt;SM&lt;/strong&gt;(ServiceManager)、MS(Media Server)、bootanim(开机动画)等,然后init进程会在解析init.rc文件后fork()出&lt;strong&gt;Zygote进程&lt;/strong&gt;。&lt;/p&gt;
+&lt;p&gt;Zygote会启动Java虚拟机,通过jni进入Zygote的java代码中,并创建&lt;strong&gt;socket&lt;/strong&gt;实现IPC进程通讯,然后启动&lt;strong&gt;SS&lt;/strong&gt;(SystemServer)进程。&lt;/p&gt;
+&lt;p&gt;SS进程负责启动和管理整个framework,包括&lt;strong&gt;AMS&lt;/strong&gt;(ActivityManagerService)、&lt;strong&gt;WMS&lt;/strong&gt;(WindowManagerService)、PMS(PowerManagerService)等服务、同时启动binder线程池,当SS进程将系统服务启动就绪以后,就会通知AMS启动Home。&lt;/p&gt;
+&lt;p&gt;AMS通过Intent隐式启动的方式启动&lt;strong&gt;Launcher&lt;/strong&gt;,Launcher根据已安装应用解析对应的xml、通过findViewById()获得一个RecycleView、加载应用图标、最后成功展示App列表。&lt;/p&gt;
+&lt;h2 id=&#34;解释&#34;&gt;解释&lt;/h2&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;strong&gt;预设代码&lt;/strong&gt;:cpu制造厂商会预设一个地址,这个地址是各厂家约定统一的,Android手机会将固态存储设备ROM预先映射到该地址上;&lt;/li&gt;
+&lt;li&gt;&lt;strong&gt;Bootloader&lt;/strong&gt;:类似BIOS,在系统加载前,用以初始化硬件设备,建立内存空间的映像图,为最终调用系统内核准备好环境;&lt;/li&gt;
+&lt;li&gt;&lt;strong&gt;init进程&lt;/strong&gt;:init进程时Android系统中用户进程的鼻祖进程,主要作用是启动系统本地服务、fork出Zygote进程;&lt;/li&gt;
+&lt;li&gt;&lt;strong&gt;SM&lt;/strong&gt;:ServiceManager是一个守护进程,它维护着系统服务和客户端的binder通信;&lt;/li&gt;
+&lt;li&gt;&lt;strong&gt;Zygote进程&lt;/strong&gt;:Zygote进程是所有Java进程的父进程,我们的APP都是由Zygote进程fork出来的;&lt;/li&gt;
+&lt;li&gt;&lt;strong&gt;socket&lt;/strong&gt;:一种独立于协议用于两个应用程序之间的数据传输的网络编程接口,是IPC中的一种;(但是在Android中一般使用Binder来实现IPC,这里使用socket的原因后面有写到)&lt;/li&gt;
+&lt;li&gt;&lt;strong&gt;SS&lt;/strong&gt;:Framework两大重要进程之一(另一个是Zygote),载着framework的核心服务,系统里面重要的服务都是SS开启的;&lt;/li&gt;
+&lt;li&gt;&lt;strong&gt;AMS&lt;/strong&gt;:服务端对象,负责系统中所有Activity的生命周期,打开App、Activity的开启、暂停、关闭都需要AMS来控制;&lt;/li&gt;
+&lt;li&gt;&lt;strong&gt;WMS&lt;/strong&gt;:窗口管理服务,窗口的启动、添加、删除、大小、层级都是由WMS管理;(下面会解释什么是窗口)&lt;/li&gt;
+&lt;li&gt;&lt;strong&gt;Launcher&lt;/strong&gt;:Launcher就是系统桌面,主要用来启动应用桌面,同时管理快捷方式和其他组件,本质上也是一个应用程序,和我们的App一样,也是继承自Activity,有自己的AndroidManifest;(所以才可以被AMS用Intent启动)&lt;/li&gt;
+&lt;/ul&gt;
+&lt;p&gt;&lt;strong&gt;Question 1: Zygote进程为什么使用Socket而不是Binder?&lt;/strong&gt;
+fork不允许存在多线程,而Binder通讯恰巧就是多线程;&lt;/p&gt;
+&lt;p&gt;&lt;strong&gt;Question 2:什么是窗口?&lt;/strong&gt;
+Android系统中的窗体是屏幕上的一块用于绘制各种UI元素并能够响应应用户输入的一个矩形区域,从原理上来讲,窗体的概念是独自占有一个Surface实例的显示区域,比如Dialog、Activity的界面、壁纸、状态栏以及Toast等都是窗体;&lt;/p&gt;
+&lt;h1 id=&#34;从点击应用图标到activity创建成功&#34;&gt;从点击应用图标到Activity创建成功&lt;/h1&gt;
+&lt;p&gt;先看流程图:
+&lt;img src=&#34;https://imgoldjii.oss-cn-beijing.aliyuncs.com/16d3acbc595b7401.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
+&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
+&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;44
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;45
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;46
+&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;47
+&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
+&lt;td class=&#34;lntd&#34;&gt;
+&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//然后点击应用图标后,先检查要打卡的Activity是否存在
+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Launcher&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;startActivitySafely&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Launcher&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;startActivity&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Activity&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;startActivity&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Activity&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;startActivityForResult&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
 
-//然后获取AMS的代理AMP
---> Instrumentation.execStartActivity()
---> ActivityManagerNative.getDefault().startActivity()
---> ActivityManagerProxy.startActivity()
---> ActivityManagerService.startActivity()
---> startActivityAsUser(intent, requestCode, userId)
---> ActivityStackSupervisor.startActivityMayWait()
---> ActivityStackSupervisor.resolveActivity()
---> ActivityStackSupervisor.startActivityLocked()
---> new ActivityRecord对象获取ActivityStack
---> 找到ActivityStack后Launcher.onPause()
+&lt;span class=&#34;c1&#34;&gt;//然后获取AMS的代理AMP
+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Instrumentation&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;execStartActivity&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ActivityManagerNative&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getDefault&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;startActivity&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ActivityManagerProxy&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;startActivity&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ActivityManagerService&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;startActivity&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;startActivityAsUser&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;intent&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;requestCode&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;userId&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ActivityStackSupervisor&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;startActivityMayWait&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ActivityStackSupervisor&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;resolveActivity&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ActivityStackSupervisor&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;startActivityLocked&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ActivityRecord对象&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;获取ActivityStack&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;找到ActivityStack后Launcher&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;onPause&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
 
-//准备启动进程
---> ActivityManagerService.startProcessLocked()
-//通过socket通知Zygote创建进程
---> zygoteSendArgsAndGetResult()
-//创建ActivityThread
---> ActivityThread.main()
-//告诉AMS我已经创建好了
---> ActivityThread.attach()
---> ActivityManagerProxy.attachApplication()
---> ActivityMangerService.attachApplication()
-//找到Application实例并初始化
---> ActivityMangerService.attachApplicationLocked()
+&lt;span class=&#34;c1&#34;&gt;//准备启动进程
+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ActivityManagerService&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;startProcessLocked&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
+&lt;span class=&#34;c1&#34;&gt;//通过socket通知Zygote创建进程
+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;zygoteSendArgsAndGetResult&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
+&lt;span class=&#34;c1&#34;&gt;//创建ActivityThread
+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ActivityThread&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
+&lt;span class=&#34;c1&#34;&gt;//告诉AMS我已经创建好了
+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ActivityThread&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;attach&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ActivityManagerProxy&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;attachApplication&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ActivityMangerService&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;attachApplication&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
+&lt;span class=&#34;c1&#34;&gt;//找到Application实例并初始化
+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ActivityMangerService&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;attachApplicationLocked&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
 
---> ApplicationThread.bindApplication()
-//创建Application
---> AcitvityThread.bindApplication()
---> Application.oncreate()
+&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ApplicationThread&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;bindApplication&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
+&lt;span class=&#34;c1&#34;&gt;//创建Application
+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;AcitvityThread&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;bindApplication&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Application&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;oncreate&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
 
-//启动Activity
---> ActivityStackSupervisor.attachApplicationLocked()
---> ActivityStackSupervisor.realStartActivityLocked()
---> ActivityThread.scheduleLaunchActivity()
+&lt;span class=&#34;c1&#34;&gt;//启动Activity
+&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ActivityStackSupervisor&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;attachApplicationLocked&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ActivityStackSupervisor&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;realStartActivityLocked&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
+&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ActivityThread&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;scheduleLaunchActivity&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
 
-//进入UI线程
---> handleLaunchActivity()
---> performLaunchActivity()
-//创建Activity实例
---> Instrumentation.newActivity()
---> Activity.onCreate()
-
-
-

解释

-
    -
  • ActivityThread:App的真正入口。当开启App之后,会调用main()开始运行,开启消息循环队列,这就是传说中的UI线程或者叫主线程。与ActivityManagerServices配合,一起完成Activity的管理工作;
  • -
  • ApplicationThread:用来实现ActivityManagerService与ActivityThread之间的交互。在ActivityManagerService需要管理相关Application中的Activity的生命周期时,通过ApplicationThread的代理对象与ActivityThread通讯;
  • -
  • Instrumentation:可以理解为应用进程的管家,每个应用程序只有一个,每个Activity内都有该对象的引用,ActivityThread要创建或暂停某个Activity时,都需要通过Instrumentation来进行具体的操作;
  • -
  • ActivityStack:Activity在AMS的栈管理,用来记录已经启动的Activity的先后关系,状态信息等。通过ActivityStack决定是否需要启动新的进程;
  • -
  • ActivityRecord:ActivityStack的管理对象,每个Activity在AMS对应一个ActivityRecord,来记录Activity的状态以及其他的管理信息。其实就是服务器端的Activity对象的映像;
  • -
-

Question 1: 如何判断APP是否已经启动? -AMS会保存一个ProcessRecord信息,有两部分构成,“uid + process”,每个应用工程序都有自己的uid,而process就是AndroidManifest.xml中Application的process属性,默认为package名。每次在新建新进程前的时候会先判断这个 ProcessRecord 是否已存在,如果已经存在就不会新建进程了,这就属于应用内打开 Activity 的过程了。

-

从Activity创建成功到显示画面

-

onCreate()方法中先执行setContentView()方法将对应的xml文件传入,之后会去调用window.setContentView(),最终会在这里创建Decorview并填充标题栏、状态栏,然后获取contentParent,然后调用LayoutInflater.inflate解析xml文件获取根root(ViewRootImpl),通过root.addView()将contentParent添加到ViewRootImpl中去,至此onCreate()结束。

-

开始onResume()阶段,在开始会向H类发送一个消息,然后在ActivityThread中获取之前创建的Decorview并调用windowManager.add(),最后在windowManager中将窗口和窗口的参数传到root.setView(),然后ViewRoot通过Binder调用WMS,使WMS所在的SS进程接收到按键事件时,可以回调到该root,同时ViewRoot会向自己的handler发送一条消息,然后进行处理(performTraversals),之后开始绘制过程(在Surface的canvas上绘制)。

-

先利用MeasureSpec完成onmeasure(),然后在onlayout()中确定各元素的坐标,ondraw()负责将view画到canvas上,再通过Surface进行跨进程最终调用Native层的SGL、openGI,最后再去调用硬件CPU进行渲染操作,最终界面显示在你眼前

-

解释

-
    -
  • DecorView:界面的根View,PhoneWindow的内部类
  • -
  • contentParent:所有View的根View,在DecorView里面
  • -
  • ViewRootImpl:ViewRoot是GUI管理系统与GUI呈现系统之间的桥梁,WindowManager通过ViewRootImplDecorView起联系。并且,View的绘制流程都是由ViewRootImpl发起的
  • -
  • SGL:底层的2D图形渲染引擎
  • -
-]]>
+&lt;span class=&#34;c1&#34;&gt;//进入UI线程 +&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;handleLaunchActivity&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; +&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;performLaunchActivity&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; +&lt;span class=&#34;c1&#34;&gt;//创建Activity实例 +&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Instrumentation&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;newActivity&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; +&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Activity&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;onCreate&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; +&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; +&lt;/div&gt; +&lt;/div&gt;&lt;h2 id=&#34;解释-1&#34;&gt;解释&lt;/h2&gt; +&lt;ul&gt; +&lt;li&gt;&lt;strong&gt;ActivityThread&lt;/strong&gt;:App的真正入口。当开启App之后,会调用main()开始运行,开启消息循环队列,这就是传说中的UI线程或者叫主线程。与ActivityManagerServices配合,一起完成Activity的管理工作;&lt;/li&gt; +&lt;li&gt;&lt;strong&gt;ApplicationThread&lt;/strong&gt;:用来实现ActivityManagerService与ActivityThread之间的交互。在ActivityManagerService需要管理相关Application中的Activity的生命周期时,通过ApplicationThread的代理对象与ActivityThread通讯;&lt;/li&gt; +&lt;li&gt;&lt;strong&gt;Instrumentation&lt;/strong&gt;:可以理解为应用进程的管家,每个应用程序只有一个,每个Activity内都有该对象的引用,ActivityThread要创建或暂停某个Activity时,都需要通过Instrumentation来进行具体的操作;&lt;/li&gt; +&lt;li&gt;&lt;strong&gt;ActivityStack&lt;/strong&gt;:Activity在AMS的栈管理,用来记录已经启动的Activity的先后关系,状态信息等。通过ActivityStack决定是否需要启动新的进程;&lt;/li&gt; +&lt;li&gt;&lt;strong&gt;ActivityRecord&lt;/strong&gt;:ActivityStack的管理对象,每个Activity在AMS对应一个ActivityRecord,来记录Activity的状态以及其他的管理信息。其实就是服务器端的Activity对象的映像;&lt;/li&gt; +&lt;/ul&gt; +&lt;p&gt;&lt;strong&gt;Question 1: 如何判断APP是否已经启动?&lt;/strong&gt; +AMS会保存一个ProcessRecord信息,有两部分构成,“uid + process”,每个应用工程序都有自己的uid,而process就是AndroidManifest.xml中Application的process属性,默认为package名。每次在新建新进程前的时候会先判断这个 ProcessRecord 是否已存在,如果已经存在就不会新建进程了,这就属于应用内打开 Activity 的过程了。&lt;/p&gt; +&lt;h1 id=&#34;从activity创建成功到显示画面&#34;&gt;从Activity创建成功到显示画面&lt;/h1&gt; +&lt;p&gt;onCreate()方法中先执行setContentView()方法将对应的xml文件传入,之后会去调用window.setContentView(),最终会在这里创建&lt;strong&gt;Decorview&lt;/strong&gt;并填充标题栏、状态栏,然后获取&lt;strong&gt;contentParent&lt;/strong&gt;,然后调用LayoutInflater.inflate解析xml文件获取根root(ViewRootImpl),通过root.addView()将contentParent添加到&lt;strong&gt;ViewRootImpl&lt;/strong&gt;中去,至此onCreate()结束。&lt;/p&gt; +&lt;p&gt;开始onResume()阶段,在开始会向H类发送一个消息,然后在ActivityThread中获取之前创建的Decorview并调用windowManager.add(),最后在windowManager中将窗口和窗口的参数传到root.setView(),然后ViewRoot通过Binder调用WMS,使WMS所在的SS进程接收到按键事件时,可以回调到该root,同时ViewRoot会向自己的handler发送一条消息,然后进行处理(performTraversals),之后开始绘制过程(在Surface的canvas上绘制)。&lt;/p&gt; +&lt;p&gt;先利用MeasureSpec完成onmeasure(),然后在onlayout()中确定各元素的坐标,ondraw()负责将view画到canvas上,再通过Surface进行跨进程最终调用Native层的&lt;strong&gt;SGL&lt;/strong&gt;、openGI,最后再去调用硬件CPU进行渲染操作,最终界面显示在你眼前&lt;/p&gt; +&lt;h2 id=&#34;解释-2&#34;&gt;解释&lt;/h2&gt; +&lt;ul&gt; +&lt;li&gt;&lt;strong&gt;DecorView&lt;/strong&gt;:界面的根View,PhoneWindow的内部类&lt;/li&gt; +&lt;li&gt;&lt;strong&gt;contentParent&lt;/strong&gt;:所有View的根View,在DecorView里面&lt;/li&gt; +&lt;li&gt;&lt;strong&gt;ViewRootImpl&lt;/strong&gt;:ViewRoot是GUI管理系统与GUI呈现系统之间的桥梁,&lt;code&gt;WindowManager&lt;/code&gt;通过&lt;code&gt;ViewRootImpl&lt;/code&gt;与&lt;code&gt;DecorView&lt;/code&gt;起联系。并且,&lt;code&gt;View&lt;/code&gt;的绘制流程都是由&lt;code&gt;ViewRootImpl&lt;/code&gt;发起的&lt;/li&gt; +&lt;li&gt;&lt;strong&gt;SGL&lt;/strong&gt;:底层的2D图形渲染引擎&lt;/li&gt; +&lt;/ul&gt; +]]>