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 @@
我喜欢:
-我讨厌:
-漫展里非常显眼的 ”黑白双煞“,首先是黑裙子的小姐姐。
- - -然后是白色。(拍这个小姐姐的人多到主摄都被挤到没位置
- - -最后是冰晶礼服鬼化蕾姆。
-]]>
+
--INFJ 可能是最稀有的人格类型,但他们肯定会在世界上留下自己的印记。他们理想主义、有原则,不满足于平平安安地度过一生–他们想要站起来,有所作为。对于 INFJ 来说,成功不是来自金钱或地位,而是来自寻求成就感,帮助他人,成为世界上一股向善的力量。
-
最稀有的人格类型?哈哈查了一些资料发现,国内互联网上关于 MBTI 的内容大多都是直接 “ 英译汉 ” 从外网搬运来的,所以引用中的修饰词也要加上 “ America ” 的前缀,实际上在国内的特殊环境下成长的 95 后中 INFJ 人格类型的占比还是不少的,国内最稀少的人格类型应该是被称为「指挥官」的 ENTJ。当然这些不属于自我人格的范畴。
-回到正题本身,“ 理想主义、原则性 ” 这些标签我是欣然接受的,毕竟寻找理论落地过程中由于人类劣根性导致的偏差是我乐子之一。但 “ 不满足于平淡生活 ” 这就有点不符合了,近几年所作的事情和近期对未来的规划都是在为畅想中的 “ 平淡生活 ” 作准备,虽然我很纠结于 “ 人生意义 ” 这个话题,但这种意义不是来自于社会地位的提升、也不是外在的物质财富,而是来自于内心畅想的实现,或者说将自己所坚持的规则(原则)具现出来并应用到现实社会。所以……哈哈太难了,畅想仅仅是畅想。
---INFJ 往往会有一种与大多数人不同的感觉,由于他们丰富的内心生活和寻找生活目标的深沉而持久的渴望,他们并不总是与周围的人融为一体,幸运的是,这种不合拍的感觉并没有削弱 INFJ 让世界变得更美好的承诺。
-INFJ 受到不公正的困扰,他们通常更关心利他主义而不是个人利益,许多 INFJ 将帮助他人视为生活中的使命,他们一直在寻找介入并为正确的事情大声疾呼的方法。具有这种人格类型的人也渴望解决社会更深层次的问题,希望不公平和艰辛能够成为过去。
-
别说了,像在被讽刺一样,如果我从小在一个比较包容开放,接受多元价值观的地方成长,那可能确实会像上面说的一样现在还在坚持倡导一些事情。但事实是我目前想不到任何我所能做的可以 “ 让世界变得更美好 ” 的事情,只能顾好自己和珍视的人,虽然现在看见 “ 精致的利己主义者 ” 还会恶心作呕,但也不会有任何想要争辩或者 “ 他是错的我是对的 ” 这样的想法。其实我也不确认对方是错的,只是不喜欢而已。
---INFJ 可能内向,但他们重视与他人的深厚、真实的关系。很少有事情会像真正认识另一个人一样给这些性格类型带来快乐–同时也为别人所理解。INFJ 喜欢有意义的对话远远超过闲聊,他们倾向于以热情和敏感的方式进行交流。这种情感上的诚实和洞察力可以给周围的人留下深刻的印象。
-
这倒是真的,我虽然是个典型的 i 人,但对于认识新朋友,特别是了解新朋友、与新朋友进行深度对话,我是毫不吝啬的,在解锁一个人的人生经历、情感经历、特别是三观塑造历程的时,我内心得到的满足和快感是十分强烈的。(我始终于好奇不同成长经历对于个体三观塑造的影响)
---INFJ 喜欢在工作中保持整洁有序,喜欢平和、安静的气氛,希望工作中每个人的贡献能得到肯定,每个人都有成就感,所有的工作都能和谐地取得良好的结果。
-
这让我不得不贴一下我使用了两年的工位了,就像我 About 页所写的一样,「有序」始终是我生活的主旋律。
-最后,其实之前在纠结 MBTI 准确性的时,有跟朋友讨论过 MBTI 与星座的区别,讨论过程有个类比觉得十分恰当。
---一场考试中你物理得分 90 分,化学得分 60 分,说明你物理比较好,我会给你打上 “ 物理 ” 的标签,这就是 MBTI,MBTI 中对于人格的描述与自我察觉的实际情况间的差异可以看作 “ 考试中影响结果的运气等不稳定因素 ”。而星座的话,就是不用参加考试,直接根据你的生辰来判定你物理、化学所得的分数。
-
就这样吧,希望有个愉快的春节。
-]]>雅安自驾大本营回身随拍,318 旅途的起点。
- -驾离市区,向大山前进,海拔开始拔升,云气触手可摸。
- -路上随处可见的经幡和国旗。
- -云雾弥漫中的折多山。
- -团队的第一张合照。(我是摄影狮
- -我在折多山很想你。
- -喜马拉雅山脉阻断的印度洋暖湿气流形成的云雾。
- -新都桥民宿的藏族老板招待的牦牛肉火锅,超香!(有个伙计因为高反在休息,错过了
- -民宿门口的无名小河。
- -继续出发,行驶在山野。
- -观景台对面山上的藏文牌子。(在西藏支教过的堂哥说是 “ 宗喀巴大师 ” 的意思
- -随处可见的雪山观景台。
- -牛吃草。
- -山在脚下,张开手就可以拥抱云彩。
- -Windows 10 壁纸。
- -草原哈雷。
- -甘孜的 “ 蒙古包 ”。
- -万岁!
- -到达世界最高城,理塘!(偶遇一个很帅的爆改 3 系
- -初入稻城亚丁。(要坐 1 个小时大巴车,累的很
- -跟 Chichi 一起爬央迈勇神山。
- -珍珠海(藏语叫 “ 卓玛拉措 “,其实只是一个大一点的水塘
- -亚丁里的雪山,忘记是哪座了。(在亚丁后面相机没电了,拍的比较少
- -跟 Chichi 堆的玛尼堆。
- -海子山的姊妹湖!
- -观景台附近的移动咖啡店。
- -一只狗
- -初入措普沟(可以感觉出来巴塘官方是真心想发展这个景区,对外游客特别友好,力推!
- -措普沟圣山脚下的 Tiffany 蓝。
- -三小只。
- -色拉山垭口拍的南迦巴瓦峰的日照金山。
-]]>其实制作的方法很简单,只是在基本 text2img 的基础上使用 ControlNet 进行垫图并附加一些光影性强的 Model 即可。
-需要用到一张黑底白字的图片,用什么做随意,用你熟悉工具即可,尺寸需要跟你上面配置的 Size 信息保持一致,以保证文字的位置是准确的,比如下图。
-另外,如果你能让文字的边缘实现模糊效果就更好了,生成的图片光影文字效果会更贴合。
-最后就抽卡吧,慢慢抽,另外附上一张网友测试的 Control Step 与出图效果的对比图。
-关于自我,涉及多个层面。一个是本然的自我,一路成长过来所形成的自我,是以往你所有选择的结果。另一个是想象中的自我,是经过自我意识洗礼,变形折射而来的,不是本真的自我。还有一个是理想中的自我,即自己觉得我应该是怎样的。
-认识自己的过程就是寻找本然自我的过程,首先一定要了解自己在社会和历史舞台上所处的位置,而不是做一个漫无目的的自然人,现在很多人看不清自我,就是不知道自己所处在什么位置上,有什么样的价值,千万别在你还没了解这个世界的时候,就把自己固定了。
-在失去坐标的转型时代,青年人该如何定位自己?青年要认识自我,不能坐在房子里,坐井观天,往往需要在不完美的探索中认识自己,有痛苦,有欢乐,于痛苦中发现自己活着,于欢乐中发现自己还很平庸,在这个过程中,才逐渐知道自己热爱什么样的生活,跟什么样的世界联系在一起。这就是所谓青春的激情。
-在这个过程中 孤独是很重要的环节,年轻的时候扔了很多珍贵的东西,到后来才发现,当年所藐视的、不值一提的东西才是最宝贵的,我们经常将无意义的东西误以为宝,只有在孤独中、在自己的选择里,体会出来的东西才是最真实的。
-本然的自我一定是不完美的,但确实独一无二的,人只有受到阻力时,才能触动自己,反思自己的生命是否真实。一个人一旦通过这种野性的方式触动自己,心里便会一片透亮,感觉特别有价值感、幸福感,而人一旦体会到这种感觉,就不肯放弃了。这种愉悦一定是在路上探索才能体会的,不经一番寒彻骨,怎得梅花扑鼻香。
-希望我们这新一代的人有生而为人的生命的连续性,在他的儿童、少年、青年、中年、老年各个时期,都能活得很真实,是一个由内向外生活的人,而不是由外向内、活在别人眼光里的人。
-书中也有梁老师关于人格、修养的阐述,但我其实不太赞成梁永安老师在这本书中对于好人的定义,人格的好坏不能与认知是否清醒、人生价值是否实现相关联,单纯的、愚昧的、不思进取的人可能有一个发自内心对外在释放善意的人格,所幸的是对于道德在于人格的重要性,跟梁老师的看法还是一致的,就像康德所说,“ 有两种东西,我对它们的思考越是深沉和持久,它们在我心灵中唤起的惊奇和敬畏就会日新月异,不断增长,这就是我头上的星空和心中的道德定律。”
-我们偶然来到这个世界,活着是为了什么? 很多人是以获得为目的的,但是一个人究竟需要多少获得?很多人不珍惜自己,把自己放在虚荣、竞争等相对价值里,以为这样就可以令人羡慕,但最终又有怎样的价值呢?我们有可能正在恶的链条上发力,争取着虚名浮利,仅仅以周围的人为参照人群——我要过得比他们好,这其实是对自己价值非常大的贬低。
-真正有价值的是寻找、珍惜自我,懂得这个世界的自由,珍惜个人生命的自由,关注自己的价值和社会需要的价值,确认自己能够做些什么,最终做出一些跟别人不一样的事情,为社会增添一些亮光,而不是执着于千篇一律的东西。这时,我们才会有一种全新的生命观。
-最后,送给大家一句我很喜欢的话,这也是目前我对于生活的态度,“ 拥有可以接纳并允许一切发生的可能,从自我出发去分享表达,但不对反馈反响抱有限定的期待 ”。
-]]>--2023 年 9 月 23 日更新:Colab 官方禁用 Stable Diffusion WebUI,无法解决断网问题,本文方法作废。详情见 https://decrypt.co/197428/google-colab-stable-diffusion-web-ui-ban
-
Stable Diffusion 是 stability.ai 于 2022 年发布的深度学习文生图模型,可以在大多数配备有适度 GPU 的电脑硬件上运行。
-Colaboratory 简称 Colab,是 Google Research 团队发布的一种托管式 Jupyter 笔记本服务,可以免费使用 GPU / TPU 计算资源。
-这是一份保姆级的在 Colab 搭建 Stable Diffusion 服务的教程,按照以下步骤操作,即可浅尝 Ai 绘画的魅力。
-进入 Stable_Diffusion_WebUi_Altryne Colab 工程。
-点击右上角的连接获取云主机资源并连接运行时。
- -运行 1.0 单元格中的命令可以获主机信息。
- -一般分配到的都是 Tesla T4 机型,如果人品欧分配到 V100,那可以好好的吹一波了。
-首先注册 Hugging Face 账号,并生成 WRITE 类型的 Access Tokens。
- -返回到 Colab 界面,展开 1.4 Connect to Google Drive 单元格,勾选 download_if_missing 并输入 Hugging Face 的 Accsee Token。
- -在 2.1 Optional - Set webUI settings and configs before running 中配置密码。
- -将所有单元块收起,依次运行所有单元块部署服务即可。
-运行耗时
-运行过程中会弹窗请求登录 Google Drive,正常登录并允许即可。
- -运行完成点击 Public URL 进入 Stable Diffusion WebUI 界面。账号为 wubei,密码为上面自己配置的。
- -然后就可以尽情的享用了。
-输入 prompt,即可生成对应的图像,比如输入 “ an astronaut in the water ”。
- -当然,你可以输入更多的 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个女孩,很长的头发,淡白色头发,红色眼睛,浅绿色眼睛,白色长裙,猫耳,发夹,阳光下,淡淡的微笑,蓝色项链,透明
-
查看 Git 本地配置
-
-
|
-
-
|
清除用户名和邮箱
-
-
|
-
-
|
使用 ssh-keygen
命令生成 ssh-key,并手动指定 id
-
|
-
-
|
生成成功会输出以下内容
-
-
|
-
-
|
然后继续生成你的其他 Git 账号对应的 ssh-key
-
-
|
-
-
|
使用 ssh-add
命令将生成的 ssh-key 添加到 ssh-agent 信任列表
-
|
-
-
|
如果遇到 Could not open a connection to your authentication agent.
,输入
-
|
-
-
|
然后重复执行 ssh-add
即可解决
复制对应公钥,配置到对应 Git 网站中(GitHub / GitLab)
-
-
|
-
-
|
这步具体的操作如果有不清楚的可以参考 Adding a new SSH key to your GitHub account
-进入 ssh 目录
-
-
|
-
-
|
按照以下规则编辑 config 文件,没有则创建
-键 | -值 | -说明 | -
---|---|---|
Host | -主机 | -自己起 | -
Hostname | -主机名 | -Git 公有地址,比如 gitub.com / gittee.com | -
IdentityFile | -身份文件 | -rsa 文件路径 | -
User | -用户 | -自己起,一般邮箱就好 | -
编辑完 config 内容如下
-
-
|
-
-
|
测试 Git 账号是否连接成功,git@
之后是 config 文件中配置的 Host
-
|
-
-
|
连接成功会有以下输出
-
-
|
-
-
|
在我国,政府不但影响“蛋糕”的分配,也参与“蛋糕”的生产,所以我们不可能脱离政府谈经济。
-要理解政府治理和运作的模式,必须首先了解权力和资源在政府体系中的分布规则。这一分布取决于两个重要体制特点:一是央地关系,二是条块分割。央地关系是指整体上中央与地方之间权力要平衡;条块分割是指地方部门要同时接受上级垂直部门和横向地方政府的领导,受到双重制约。
-基于这两个特点,地方政府的权力划分可以从三个视角考察:从外部性的视角,若政府提供的公共服务只影响本地,就可以由本地全权处理,若存在外部性,则需要上级协调;从信息的视角,有信息优势的一方实际权威也会更大,因此上级往往也会花费大量的精力获取下级的信息;从激励相容的视角,与本地发展目标无关或相反的事项更倾向于垂直领导,与本地发展目标相符或本地能受益的事项则倾向于交给本地政府处理。
-根据这些事权划分原则,地方政府在招商引资、发展经济的过程中享有非常广泛的权力,因此可以深度参与资源的生产与分配过程。
-要发展经济,地方政府除了有事权还不够,更重要的是财权。1985-1993年,推行财政包干,导致中央财政收入占比逐年下降,于是1994年进行分税制改革,增加了中央的收入,却大大降低了地方的财政资源。
-分税制并没有改变地方政府以经济建设为中心的任务,却减少了其手头可支配的财政来源,地方不得不另谋出路,寻找资金来源,于是轰轰烈烈的土地财政就此登场。土地财政简单来讲就是指地方政府依靠出让土地使用权的收入来维持地方财政支出。
-1998年,发生了两件改变房地产市场的大事:第一,单位停止福利分房,老百姓从此需要自己买房子了,房地产时代自此拉开大幕;第二,修订后的《中华人民共和国土地管理法》开始实施,简言之就是地方政府拥有卖地的决定权,而卖地的收入也归地方所有。地方政府就可以限制商住用地供给,从不断攀升的地价中赚取土地垄断收益。
-一些地区政府实行土地财政。一方面补贴工业用地,招商引资,一方面限制商住用地,房价带动地价。但对于无法依靠土地财政的地区,则出现了财政困难,地区间不平等,需要依靠中央转移支付,并继续进行财政改革。如农村税费改革,乡财县管、省直管县等等。
-土地真正的力量还不在土地财政,而在以土地为抵押而撬动的银行信贷和其他各路资金,土地财政一旦嫁接了资本市场,加上了杠杆,就成了土地金融,能像滚雪球般越滚越大,推动经济飞速扩张。
-政府成立地方政府融资平台,即“城投公司”,以未来的土地收益为抵押撬动大量银行贷款,就可以进行投资以推动城市化和工业化。政府投资主要分两个方向,一是投资基础建设,二是投资工业。投资基础建设就是城投公司开发后再交给政府招商引资,或企业从开发到引资全负责,政府付费使用,这就是“政府与社会资本合作”模式(PPP)。
-但是地方政府通过土地金融推动经济发展的模式仍然存在一些问题。从微观层面,土地金融会增加地方政府债务风险。地方政府债务除了账面的显性负债,更多的是融资平台公司的“隐性负债”。经济向好时可以以土地增值收益作为还款来源,一旦经济遇冷,低价下跌,就可能出现严重的债务问题。
-目前改革的主要措施有用政府公债置换城投公司债务,将城投公司转型为普通国企,避免资金继续流入,约束官员的投资冲动。但是从根本上说,现行体制下,地方官员和普通政府工作人员都拥有非常强的激励去发展经济,这一方面导致投资过度,一方面出现了“官商勾结共同发财式”的系统性腐败。因此深层次的改革应当简政放权,从生产投资型政府向服务型政府转变。
-我国的经济改革脱胎于计划经济,政府手中掌握大量对产业发展至关重要的资源,如土地、银行、大学和科研机构等,所以必然会以各种方式深度参与工业化进程。
-上面描述的政府投资的第二个方向就是投资工业,由政府对特定企业进行支持和补贴。此外,政府还会设置产业引导基金,成立投资公司或交给市场化的基金管理人运作,作为“母基金”投资其他基金,再通过后者投资未上市公司的股权,从而将更多社会资本引导至战略新兴产业。
-书中以京东方、光伏发展为例讲解了具体的地方政府投资工业的过程,在此不做赘述。
-上面描述了土地金融模式微观层面上的影响,下面描述宏观影响。
-第一,城市化过程中“重土地、轻人”,从而推高房价,增加居民债务负担,加剧收入差距和贫富差距。
-房价升高在中长期看来是因为大城市的建设用地指标被严格管理,跟不上人口流入的速度,导致住房供不应求。房价越高,居民债务负担就越重。
-收入差距则是由于城市相关公共服务供给不足,劳动力无法自由流动,低技能人员难以在城市立足。在经济快速增长的过程中,低收入群体对于贫富差距的敏感度没有那么高,但一旦经济放缓,社会对不平等的容忍度将会降低。
-因此改革办法是将重心从土地转移到人,既要让建设用地指标流转起来,打破城市政府对城市住宅用地的垄断,又要改革户籍制度,增加低收入群体的流动性和选择权。
-第二,招商引资过程中“重规模,重扩张”,加重企业债务负担,以及经济整体的债务和金融风险。
-债务的成因在于金融危机后我国金融管制的放松,银行更愿意把钱借出去。
-从债务人企业的角度,主要是地方政府融资平台企业、国有企业、房地产企业债务负担较重。它们为了还贷抛售资产,会造成资产价格下跌,而资产价格下跌又会使银行坏账上升,不愿意继续贷款,使企业资金链断裂,从而带来经济衰退。
-从债权人银行的角度,一是规模大,杠杆率高;二是银行负债与资产期限的不匹配会带来流动性风险;三是银行信贷大多以房地产或土地为抵押,因此经济衰退时,银行反而不愿放贷,加速经济下行;四是银行风险会传导至其他金融部门,形成系统风险。银行为了逃避监管设立影子银行,资本在金融机构流转,一旦泡沫破裂,将影响整个金融行业。
-要解决债务问题,可以分为两个部分,一是偿还已有债务,二是遏制新增债务。
-偿还债务要么变卖资产,要么压缩支出,要么增发货币。后者有三种方法:直接增发、量化宽松、债务货币化。
-要遏制新增债务,除了严控房价上涨、限制土地金融等金融改革,关键是要找到债务增长的根源。企业为什么总是向银行贷款?根据“谁做投资决策,谁承担投资风险”的原则,我国的投资主要有政府和国企主导,因此风险也是由政府及其控制的金融机构,即银行承担。因为银行的风险归根结底是政府的风险。因此限制债务增长的根本措施是资本市场改革,将权力下放给市场,拓宽直接融资渠道,让企业通过股权、债券筹资。
-第三,发展战略“重投资、重生产、轻消费”,导致经济结构不平衡。
-对于内部,结构失衡最突出的特征是消费不足,这一方面是因为计划生育、民生支出不足、房价上涨等因素造成的储蓄偏高,一方面也是因为居民收入份额较低。
-在经济发展初期,大量投入资本可以有效实现工业化,推动经济增长,但当经济发展到一定阶段,这种发展模式会造成产能过剩、债务风险(投资流入房地产,推高房价)、贫富差距过大、外部失衡(出口常年大于进口)等一系列问题。
-但是,中美之间的贸易冲突,并非因为中国的出口对美国就业的冲击,而是制造业崛起对美国技术的冲击,再加上美国政治保守主义的兴起。这势必要求我国构建以国内大循环为主体的双循环模式,壮大国内市场,实现“市场—研发—迭代—更大市场”的良性循环。
-这一转型的关键在于提高居民收入与消费。这应当从三个方面入手,一是继续推进城市化,让服务业进一步发展;二是加大民生支出,降低生产性支出;三是发展直接融资渠道,扩宽居民财产性收入。
-政府过去发展经济的核心在于引入竞争机制,由中央协调,地方政府竞争。竞争的评价标准就是经济建设。
-这种“官场+市场”的体制优势在于设计了一晰有效的升迁标准,从而给官员提供了极强的激励发展经济。但也会产生地方保护主义、腐败等问题。正如计划经济时代政府干预的特点延续到市场化改革,这一体制也会留下重视经济、忽视民生的路径依赖。
-在市场机制尚不完善的情况下,政府可以作为其补充动员和调配资源,为完善和建设市场经济争取时间。但当市场机制已经相对成熟,政府也要适时改变自己的角色定位,降低政府投资支出,加大民生支出,从生产型政府转向服务型政府。
-这一转变也契合发展经济学的一般视角:对于发展中国家,提高生产率的关键在于学习已知的技术和管理模式,当生产率提升到一定水平后,又要由组织学习模式转向探索创新模式。而无论是怎样的发展模式,不同的国家、不同的地区都会根据具体的政治社会现实而有所不同。
-]]>该教程适用于普通家用路由器网络以及 iOS 系统的手机,操作可以分为以下几步:
-通过 “ 王者人生 ” 微信小程序获取商户 WiFi 名称,具体步骤如下:
-操作视频如下:
-⚠️ 值得注意的是,请选择与手机 IP 地址所在城市的商户,另外最好选择 WiFi 很少变化的商户,如电影院、KFC 等。
-在路由器管理后台修改当前 WiFi 名称为第一步获取的商户 WiFi 名称即可,常见路由器管理后台地址为:192.168.1.1。
-⚠️ 值得注意的是,如果你是合租房等与他人共享 WiFi 的情况,可以通过 “ 朋友专属网络 ” 功能避免修改 WiFi 名称对他人的网络状态造成影响。现在大多路由器的管理后台都提供了开辟朋友专属子网络的功能,修改该网路的名称即可在不影响他人使用的情况下完成该步骤。
-最后一步,使用爱思助手修改手机定位。
-完成以上步骤,即可在王者荣耀中开启商家特权了,开启方法很简单:
-记录阅读 罗兰·米勒 的《亲密关系》过程中的收获。
-人际关系是由多种影响因素构成的,其范围从当前文化的流行时尚到人类种族的基本属性,非常广泛。在这些一般的影响因素之外,还有很多个体独有的影响因素如人格和经验,它们有些是习得的,有些是遗传的。最终,两个来自同一星球,但在很多方面存在一定程度差异的人,开始了他们的互动。互动的结果或许令人沮丧,或许令人满意。
-人际关系种类多样,规格不齐,我们上有父母,还可能下有子女,我们还有朋友和爱人,《亲密关系》书中重点关注的是后两种伙伴关系,而且限制于成人之间。
-亲密关系是一个复杂的概念,包含各种成分,很难去下定义,好在研究者和普通人都认为亲密关系和泛泛之交之间存在六个方面的程度差异。
-为什么人们需要亲密关系呢?
-亲密关系对于人类的重要性主要体现在归属需要上,归属需要是人类长期演化的产物,逐渐成为所有人类共同的自然倾向,让我们觉得与和自己有关联的人的正常社会交往是必不可少的,是对持续关爱和包容的一种需要。
-文化标准是人们建立人际关系的基石,它影响着人们对人际关系的期望,限定了正常的人际关系模式。
-就拿最近普遍流行的同居现象来说,现在许多高中生认为情侣未婚同居是个“好主意”,因为他们能据此考察彼此是否真正能“和睦相处”(Bachman etal.,2001)。这种态度使未婚同居看上去很有道理,好像是个不错的选择。
-然而,如果人们并没有切实的结婚计划,未婚同居并不能确保随后的婚姻幸福美满;相反,同居增加了夫妻离婚的危险。如上图的研究结果所示,随着时间的推移,同居情侣结婚的可能性逐渐降低,但分手的可能性却不下降。
-总的说来,草率同居原本用来测试伴侣能否和睦共处,却好像会损害人们对婚姻的积极态度和维持婚姻的决心,这种态度和决心是幸福婚姻的支柱(Rhoades et al.,2009)。
-依恋类型不受基因影响,是在与生俱来的个体特征和养育水平的影响下塑造的。
-我们幼时对人际交往价值和他人是否可信的观念,起源于我们与照料者的交往,由于运气的好坏,我们就此走向了信任或恐惧的亲密关系之路。这段历程永远不会停止,同行者随后给予的阻碍或帮助会改变我们亲密关系的方向和进程。视乎人际交往经验的不同,我们习得的依恋类型既可随时间发生变化,也可永久保持稳定。
-两性群体差异确实存在,但远低于同性不同个体间的差异程度。
-两性性别内的行为和观点差异通常远大于两性之间的平均差异。男性较女性更能接受随意、短暂的性关系(Peterson & Hyde,2010),这未必表示所有男性都喜欢随意的性关系。
-有些男人喜欢与陌生人发生性行为,但也有些男人根本不喜欢这样做,这两组男性在性生活上的相似程度远不如男性和女性的平均水平。换句话说,尽管在性放任上存在两性差异,一位非常性放任的男人与女性性放任的平均水平的差别,远低于他与另一位性保守的男人在性放任上的差别。
-书中一则描述刻板化印象的笑话很有趣。
-怎样吸引女人:
-夸赞她,依偎她,亲吻她,爱抚她,热爱她,安慰她,保护她,拥抱她,不惜千金买笑,奉出美酒佳肴,烦心的时候听她唠叨,微恙的时候悉心照料,永远和她待在一起,永远支持她的意见,为她走遍天涯海角。
-怎样吸引男人:
-脱光衣服,带上啤酒。
-实际上,两性对亲密关系的期望差异很小,他们根本不是“相反的”两类人(Hyde,2007)。如果认为男女两性差异很大,当面临冲突时(这无法避免)不太可能去努力修复自己与异性的亲密关系。认为异性是来自另一世界的外星人不仅是错误的,更是有害的,它阻碍了对伴侣观点的理解,妨碍了双方协作解决问题。
-性认同差异指的是由文化和教育引起的两性在社会性和心理上的差异,或者叫社会性别(Wood & Eagly,2009)。
-性认同最好的例子是性别角色(gender roles),即社会文化所期待的男女两性应有的“正常”行为模式,下面是常见的性别角色分类,社会期望和鼓励男人具有工具性,女人具有表达性。
-大五人格特质可以很好地区分人们在诸多方面(如行为、思维和情感等)的差异(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)。
-那么为什么还要冒这种风险呢?书中的回答很是浪漫。
--]]>因为我们人类是社会化的动物,我们需要彼此。没有与他人的亲密联系,我们就会枯萎和死亡 。
-
如果存在一个圆 A 和若干个圆 a,圆 a 皆于圆 A 相交,圆 a 的圆心皆位于圆 A 上,且圆 a 间的 圆心距 相等。
-即环形布局应当满足以下两个属性:
-根据以上性质我们可以根据数学公式计算出 圆 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) -$$
-
-
|
-
-
|
为了满足子元素环形排列的需要,最大子元素的外切圆上限需为 $ 90^\circ $ 扇形的 内切圆,如下图所示。
-设扇形半径为 $ R $、扇形圆心角为 $ \alpha $、扇形内切圆半径为 $ r $。
-最大子元素半径推导过程如下。
-$$
+
$$ +$$</p> +<p>$$ r = (R - r) \times sin(\frac{\alpha}{2}) -$$
-$$ +$$</p> +<p>$$ 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})} -$$
-
-
|
-
-
|
我们选择使用 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
-
-
|
-
-
|
公司园区爆发疫情,被迫居家隔离,心念念外面的豆角焖面,在猪屁的指导下尝试了下,还算成功。
-画地为牢的居家隔离生活总是漫长的,每天都要看几集《圆桌派》消磨时光,其中印象最深刻的是第五季的第十一期,这期的嘉宾是华大基因的 CEO 尹烨,一个理科生、三个文科生围绕 “ 基因 ” 展开了热火朝天的讨论,从 “ 体外胚胎技术 ” 到 “ 人体细胞更新 ”,从 “ 忒修斯之船 ” 到 “ 道金斯的自私的基因 ”,每个话题都充斥着思维碰撞的火花,节目的最后尹烨便推荐了这本书 -《自私的基因》。
-断断续续几个月读完全书后,受益良多,所以在此简单记录下书中作者的主要观点和自己的一些思考。
-“ 纵观历史,人类对自己的认识总是不断矮化的。 ”
-从最初哥白尼提出 “ 日心说 ”,地球是宇宙中心的观念在人们心中崩塌,到达尔文提出 “ 进化论 ”,人们意识到自己是由猴子演变而来的,人类和猴子的差距并不大,再然后弗洛伊德提出 “ 性学三论 ” ,表示人类并没有所谓的自控能力,我们的行为只是受到了原始 “ 力比多 ” 的驱使,最后道金斯在《自私的基因》一书中更进一步,指出人类不过是基因的容器,不过是基因复制自己传宗接代的工具而已。
-道金斯认为是基因在操纵人,或者说是基因在推动人类复制和传播自身,就像人在开车时,汽车是没有方向感的,是人在操控汽车,是人在选择方向,人类驾驶汽车是为了满足自身高速移动的需求,对于汽车而已,本身是盲目,是无意义的。同样,人类个体的生存也是盲目的、无意义的,不过是体内基因复制和传播自己的工具,存在的意义不过是为了满足基因复制更快、传播更远、更加长寿三个目的而已。
-“ 明显的利他行为实际上是伪装起来的自私行为。 ”
-动物养育自己的后代,站在生物个体的角度,这会是一种利他行为,但道金斯认为,这种行为是建立在 基因可以通过养育后代这种利他行为来达成复制和传播自身的目的 的前提下的。所有站在生物个体角度看起来属于利他行为的情况,都是基因自私的产物。
-对基因来说唯一有意义的事情就是不断的复制自己、传播自己,以便在生物进化这场 “ 战争 ” 中获得更多的优势,获得更多活下来的可能性。
-在书中看到这些,或多或少会有些失望,对人类失望、对自己失望,人类的诞生是偶然的,也是荒谬的,生命的意义可以说是微不足道的。人类世界里那些崇高而辉煌的舍生取义、视死如归,在基因的客观世界里是多么的不合情理。
-但正如道金斯在书中写到,人们不能对事实视而不见,也不能因为事实而自暴自弃,书中的观点和结论,都只是对动物生物性的观察,是科学事实的陈述,并不代表道金斯本人的道德观。
-相反,他认为如果我们想要建立一个人与人之间慷慨大度、无私奉献的社会,那我们就不能指望我们的生物学本性,我们必须设法通过教育,把慷慨大度和利他主义灌输到人们的头脑中去,因为我们生来就是自私的。
-“ 我们具备足够的力量去抗拒我们那些与生俱来的自私基因。我们也可以抗拒那些灌输到我们脑子里的自私觅母。我们甚至可以讨论如何审慎地培植纯粹的、无私的利他主义,这种利他主义在自然界里是没有立足之地的,在世界整个历史上也是前所未有的。我们是作为基因机器而被建造的,是作为觅母机器而被培养的,但我们具备足够的力量去反对我们的缔造者。在这个世界上,只有我们,我们人类,能够反抗自私的复制基因的暴政。 ”
-就像尹烨在节目结尾说道:“ 如果人类是一组代码,那我相信人类的代码中有爱 ”。
-你我,共勉。
-]]>在 Hugo 中引入 MathJax 即可解决该问题,MathJax 是一个适用于所有浏览器的 JavaScript 数学显示引擎。
-引入方法很简单,在文章的模版的 HTML 的中添加以下内容即可。
-
-
|
-
-
|
要注意的是需要确保该 HTML 必定被 Hugo 框架加载,我这里是添加到了 layouts/partials/
目录下的 footer.html
文件中,因为我确定该文件必定包含在网站的每个页面中。
实际上单纯引入 MathJax 并不需要如此多行代码,多余的部分是为了解决 Markdown 和 LaTeX 中对于划线 _ 的不同定义带来的问题,详情可以参考 这篇文章 。
-$$
+<span class="o">&lt;</span><span class="nx">style</span><span class="o">&gt;</span>
+<span class="nx">code</span><span class="p">.</span><span class="nx">has</span><span class="o">-</span><span class="nx">jax</span> <span class="p">{</span>
+ <span class="nx">font</span><span class="o">:</span> <span class="nx">inherit</span><span class="p">;</span>
+ <span class="nx">font</span><span class="o">-</span><span class="nx">size</span><span class="o">:</span> <span class="mi">100</span><span class="o">%</span><span class="p">;</span>
+ <span class="nx">background</span><span class="o">:</span> <span class="nx">inherit</span><span class="p">;</span>
+ <span class="nx">border</span><span class="o">:</span> <span class="nx">inherit</span><span class="p">;</span>
+ <span class="nx">color</span><span class="o">:</span> <span class="err">#</span><span class="mi">515151</span><span class="p">;</span>
+<span class="p">}</span>
+<span class="o">&lt;</span><span class="err">/style&gt;</span>
+</code></pre></td></tr></table>
+</div>
+</div><p>要注意的是需要确保该 HTML 必定被 Hugo 框架加载,我这里是添加到了 <code>layouts/partials/</code> 目录下的 <code>footer.html</code>文件中,因为我确定该文件必定包含在网站的每个页面中。</p>
+<p>实际上单纯引入 MathJax 并不需要如此多行代码,多余的部分是为了解决 Markdown 和 LaTeX 中对于划线 _ 的不同定义带来的问题,详情可以参考 <a href="https://www.gohugo.org/doc/tutorials/mathjax/">这篇文章</a> 。</p>
+<p>$$
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; x&lt;3 \<br>
+\pi, &amp; x=3 \<br>
+\int_a^{3b}x_{ij}+e^2 \mathrm{d}x,&amp; x&gt;3 \<br>
\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 吧。
-]]>三者关系:
-流媒体就是以数据流的方式,实时发布音频、视频多媒体内容的媒体形式,关键技术在于流式传输
。
流媒体传输协议就是用来定义如何流式传输的,设计、制定了流媒体服务器和客户端通讯的方式。
-主流的流媒体传输协议:
---本文只详细介绍 HLS,不涉及 RTMP 与 RTSP
-
大多数流媒体传输协议都可以分为拆分、加密两部分。
-拆分是 将完整的视频流拆分为连续的视频片段,不同的传输协议的区别在于拆分片段的大小、视频容器的格式不同。
-加密是 对每段视频片段进行加密,使用对称加密算法,在服务端加密,在客户端解密,且通过一定手段限制解密密钥的获取。
---一般使用 AES 加密算法
-
对称加密效率相对较高,非对称加密效率相对较低,但是更安全。流媒体场景对实时性的要求很高,而且数据量也很大,所以选用效率相对较高的对称加密算法。
-类似的场景还有很多,比如 HTTPS 的请求过程,内容传输为了效率选用对称加密(TLS),证书校验为了安全选用非对称加密(SSL)。
-HLS 全称 HTTP Live Streaming, 是由 Apple 提出的基于 HTTP 的流媒体传输协议,用于实时音视频流的传输,目前已被广泛应用与视频点播、直播场景。
-- --
完整的 HLS 架构可以划分为 3 个部分:
-完整的 HLS 的过程可以参考下图:
- -大体可以划分为 6 个阶段:
-Media Segment
,并创建配套的媒体列表文件 Media Playlist
以及索引文件 Master Platlist
经过上面第 4 步骤的加工可以形成完整的结构,由 Master Playlist
、Media Playlist
、Media Segment
构成,关系结构如图。
完整的 HLS 结构由两部分组成:
-Media Segment 就是单纯的 ts 格式的视频文件,并无任何描述信息,可以单独使用播放器进行播放。
---M3U8 是 Unicode 版本的 M3U,8 代表使用的是 UTF-8 编码,M3U 和 M3U8 都是多媒体列表的文件格式。
-
M3U8 描述文件中由各种描述字段构成,下面解释部分主要字段的含义。
-我在网上随便找的一个 m3u8 视频的链接:https://mgtv-com.jjyl12349.com/20210519/fXE0kuJ7/index.m3u8
请求该链接的返回结果为一个 m3u8 文件,也就是 Master Playlist 文件。
-
-
|
-
-
|
字节解释:
-我们根据 BANDWIDTH、RESOLUTION 等信息选取合适的 Media Playlist 的请求链接,并将链接与视频链接的域名结合,即可得到完整的链接。
-比如,BANDWIDTH 为 1000kb、RESOLUTION 为 1280x720 的备用源的请求链接为:https://mgtv-com.jjyl12349.com/20210519/fXE0kuJ7/1000kb/hls/index.m3u8
请求该链接的返回结果也为一个 m3u8 文件,也就是 Media Playlist 文件。
-
-
|
-
-
|
字节解释:
-NONE
、AES-128
、SAMPLE-AES
最近有在准备跳槽,所以重新把 剑指Offer 的题目写了一遍,并每道题目都整理了对应的讲解文章,希望对您有所帮助。
-IPC是Interprocess communication的缩写,即进程间通讯。
-管道:在创建时分配一个page大小的内存,缓存区大小比较有限
-消息队列:信息复制两次,额外的CPU消耗;不适合频繁或信息量大的通讯
-共享内存:无需复制,共享缓冲区直接附加到进程虚拟地址空间,速度快,但进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决
-套接字:作为更通用的接口,传输效率低,主要用于不同机器或跨网络的通讯
-信号量:常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源,因此,主要作为进程间以及同一进程内不同线程之间的同步手段
-信号:不适用与信息交换,更适合与进程中断控制,比如非法内存访问、杀死某个进程等
-首先,我们要知道,Linux中进程之间是有隔离的,而且每个进程的进程空间都会分为“用户空间”和“内核空间”,对应着“用户态”和“内核态”,而“系统调用”则是用户空间访问内核空间的唯一方式,系统调用主要通过如下两个函数实现:
-接下来,我们就可以研究传统IPC通讯的原理了,如下图所示: -
-消息方将要发送的数据存放在内存缓存区,通过系统调用进入内核态,然后内核程序在内核空间分配内存,开辟一块内存缓存区,调用copy_from_user()将数据从用户空间的内存缓存区拷贝到内核空间的内核缓存区中。同样的,接收方进程在接受数据时在自己的用户空间开辟一块内存缓存区,然后内核程序调用copy_to_user()将数据从内核缓存区拷贝到接受进程的内存缓存区。这时两个进程间就完成了一次数据传输,我们称完成了一次进程间通讯
-通常,一个App只有一个进程,但Android是可以实现多进程的,比如某些通讯App会单独开辟一个常驻后台的进程。发展迅速,这种做法越来越常见
-通常,只有一种方法,即在AndroidMenifest中指定新的android:process
属性
具体如下:
-
-
|
-
-
|
还有一种非常规的方法:通过JNI在native层中去fork新进程
-在介绍AndroidIPC之前,先说一下为什么需要这些方式,原有的Android通讯机制或者LinuxIPC满足不了Android多进程吗?
-Android系统会给每一个进程分配一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,这就导致了很多问题:
-为了解决上述问题,引出了如下AndroidIPC的方式
-除文件共享外,这些IPC的方式,底层其实都是使用的Binder机制
-显而易见,就是通过两个进程读/写同一个文件来实现交换数据
-缺点:并发操作可能会导致文件数据的有效性
-场景:适用于对数据同步要求不高的进程之间通讯,并且要妥善解决并发读写的问题
-主要是以表格的形式来组织数据,包含多张表,一行对应一条记录、一列对应一条记录中的一个字段,与数据库类似。除去表格形式还支持图片、视频等文件数据
-组织封装完数据后会提供统一的存取接口,使得其他进程可以忽略底层数据存储的方式,仅通过统一接口来操作数据
-通常与他的辅助工具类一起实现通讯,可以实现进程间通讯或进程内通讯
-原理:Binder机制,外部调用CURD方法的话是运行在ContentProvider进程的Binder线程池中(不是主线程)
-优点:对数据进行安全的封装;提供统一的存取数据的接口供其他进程调用(无需考虑底层测数据存储方式是SQLite还是内存存储之类的)
-场景:一对多的进程间共享数据,比如获取/修改系统亮度
-用法:Android校招面试指南-ContentProvider全方位分析
-Bundle中数据以key-value键值对的形式存在,我们通常在Activity、线程之间使用它,其实由于Bundle实现了Parcelable接口,所以也可以在进程之间使用,只需配置一下目标包名信息即可
-参考如下:
-
-
|
-
-
|
当要使用Bundle传递对象时,必须序列化,即实现Serializable/Parcelable接口
-关于序列化:Oldjii的笔记-Android序列化
-原理:Bundle只是一个信息的载体,内部维护了一个Map<String, Object>
-场景:四大组件间的进程间通讯
-Messager可以翻译为“信使”,通过它可以在不同进程间传递Message对象,在Message中放如数据,就可以实现进程间通讯了,这是一种轻量级的IPC方案
-与Handler的关系:Messager底层使用了AIDL的方式,但和普通的AIDL不同的是,它是利用Handler进行处理的,其实这就是它不支持并发的原因;Handler是线程间通讯的一种机制,其本身是不支持进程间通讯(IPC)的
-想了解Handler机制,可以参考:Oldjii的笔记-Handler机制解析
-原理:底层实现是AIDL
-优点:安全,不支持并发(既可以说是优点也可以说是缺点);封装AIDL,使用简单,不需要AIDL文件;支持实时通讯
-缺点:串行处理客户端发来的消息,服务端不存在并发情况;数据通过Message传递所以只能传递Bundle支持的数据类型
-场景:低并发的一对多即使通讯
- -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机制是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也可以直接使用,下面是三者使用的场景
-直接使用:Android Binder的极简使用
-在看这部分时,建议你先翻到上面去回顾一下“传统IPC的原理”,这样可以更好的理解Binder的通讯原理
-Binder与传统IPC不同的地方主要在于使用了“动态内核可加载模块”和“内存映射”
-Linux的动态内核可加载模块(LKM)是一段具有独立功能的程序,它可以被单独编译,但是不能单独运行,该模块在运行时被链接到内核作为内核的一部分运行。这样Android系统就可以通过动态添加一个内核模块运行在内核空间,用户进程之间则通过这个内核模块作为桥梁来实现通讯
-这个负责各用户进程Binder通讯的内核模块就是Binder驱动(Binder Dirver)
-内存映射简单来说就是,将用户空间的一块内存区域映射到内核空间,映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间,反之亦然。这样利用内存映射就可以减少数据拷贝的次数
-BinderIPC中的内存映射是通过mmap()来实现的,mmap()是操作系统中一种内存映射的方式,mmap()通常是用在物理介质的文件系统中,但Binder并不存在物理介质,也无法实现利用mmap()在物理介质和用户空间之间建立映射关系。mmap()在Binder的作用是用来在内核空间创建数据接受的缓存空间
-了解了上面的概念,就可以理解完整BinderIPC的通讯原理了
-一次完整的BinderIPC通讯过程:
-如下图所示: -
-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 收到数据后从中取出名字和引用填入查找表
-
init():创建/dev/binder设备节点
-open():获取Binder Driver的文件描述符
-mmap():在内核分配一块内存,用于存放数据
-ioctl():将IPC数据作为参数传递给Binder Driver
-ioctl命令:
-
-
|
-
-
|
更多关于Binder源码的内容请参考:Binder系列1—Binder Driver初探
-一次完整的Binder通讯过程如下:
-可以从性能、安全两个方面来回答这个问题
-在移动设备上,广泛的使用跨进程通讯对通讯机制的性能有很严格的要求,Binder相对于传统的Socket方式,更加高效。因为Binder数据拷贝只需一次,而管道、消息队列、Socket等需要2此,虽然共享内容方式一次内存拷贝都不要,但是实现方式过于复杂,不适合该场景。
-传统的进程通讯方式对于通讯双方的身份并没有作出严格的验证,比如Socket通讯的IP地址是客户端手动填入,很容易进行伪造。然而,Binder机制从协议本身就支持对通讯双方做身份校验,从而大大的提高了安全性
-]]>本文不对Rxjava的基本使用进行讲解,仅对源码做分析,如果你对Rxjava的基本使用还有不清楚的,建议学习官方文档之后再阅读本文
-ReactiveX文档中文翻译
-Rxjava
-给Android开发者的RxJava详解
本文会逐一解析Rxjava的create()、subscribe()、操作符、subscribeOn()、obsweveOn()的源码,模式是先给出一段模版代码,然后逐渐深入分析
-这里给出一个最简单的Rxjava的实例
-
-
|
-
-
|
直接看create()方法主体
-
-
|
-
-
|
调用对象和返回对象都为Observable,而传入参数为ObservableOnSubscribe
-
-
|
-
-
|
这是一个接口,仅包含一个方法,就是上面我们在new ObservableOnSubscribe时候需要重写的那个方法。 -再看subscribe()的形参类型ObservableEmitter
-
-
|
-
-
|
发现这也是一个接口,不需要太过关注,值得关注的是他的上层,由接口特性我们知道Emitter肯定也是一个接口,我们来看下它定义了什么方法
-
-
|
-
-
|
看到这三个熟悉的方法,你就知道为什么我们实例化的ObservableEmitter对象e可以调用onNext()、onError()、onComplete()这三个方法了
-create()的参数已经看完了,下面看下create()的内容
-第一句ObjectHelper.requireNonNull(source, "source is null");
是判空代码。
-返回值是RxJavaPlugins.onAssembly(new ObservableCreate<T>(source))
,我看看下这个方法。
-
|
-
-
|
具体的内容我们不需要去解读,我们只看他的返回值和传入参数,经过观察发现都是Observable类型,乍看好像没什么问题,但是看上面,源码中传入的是一个ObservableCreate类型,所以这里ObservableCreate有适配器的作用,将ObservableOnSubscribe适配为Observable类型。 -下面我们就看看这个适配器ObservableCreate
-
-
|
-
-
|
成员变量、构造方法略过,我们先看看这里频频出现的观察者observer
-
-
|
-
-
|
同样的也是一个接口,定义的这个四个方法就是我们在订阅时,观察者需要重写的四个方法,注意与上面的Emitter接口及其三个方法进行区分。
-看这行observer.onSubscribe(parent);
,由上面我们知道observer.onSubscribe()是接受Disposable类型,而这里的parent是CreateEmitter类型,你可能已经猜出来了,没错,这里的CreateEmitter也是一个适配器,前面的ObservableCreate对被观察者进行了适配,CreateEmitter则对观察者进行了适配,将observer类型转化为Disposable类型,下面看下他的源码
-
|
-
-
|
主要就是重写了那四个方法,定义了规则,比如:
---并且,到这里你对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);
,最重要的是这一行,调用者是被观察者,传入的参数为观察者,基本可以猜出来了,这里是订阅的作用,真正将被观察者与观察者联系起来的地方
-
|
-
-
|
第一句的作用同样是判空,接下来先获取了传入的observer并进行了相关配置,然后调用subscribeActual(observer);
,细心的同学可能注意到了,subscribeActual()正是在上面ObservableCreate中被重写的方法,而具有“订阅”意义的那行代码也包含其中,结合subscribe()的本意,这行代码的作用也很明朗了
如果你只是想对Rxjava基本的数据传输流程、订阅的原理感兴趣,那么就不用看下去了,下面的内容主要是Rxjava操作符、线程调度、背包的源码分析
-开始分析Rxjava的操作符部分
-我们以Map操作符为例展开分析,首先,还是给出一个最简单的实例
-
-
|
-
-
|
先看map()方法整体
-
-
|
-
-
|
返回值肯定是Observable,参数是一个泛型接口,我们看下这个接口
-
-
|
-
-
|
传入T,返回R,符合Map操作符传入两个数据类型进行转换的效果,在意料之中。 -继续看map()的方法内容,第一行按照惯例是判空语句,我们发现map()的return语句与create()极为相似,都是调用了RxJavaPlugins.onAssembly(),仅是传入的参数不同,其实不只是Map操作符,大多操作符都是这样的,他们的不同仅仅是传入参数的不同,也就是适配器的不同,这说明,操作符的具体实现(比如Map的类型转换)都是在各自的适配器中做的。
---小结:create以及对大多数操作符的retun语句都是RxJavaPlugins.onAssembly(),仅是传入参数不同
-
进入ObservableMap的部分
-
-
|
-
-
|
我们发现,ObservableMap做的事情很少,就三件事,第一:在构造方法中,将传入的Observable也就是本身抛给父类(ObservableSource是Observable的父类,所以可以接受);第二:对转换逻辑funtion进行保存;第三:重写subscribeActual()方法并在其中实现订阅,这里与ObservableCreate是一样的,只是传递的参数不同
---小结:create以及对大多数操作符的第一层适配器中都会重写subscribeActual()并实现订阅逻辑
-
我们并没有在ObservableMap的代码中发现进行类型转换的代码,不要心急,有的同学估计已经发现了,这里的进行订阅操作的source.subscribe()
传入的参数类型改变了 ,之前是CreateEmitter,现在变为了一个叫MapObserver的类,我们知道CreateEmitter中实现了那四个常用的方法并制定了相关规则,所以你推测MapObserver中做了同样的操作,其实不是的,但也差不了太多,除onNext()之外的三个方法是在它的父类BasicFuseableObserver中重写的,MapObserver中只对onNext()进行的重写,而且在其中进行了数据类型转换的工作,我们看一下源码(这里我们只看onNext()部分就可以了)
-
|
-
-
|
可以看到再代码中利用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()
-
-
|
-
-
|
返回值Observable情理之中,return返回RxJavaPlugins.onAssembly()也是一样,两点不同:
-进入ObservableSubscribeOn
-
-
|
-
-
|
根据经验,构造中进行调用父类、存值一些操作,没什么可看的,直接看订阅的实现subscribeActual()方法,可以看见,这次对下游观察者进行封装的适配器是SubscribeOnObserver类,根据CreateEmitter、MapObserver的经验,我们可以猜测出它或它的父类肯定实现了那四个方法,下面我们看一下
-
-
|
-
-
|
除去构造、四个方法、基本的存储语句就剩下一个setDisposable()方法了,如果你对Scheduler有研究,你就知道在Scheduler中真正处理线程调用逻辑的是Worker类,这里setDisposable()的作用就是将你传入的Scheduler返回的worker加入管理。
-目光回到subscribeActual()中,调用观察者的onSubscribe()之后,马上调用了parent.setDisposable(),这里停一下,你可以翻上去观察一下其他方法的subscribeActual()部分,都是在这时候执行订阅操作,但是我们在这里并没有发现,订阅操作不可能没有发生,那么是不是发生在了parent.setDisposable()这个方法里面呢?我们之前只关注了这个方法的内容,对于传入的参数还没有解析,我们现在看一下,希望有新的发现。
-传入的参数是scheduler.scheduleDirect(new SubscribeTask(parent))
。
-先看SubscribeTask这个类
-
|
-
-
|
这个类继承Runnable,所以实现了一个子线程,在run()中执行操作,没错,source.subscribe(parent);
,熟悉的语句,这就证明了这里的订阅操作发生在了Scheduler的线程中。
我们继续看scheduleDirect()这个方法
-
-
|
-
-
|
继续
-
-
|
-
-
|
我们可以发现,传入的子线程被包装配置之后,开始在Worker也就是Scheduler线程中执行 -我们继续看DisposeTask这个类,具体的订阅子线程的启动就在这里
-
-
|
-
-
|
可以看到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()
-
-
|
-
-
|
没看见RxJavaPlugins.onAssembly(),担心不一样?不存在的,被包了一层而已
-
-
|
-
-
|
还是那个顺序,ObservableObserveOn
-
-
|
-
-
|
看subscribeActual(),很好理解,就是先判断是不是在主线程,是的话,直接订阅完事,不是的话跳到主线程去,在订阅,切换线程依旧是使用的Worker那一套,与subscribeOn()中类似,先创建一个主线程的Worker,然后把Worker放进观察者的包装类ObserveOnObserver中,不用多说,里面肯定有对那四个方法的实现,我这里简化一下他的代码
-
-
|
-
-
|
其他那三个方法与onNext()大致相同,只看这一个就可以了,schedule();
这行代码上面都是取数据的操作,并没有对数据进行发送,所以说即使使用线程调用将被观察者的操作放在主线程,他的数据准备阶段仍然是在原线程执行的,当schedule();
执行后,进入上面传入Workder线程,也就是主线程,然后才将queue中的T取出,继而发送给下游的观察者。其他方法也是一样的流程,比如onError()、onComplete(),都是将错误或完成的信息先保存,等待切换线程后在执行发送操作。
--由此,我们可知ObserverOn()是向下作用的,每次调用都对下游的代码产生作用,所以多次调用ObserverOn(),是最后一次生效的
-
好了,Rxjava的源码分析到这里结束了,文中有很多没有讲到的地方,日后有时间的会继续讲解剩余部分。
-我们来整理一下文中出现的各个装饰者
-
-
|
-
-
|
本文中,我们对create()、subscribe()、map()、subscribeOn()、observeOn()的源码进行的阅读,想你已经可以从源码的角度回答以下问题:
-本文把这段流程分为三部分:
-先看流程图: -
-开机加电后,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列表。
-Question 1: Zygote进程为什么使用Socket而不是Binder? -fork不允许存在多线程,而Binder通讯恰巧就是多线程;
-Question 2:什么是窗口? -Android系统中的窗体是屏幕上的一块用于绘制各种UI元素并能够响应应用户输入的一个矩形区域,从原理上来讲,窗体的概念是独自占有一个Surface实例的显示区域,比如Dialog、Activity的界面、壁纸、状态栏以及Toast等都是窗体;
-先看流程图: -
-
-
|
-
-
|
Question 1: 如何判断APP是否已经启动? -AMS会保存一个ProcessRecord信息,有两部分构成,“uid + process”,每个应用工程序都有自己的uid,而process就是AndroidManifest.xml中Application的process属性,默认为package名。每次在新建新进程前的时候会先判断这个 ProcessRecord 是否已存在,如果已经存在就不会新建进程了,这就属于应用内打开 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进行渲染操作,最终界面显示在你眼前
-WindowManager
通过ViewRootImpl
与DecorView
起联系。并且,View
的绘制流程都是由ViewRootImpl
发起的