IT技术精华 http://it.taocms.org/ 聚合国内IT技术精华文章,分享IT技术精华,帮助IT从业人士成长 2020-09-21 IT技术精华 41197 关于高尿酸的一些研究

题图:尿酸的分子结构。

序言

我因为自己有 5 年的高尿酸(指标大于 550) 的情况,加上最近在服药控制,所以查了一些资料。

中国的高尿酸比例其实挺高的,相关资料显示总体患病率为 13.3%,保守估计有 1.8 亿高尿酸患者,痛风患者达 1700 万。

如果你也和我有相似的身体情况,希望本文对你也有一些帮助。

高尿酸是什么

定义

高尿酸症状的医学说法叫:高尿酸血症(hyperuricemia,简称 HUA)。

高尿酸是指血液中尿酸的浓度超过了健康人的指标。血尿酸正常值:

  • 成人男性为 149~416μmol/L ;女性为 89~357μmol/L
  • 年龄大于 60 岁的男性为:250~476μmol/L; 女性为:190~434μmol/L

危害

尿酸在血液中的浓度如果过高,就不能完全溶解于血液中,于是就会出现析出结晶。这些结晶在关节处累积,就会诱发痛风。

通常高尿酸不会那么快导致痛风,也有很多人高尿酸但是一直没有痛风,但是两者的相关性极大。我自己高尿酸 5 年了还没有痛风,但我一个朋友也就 3 年左右就开始痛风了。

《高尿酸血症和痛风治疗的中国专家共识》建议:如果低嘌呤饮食的情况下,高尿酸还在 540 以上,或高于420(男)/360(女)持续半年以上,即需要进行降尿酸治疗(下图)。

我自己在这件事情上还是拖了很多年,其实是不对的。

来源和排泄

尿酸(uric acid)是嘌呤的代谢产物。整体上人体的尿酸来自两类:

  • 内源性。依赖于自身代谢产生,这部分占到体内尿酸总量的 80%。
  • 外源性。从饮食中直接摄入的,这部分占到体内尿酸总量的 20%。

尿酸产生之后,主要通过肾脏排泄(占比 70%),其余通过消化道排泄。

原因

刚刚说到来源和排泄,所以产生高尿酸的原因主要是两种:

  • 来源来得太多了。主要包括自身代谢紊乱(内源性)或者饮食中摄入(外源性)太多。
  • 排泄得太少。主要是肾脏排泄能力下降导致。

当你体检时发现尿酸高的时候,医生或者朋友第一个建议你的事情就是:在饮食上进行控制,进行低嘌呤饮食。嘌呤主要来源于海鲜、内脏以及肉汤。

但是就像我们刚刚提到的,这部分只占到体内尿酸生成总量的 20%。所以如果是自身代谢紊乱或者肾脏排泄能力下降的话,即便是低嘌呤饮食也无法降低尿酸。

医生可以通过测量 24 小时尿尿酸来判断是否排泄减少:

  • 小于 600mg(3.6mmol) 定义为尿酸排泄减少型
  • 超过 800mg(4.8mmol) 定为尿酸产生过多型

研究表明,有 90% 的原发性高尿酸都是排泄减少型。对于排泄减少型,即使你坚持低嘌呤饮食,影响也相当有限。

监控

精确地测量尿酸值,需要到医院抽晨血(抽血前不能进食)。我自己研究发现,可以购买尿酸测试仪在家自行测量,用于定期的监控。我买的是三诺的 EA-12 测试仪(下图),别的品牌也没试过。

我自己在去医院测试的同一天用测试仪也测试,发现这款家用测试仪误差大概在 50 以内,凑合能够接受吧。

治疗

低嘌呤饮食

饮食对于尿酸的影响占比是20%,当患者是代谢或者排泄问题导致高尿酸时,低嘌呤饮食虽然很多时候并不能解决问题,但是对控制尿酸还是有一些帮助的。

我个人总结的经验是,除了严禁的食物外,其它不用刻意地控制,因为如果真要严格控制,很多东西都吃不了了。严禁的食物是:

  • 动物内脏,比如鹅肝。
  • 海鲜。
  • 浓肉汤和肉汁。
  • 酒。

高尿酸鼓励吃的食物是:

  • 脱脂或低脂奶制品
  • 鸡蛋
  • 蔬菜
  • 低生糖指数谷物
  • 饮水大于 2000 ML

可能有一些人还是忍不住想喝酒,现在研究只能证明啤酒和烈酒会引发痛风,而红酒与痛风的相关性证据还不足,所以你如果实在忍不住,就喝红酒吧。

别嘌醇(Allopurinol)

别嘌醇是一个老药了,1966 年在美国被FDA批准上市,到现在已经经过了半个世纪的使用。根据相关统计,别嘌醇在美国一年被开的处方次数为 1400 万次,在 2017 年排名第 54 位。

别嘌醇的用量:

  • 初使剂量:50mg,一天1-2次。
  • 一般剂量:200-300mg 一天。
  • 最高剂量:600mg 一天。

别嘌醇对部分人会有超敏反应,严重情况致死。所以服用初期要密切关注身体状况。后来人们研究发现,超敏反应与白细胞 HLA-B5801 基因相关,所以在国内,有条件的医院就会在患者服用别嘌醇前,给他开HLA-B5801 基因的检测(这个检测也是可以走医保的)。

下图是我的检测结果:

刚刚我们提到这个药在美国使用得非常普遍,但是在中国使用得很少,这里面有一个重要原因,是白细胞 HLA-B5801 基因在不同种族人群间的差异:

  • 中国汉族的基因频率为 6%-8%
  • 白人的基因频率为 2%

所以即使在美国,如果是给亚裔人群开别嘌醇,都建议先检测 HLA-B5801 基因。

而中国不是每个医院都有这个基因检测设备,加上有后面两种提到的替代药品,所以如果你不是对那两种药过敏,否则医生默认都不会给你开这个药。

这个药虽然超敏反应严重,但是也有一个好处,就是特别便宜。一般剂量的情况下,每天的费用只需要花 0.5 - 1 元钱。因为降尿酸的药需要天天吃,所以全年算下来就不算贵。而后面我即将介绍到的非布司他就比较贵,每天的费用大概是别嘌醇价格的 10 倍。

非布司他(Febuxostat)

非布司他相比别嘌醇来说是一个新药,它 2008 年在欧洲上市,2009 年在美国上市,2013年国内上市。整个中国人的药物使用数据还不到 10 年(相比来说别嘌醇有半个世纪的使用历史)。

非布司他和别嘌醇都同属于抑制身体内部尿酸的合成,以此来达到降尿酸的目的。通常剂量是 40 - 80 mg 每天。

刚刚也提到非布司他比较贵,每天的费用大概是 10 元钱。

非布司他刚开始的时候没有发现什么副作用,后来美国 FDA 怀疑它会导致一些心血管疾病加重,但是相关的研究没有证实或证伪这件事情,所以 FDA 在 2017 年前公布了一个警告,建议有心血管疾病的患者慎重选择非布司他。

这个警告我在「用药助手App」上也查到了,如下图:

我刚开始也因为这个警告有点担心,但是咨询了多位协和医院的医生,基本上大部分都还是推荐非布司他。所以这个药是我现在正服用的药物。

苯溴马隆(Benzbromarone)

相对于上面两种药,苯溴马隆的历史就有点波折了。

苯溴马隆在 1970 年上市,但是在 2003 年,因为发现了严重的不良反应,所以发明它的公司 Sanofi-Synthélabo 将它从多个国家退市。

但是在很长一段时间,因为也没有找到它的替代药品,所以包括中国在内,别的一些药厂一直在向市场继续提供苯溴马隆。我查了一下,它的主要不良反应是肝功能异常,在欧美国家的不良反应率是 1/17000,国内还无此项数据公布。

苯溴马隆的降尿酸原理和前两者都不同,前两种药是尿酸抑制药物,而苯溴马隆是通过促进尿酸排泄来达成降尿酸。因为它大量的尿酸通过尿液排出,所以为了避免尿液中的尿酸太高形成结石,服药期间需要进行大量的饮水,每天饮水量不少于 2000ML。

我当时为了测量饮水量还专门买了一个 1升 的巨型水杯,每天喝完两杯就算达标了。

因为尿酸大量进入到了尿液中,所以尿液的 PH 值会偏酸性,如果 PH 值过低,会影响尿酸的排泄,所以通常我们还需要服用碳酸氢钠片(其实就是小苏打)来碱化尿液。

但是碳酸氢钠片服用过多对胃还是有一点影响,我当时就感受胃比较胀气,毕竟碳酸氢钠和酸中和的时候会产生气体。和面的时候放小苏打能让面发胀也是这个原理。

我去年服用过一段时间苯溴马隆,后来主要是3个原因想换药:

  1. 查到这个药在国外退市。
  2. 每天必须喝够 2 升水让人感觉很麻烦和紧张,有时候出差路途不方便喝不了那么多水,就会感觉不方便。
  3. 我对碳酸氢钠有点胀气反应,胃不舒服。

苯溴马隆的价格介于别嘌醇和非布司他之间,每天服药的费用约为 2.5 元。

信息对比汇总

别嘌醇(Allopurinol)非布司他(Febuxostat)苯溴马隆(Benzbromarone)
上市时间1966年在美国上市2008年欧洲上市、
2009年美国上市、
2013年国内上市
1970年上市。
2003年从多个国家退市。
使用风险超敏反应,严重情况致死。
超敏反应与白细胞HLA-B5801基因相关。
怀疑有心血管风险,但是没有被证实或证伪。肝功能异常
风险数据统计HLA-B5801基因:白人2%,亚裔8%几十个怀疑案例欧美国家的肝功能异常率为 1/17000,国内无数据。
Wikihttps://en.wikipedia.org/wiki/Allopurinolhttps://en.wikipedia.org/wiki/Febuxostathttps://en.wikipedia.org/wiki/Benzbromarone
初使用量一次50mg 1-2次/d20-40mg 1次/d50mg 1次/d
每次递增50-100mg20mg50mg
一般剂量200-300mg 分2-3次服用40mg50mg
最高用量600mg/d80mg100mg
处方量统计1400w 一年90w 一年退市
每天费用100mg 约 0.5-1 元40mg 约 10 元50mg 约 2.5 元

小结

高尿酸血症是一个内分泌代谢紊乱的病,如果不治疗有可能引发痛风。

国内最常用的治疗药物是别嘌醇,非布司他和苯溴马隆。

我自己研究下来,推荐大家有经济条件的话选择非布司他。

参考资料


立即购买:淘宝网]]>
IT技术精华 2020-09-19 22:09:22
40995 Flutter包大小治理上的探索与实践 一、背景

Flutter作为一种全新的响应式、跨平台、高性能的移动开发框架,在性能、稳定性和多端体验一致上都有着较好的表现,自开源以来,已经受到越来越多开发者的喜爱。随着Flutter框架的不断发展和完善,业内越来越多的团队开始尝试并落地Flutter技术。不过在实践过程中我们发现,Flutter的接入会给现有的应用带来比较明显的包体积增加。不论是在Android还是在iOS平台上,仅仅是接入一个Flutter Demo页面,包体积至少要增加5M,这对于那些包大小敏感的应用来说其实是很难接受的。

对于包大小问题,Flutter官方也在持续跟进优化:

除了Flutter SDK内部或Dart实现的优化,我们是否还有进一步优化的空间呢?答案是肯定的。为了帮助业务方更好的接入和落地Flutter技术,MTFlutter团队对Flutter的包大小问题进行了调研和实践,设计并实现了一套基于动态下发的包大小优化方案,瘦身效果也非常可观。这里分享给大家,希望对大家能有所帮助或者启发。

二、Flutter包大小问题分析

在Flutter官方的优化文档中,提到了减少应用尺寸的方法:在V1.16.2及以上使用—split-debug-info选项(可以分离出debug info);移除无用资源,减少从库中带入的资源,控制适配的屏幕尺寸,压缩图片文件。这些措施比较直接并容易理解,但为了探索进一步瘦身空间并让大家更好的理解技术方案,我们先从了解Flutter的产物构成开始,然后再一步步分析有哪些可行的方案。

2.1 Flutter产物介绍

我们首先以官方的Demo为例,介绍一下Flutter的产物构成及各部分占比。不同Flutter版本以及打包模式下,产物有所不同,本文均以Flutter 1.9 Release模式下的产物为准。

2.1.1 iOS侧Flutter产物

图1 Flutter iOS 产物组成示意图

iOS侧的Flutter产物主要由四部分组成(info.plist 比较小,对包体积的影响可忽略,这里不作为重点介绍),表格1中列出了各部分的详细信息。

表1 Flutter产物组成

2.1.2 Android侧Flutter产物

图2 Flutter Android 产物组成示意图

Android侧的Flutter产物总共5.16MB,由四部分组成,表格2中列出了各部分的详细信息。

表2 Flutter Android产物组成

2.1.3 各部分产物的变化趋势

无论是Android还是iOS,Flutter的产物大体可以分为三部分:

  1. Flutter引擎,该部分大小固定不变,但初始占比较高。
  2. Flutter业务与框架,该部分大小随着Flutter业务代码的增多而逐渐增加。它是这样的一个曲线:初始增长速度极快,随着代码增多,增长速度逐渐减缓,最终趋近线性增长。原因是Flutter有一个Tree Shaking机制,从Main方法开始,逐级引用,最终没有被引用的代码,比如类和函数都会被裁剪掉。一开始引入Flutter之后随便写一个业务,就会大量用到Flutter/Dart SDK代码,这样初期Flutter包体积极速增加,但是过了一个临界点,用户包体积的增加就基本取决于Flutter业务代码增量,不会增长得太快。
  3. Flutter资源,该部分初始占比较小,后期增长主要取决于用到的本地图片资源的多少,增长趋势与资源多少成正比。

下图3展示了Flutter各资源变化的趋势:

图3 Flutter各资源大小变化的趋势图

2.2 不同优化思路分析

上面我们对Flutter产物进行了分析,接下来看一下官方提供的优化思路如何应用于Flutter产物,以及对应的困难与收益如何。

1.删减法

Flutter引擎中包括了Dart、skia、boringssl、icu、libpng等多个模块,其中Dart和skia是必须的,其他模块如果用不到倒是可以考虑裁掉,能够带来几百k的瘦身收益。业务方可以根据业务诉求自定义裁剪。

Flutter业务产物,因为Flutter的Tree Shaking机制,该部分产物从代码的角度已经是精简过的,要想继续精简只能从业务的角度去分析。

Flutter资源中占比较多的一般是图片,对于图片可以根据业务场景,适当降低图片分辨率,或者考虑替换为网络图片。

2.压缩法

因为无论是Android还是iOS,安装包本身已经是压缩包了,对Flutter产物再次压缩的收益很低,所以该方法并不适用。

3.动态下发

对于静态资源,理论上是Android和iOS都可以做到动态下发。而对于代码逻辑部分的编译产物,在Android平台支持可执行产物的动态加载,iOS平台则不允许执行动态下发的机器指令。

经过上面的分析可以发现,除了删减、压缩,对所有业务适用、可行且收益明显的进一步优化空间重点在于动态下发了。能够动态下发的部分越多,包大小的收益越大。因此我们决定从动态下发入手来设计一套Flutter包大小优化方案。

三、基于动态下发的Flutter包大小优化方案

我们在Android和iOS上实现的包大小优化方案有所不同,区别在于Android侧可以做到so和Flutter资源的全部动态下发,而iOS侧由于系统限制无法动态下发可执行产物,所以需要对产物的组成和其加载逻辑进行分析,将其中非必须和动态链接库一起加载的部分进行动态下发、运行时加载。

当将产物动态下发后,还需要对引擎的初始化流程做修改,这样才能保证产物的正常加载。由于两端技术栈的不同,在很多具体实现上都采用了不同的方式,下面就分别来介绍下两端的方案。

3.1 iOS侧方案

在iOS平台上,由于系统的限制无法实现在运行时加载并运行可执行文件,而在上文产物介绍中可以看到,占比较高的App及Flutter这两个均是可执行文件,理论上是不能进行动态下发的,实际上对于Flutter可执行文件我们能做的确实不多,但对于App这个可执行文件,其内部组成的四个模块并不是在链接时都必须存在的,可以考虑部分移出,进而来实现包体积的缩减。

因此,在该部分我们首先介绍Flutter产物的生成和加载的流程,通过对流程细节的分析来挖掘出产物可以被拆分出动态下发的部分,然后基于实现原理来设计实现工程化的方案。

3.1.1 实现原理简析

为了实现App的拆分,我们需要了解下App.framework是怎样生成以及各部分资源时如何加载的。如下图4所示,Dart代码会使用gen_snapshot工具来编译成.S文件,然后通过xcrun工具来进行汇编和链接最终生成App.framework。其中gen_snapshot是Dart编译器,采用了Tree Shaking等技术,用于生成汇编形式的机器代码。

图4 App.framework生成流程示意图

产物加载流程:

图5 Flutter产物加载流程图

如上图5所示,Flutter engine在初始化时会从根据 FlutterDartProject 的settings中配置资源路径来加载可执行文件(App)、flutter_assets等资源,具体settings的相关配置如下:

// settings
{
...
// snapshot 文件地址或内存地址
std::string vm_snapshot_data_path;
MappingCallback vm_snapshot_data;
std::string vm_snapshot_instr_path;
MappingCallback vm_snapshot_instr;

std::string isolate_snapshot_data_path;
MappingCallback isolate_snapshot_data;
std::string isolate_snapshot_instr_path;
MappingCallback isolate_snapshot_instr;

// library 模式下的lib文件路径
std::string application_library_path;
// icudlt.dat 文件路径
std::string icu_data_path;
// flutter_assets 资源文件夹路径
std::string assets_path;
//
...
}

​以加载vm_snapshot_data为例,它的加载逻辑如下:

std::unique_ptr<DartSnapshotBuffer> ResolveVMData(const Settings& settings) {
// 从 settings.vm_snapshot_data 中取
if (settings.vm_snapshot_data) {
...
}

// 从 settings.vm_snapshot_data_path 中取
if (settings.vm_snapshot_data_path.size() > 0) {
...
}
// 从 settings.application_library_path 中取
if (settings.application_library_path.size() > 0) {
...
}

auto loaded_process = fml::NativeLibrary::CreateForCurrentProcess();
// 根据 kVMDataSymbol 从native library中加载
return DartSnapshotBuffer::CreateWithSymbolInLibrary(
loaded_process, DartSnapshot::kVMDataSymbol);
}

对于iOS来说,它默认会根据kVMDataSymbol来从App中加载对应资源,而其实settings是给提供了通过path的方式来加载资源和snapshot入口,那么对于 flutter_assets、icudtl.dat这些静态资源,我们完全可以将其移出托管到服务端,然后动态下发。

而由于iOS系统的限制,整个App可执行文件则不可以动态下发,但在第二部分的介绍中我们了解到,其实App是由kDartVmSnapshotData、kDartVmSnapshotInstructions、kDartIsolateSnapshotData、kDartIsolateSnapshotInstructions等四个部分组成的,其中kDartIsolateSnapshotInstructions、kDartVmSnapshotInstructions为指令段,不可通过动态下发的方式来加载,而kDartIsolateSnapshotData、kDartVmSnapshotData为数据段,它们在加载时不存在限制。

到这里,其实我们就可以得到iOS侧Flutter包大小的优化方案:将flutter_assets、icudtl.dat等静态资源及kDartVmSnapshotData、kDartIsolateSnapshotData两部分在编译时拆分出去,通过动态下发的方式来实现包大小的缩减。但此方案有个问题,kDartVmSnapshotData、kDartIsolateSnapshotData是在编译时就写入到App中了,如何实现自动化地把此部分拆分出去是一个待解决的问题。为了解决此问题,我们需要先了解kDartVmSnapshotData、kDartIsolateSnapshotData的写入时机。接下来,我们通过下图6来简单地介绍一下该过程:

图6 Flutter Data段写入时序图

代码通过gen_snapshot工具来进行编译,它的入口在gen_snapshot.cc文件,通过初始化、预编译等过程,最终调用Dart_CreateAppAOTSnapshotAsAssembly方法来写入snapshot。因此,我们可以通过修改此流程,在写入snapshot时只将instructions写入,而将data重定向输入到文件,即可实现 kDartVmSnapshotData、kDartIsolateSnapshotData与App的分离。此部分流程示意图如下图7所示:

图7 Flutter产物拆分流程示意图

3.1.2 工程化方案

在完成了App数据段与代码段分离的工作后,我们就可以将数据段及资源文件通过动态下发、运行时加载的方式来实现包体积的缩减。由此思路衍生的iOS侧整体方案的架构如下图8所示;其中定制编译产物阶段主要负责定制Flutter engine及Flutter SDK,以便完成产物的“瘦身”工作;发布集成阶段则为产物的发布和工程集成提供了一套标准化、自动化的解决方案;而运行阶段的使命是保证“瘦身”的资源在engine启动的时候能被安全稳定地加载。

图8 架构设计

注:图例中MTFlutterRoute为Flutter路由容器,MWS指的是美团云。

3.1.2.1 定制编译产物阶段

虽然我们不能把App.framework及Flutter.framework通过动态下发的方式完全拆分出去,但可以剥离出部分非安装时必须的产物资源,通过动态下发的方式来达到Flutter包体积缩减的目的,因此在该阶段主要工作包括三部分。

1.新增编译command

在将Flutter包瘦身工程化时,我们必须保证现有的流程的编译规则不会被影响,需要考虑以下两点:

  • 增加编译“瘦身”的Flutter产物构建模式, 该模式应能编译出AOT模式下的瘦身产物。
  • 不对常规的编译模式(debug、profile、release)引入影响。

对于iOS平台来说,AOT模式Flutter产物编译的关键工作流程图如下图9所示。runCommand会将编译所需参数及环境变量封装传递给编译后端(gen_snapshot负责此部分工作),进而完成产物的编译工作:

图9 AOT模式Flutter产物编译的关键工作流程图

为了实现“瘦身”的工作流,工具链在图9的流程中新增了buildwithoutdata的编译command,该命令针对通过传递相应参数(without-data=true)给到编译后端(gen_snapshot),为后续编译出剥离data段提供支撑:

if [[ $# == 0 ]]; then
# Backwards-compatibility: if no args are provided, build.
BuildApp
else
case $1 in
"build")
BuildApp ;;
"buildWithoutData")
BuildAppWithoutData ;;
"thin")
ThinAppFrameworks ;;
"embed")
EmbedFlutterFrameworks ;;
esac
fi

..addFlag('without-data',
negatable: false,
defaultsTo: false,
hide: true,
)

2.编译后端定制

该部分主要对gen_snapshot工具进行定制,当gen_snapshot工具在接收到Dart层传来的“瘦身”命令时,会解析参数并执行我们定制的方法Dart_CreateAppAOTSnapshotAsAssembly,该部分主要做了两件事:

  1. 定制产物编译过程,生成剥离data段的编译产物。
  2. 重定向data段到文件中,以便后续进行使用。

具体到处理的细节,首先我们需要在gen_sanpshot的入口处理传参,并指定重定向data文件的地址:

  CreateAndWritePrecompiledSnapshot() {
...
if (snapshot_kind == kAppAOTAssembly) { // 常规release模式下产物的编译流程
...
} else if (snapshot_kind == kAppAOTAssemblyDropData) {
...
result = Dart_CreateAppAOTSnapshotAsAssembly(StreamingWriteCallback,
file,
&vm_snapshot_data_buffer,
&vm_snapshot_data_size,
&isolate_snapshot_data_buffer,
&isolate_snapshot_data_size,
true); // 定制产物编译过程,生成剥离data段的编译产物snapshot_assembly.S
...
} else if (...) {
...
}
...
}

在接受到编译“瘦身”模式的命令后,将会调用定制的FullSnapshotWriter类来实现Snapshot_assembly.S的生成,该类会将原有编译过程中vm_snapshot_data、isolate_snapshot_data的写入过程改写成缓存到buff中,以便后续写入到独立的文件中:

// drop_data=true, 表示后瘦身模式的编译过程
// vm_snapshot_data_buffer、isolate_snapshot_data_buffer用于保存 vm_snapshot_data、isolate_snapshot_data以便后续写入文件
Dart_CreateAppAOTSnapshotAsAssembly(Dart_StreamingWriteCallback callback,
void* callback_data,
bool drop_data,
uint8_t** vm_snapshot_data_buffer,
uint8_t** isolate_snapshot_data_buffer) {
...
FullSnapshotWriter writer(Snapshot::kFullAOT, &vm_snapshot_data_buffer,
&isolate_snapshot_data_buffer, ApiReallocate,
&image_writer, &image_writer);

if (drop_data) {
writer.WriteFullSnapshotWithoutData(); // 分离出数据段
} else {
writer.WriteFullSnapshot();
}
...
}

当data段被缓存到buffer中后,便可以使用gen_snapshot提供的文件写入的方法 WriteFile来实现数据段以文件形式从编译产物中分离:

static void WriteFile(const char* filename, const uint8_t* buffer, const intptr_t size);
// 写data到指定文件中
{
...
WriteFile(vm_snapshot_data_filename, vm_snapshot_data_buffer, vm_snapshot_data_size); // 写入vm_snapshot_data
WriteFile(isolate_snapshot_data_filename, isolate_snapshot_data_buffer, isolate_snapshot_data_size); // 写入isolate_snapshot_data
...
}

3.engine定制

编译参数修改

iOS侧使用-0z参数可以获得包体积缩减的收益(大约为700KB左右的收益),但会有相应的性能损耗,因此该部分作为一个可选项提供给业务方,工具链提供相应版本的Flutter engine的定制。

资源加载方式定制

对于engine的定制,主要围绕如何“手动”引入拆分出的资源来展开,好在engine提供了settings接口让我们可以实现自定义引入文件的path,因此我们需要做的就是对Flutter engine初始化的过程进行相应改造:

/**
* custom icudtl.dat path
*/
@property(nonatomic, copy) NSString* icuDataPath;

/**
* custom flutter_assets path
*/
@property(nonatomic, copy) NSString* assetPath;

/**
* custom isolate_snapshot_data path
*/
@property(nonatomic, copy) NSString* isolateSnapshotDataPath;

/**
*custom vm_snapshot_data path
*/
@property(nonatomic, copy) NSString* vmSnapshotDataPath;

在运行时“手动”配置上述路径,并结合上述参数初始化FlutterDartProject,从而达到engine启动时从配置路径加载相应资源的目的。

engine编译自动化

在完成engine的定制和改造后,还需要手动编译一下engine源码,生成各平台、架构、模式下的产物,并将其集成到Flutter SDK中,为了让引擎定制的流程标准化、自动化,MTFlutter工具链提供了一套engine自动化编译发布的工具。如流程图10所示,在完成engine代码的自定义修改之后,工具链会根据engine的patch code编译出各平台、架构及不同模式下的engine产物,然后自动上传到美团云上,在开发和打包时只需要通简单的命令,即可安装和使用定制后的Flutter engine:

图10 Flutter engine自动化编译发布流程

3.1.2.2 发布集成阶段

当完成Dart代码编译产物的定制后,我们下一步要做的就是改造MTFlutter工具链现有的产物发布流程,支持打出“瘦身”模式的产物,并将瘦身模式下的产物进行合理的组织、封装、托管以方便产物的集成。从工具链的视角来看,该部分的流程示如下图11所示:

图11 Flutter产物发布集成流程示意图

自动化发布与版本管理

MTFlutter工具链将“瘦身”集成到产物发布的流水线中,新增一种thin模式下的产物,在iOS侧该产物包括release模式下瘦身后的App.framework、Flutter.framework以及拆分出的数据、资源等文件。当开发者提交了代码并使用Talos(美团内部前端持续交付平台)触发Flutter打包时,CI工具会自动打出瘦身的产物包及需要运行时下载的资源包、生成产物相关信息的校验文件并自动上传到美团云上。对于产物资源的版本管理,我们则复用了美团云提供资源管理的能力。在美团云上,产物资源以文件目录的形式来实现各版本资源的相互隔离,同时对“瘦身”资源单独开一个bucket进行单独管理,在集成产物时,集成插件只需根据当前产物module的名称及版本号便可获取对应的产物。

自动化集成

针对瘦身模式MTFlutter工具链对集成插件也进行了相应的改造,如下图12所示。我们对Flutter集成插件进行了修改,在原有的产物集成模式的基础上新增一种thin模式,该模式在表现形式与原有的debug、release、profile类似,区别在于:为了方便开发人员调试,该模式会依据当前工程的buildconfigration来做相应的处理,即在debug模式下集成原有的debug产物,而在release模式下才集成“瘦身”产物包。

图12 Flutter iOS端集成插件修改

3.1.2.3 运行阶段

运行阶段所处理的核心问题包括资源下载、缓存、解压、加载及异常监控等。一个典型的瘦身模式下的engine启动的过程如图13所示。

该过程包括:

  • 资源下载:读取工程配置文件,得到当前Flutter module的版本,并查询和下载远程资源。
  • 资源解压和校验:对下载资源进行完整性校验,校验完成则进行解压和本地缓存。
  • 启动engine:在engine启动时加载下载的资源。
  • 监控和异常处理:对整个流程可能出现的异常情况进行处理,相关数据情况进行监控上报。

图13 iOS侧瘦身模式下engine启动流程图

为了方便业务方的使用、减少其接入成本,MTFlutter将该部分工作集成至MTFlutterRoute中,业务方仅需引入MTFlutterRoute即可将“瘦身”功能接入到项目中。

3.2 Android侧方案

3.2.1 整体架构

在Android侧,我们做到了除Java代码外的所有Flutter产物都动态下发。完整的优化方案概括来说就是:动态下发+自定义引擎初始化+自定义资源加载。方案整体分为打包阶段和运行阶段,打包阶段会将Flutter产物移除并生成瘦身的APK,运行阶段则完成产物下载、自定义引擎初始化及资源加载。其中产物的上传和下载由DynLoader完成,这是由美团平台迭代工程组提供的一套so与assets的动态下发框架,它包括编译时和运行时两部分的操作:

  1. 工程配置:配置需要上传的so和assets文件。
  2. App打包时,会将配置1中的文件压缩上传到动态发布系统,并从APK中移除。
  3. App每次启动时,向动态发布系统发起请求,请求需要下载的压缩包,然后下载到本地并解压,如果本地已经存在了,则不进行下载。

我们在DynLoader的基础上,通过对Flutter引擎初始化及资源加载流程进行定制,设计了整体的Flutter包大小优化方案:

图14 Android侧Flutter包大小优化方案整体架构

打包阶段:我们在原有的APK打包流程中,加入一些自定义的gradle plugin来对Flutter产物进行处理。在预处理流程,我们将一些无用的资源文件移除,然后将flutter_assets中的文件打包为bundle.zip。然后通过DynLoader提供的上传插件将libflutter.so、libapp.so和flutter_assets/bundle.zip从APK中移除,并上传到动态发布系统托管。其中对于多架构的so,我们通过在build.gradle中增加abiFilters进行过滤,只保留单架构的so。最终打包出来的APK即为瘦身后的APK。

不经处理的话,瘦身后的APK一进到Flutter页面肯定会报错,因为此时so和flutter_assets可能都还没下载下来,即使已经下载下来,其位置也发生了改变,再使用原来的加载方式肯定会找不到。所以我们在运行阶段需要做一些特殊处理:

1.Flutter路由拦截

首先要使用Flutter路由拦截器,在进到Flutter页面之前,要确保so和flutter_assets都已经下载完成,如果没有下载完,则显示loading弹窗,然后调用DynLoader的方法去异步下载。当下载完成后,再执行原来的跳转逻辑。

2.自定义引擎初始化

第一次进到Flutter页面,需要先初始化Flutter引擎,其中主要是将libflutter.so和libapp.so的路径改为动态下发的路径。另外还需要将flutter_assets/bundle.zip进行解压。

3.自定义资源加载

当引擎初始化完成后,开始执行Dart代码的逻辑。此时肯定会遇到资源加载,比如字体或者图片。原有的资源加载器是通过method channel调用AssetManager的方法,从APK中的assets中进行加载,我们需要改成从动态下发的路径中加载。

下面我们详细介绍下某些部分的具体实现。

3.2.2 自定义引擎初始化

原有的Flutter引擎初始化由FlutterMain类的两个方法完成,分别为startInitialization和ensureInitializationComplete,一般在Application初始化时调用startInitialization(懒加载模式会延迟到启动Flutter页面时再调用),然后在Flutter页面启动时调用ensureInitializationComplete确保初始化的完成。

图15 Android侧Flutter引擎初始化流程图

在startInitialization方法中,会加载libflutter.so,在ensureInitializationComplete中会构建shellArgs参数,然后将shellArgs传给FlutterJNI.nativeInit方法,由jni侧完成引擎的初始化。其中shellArgs中有个参数AOT_SHARED_LIBRARY_NAME可以用来指定libapp.so的路径。

自定义引擎初始化,主要要修改两个地方,一个是System.loadLibrary(“flutter”),一个是shellArgs中libapp.so的路径。有两种办法可以做到:

  1. 直接修改FlutterMain的源码,这种方式简单直接,但是需要修改引擎并重新打包,业务方也需要使用定制的引擎才可以。
  2. 继承FlutterMain类,重写startInitialization和ensureInitializationComplete的逻辑,让业务方使用我们的自定义类来初始化引擎。当自定义类完成引擎的初始化后,通过反射的方式修改sSettings和sInitialized,从而使得原有的初始化逻辑不再执行。

本文使用第二种方式,需要在FlutterActivity的onCreate方法中首先调用自定义的引擎初始化方法,然后再调用super的onCreate方法。

3.2.3 自定义资源加载

Flutter中的资源加载由一组类完成,根据数据源的不同分为了网络资源加载和本地资源加载,其类图如下:

图16 Flutter 资源加载相关类图

AssetBundle为资源加载的抽象类,网络资源由NetworkAssetBundle加载,打包到Apk中的资源由PlatformAssetBundle加载。

PlatformAssetBundle通过channel调用,最终由AssetManager去完成资源的加载并返回给Dart层。

我们无法修改PlatformAssetBundle原有的资源加载逻辑,但是我们可以自定义一个资源加载器对其进行替换:在widget树的顶层通过DefaultAssetBundle注入。

自定义的资源加载器DynamicPlatformAssetBundle,通过channel调用,最终从动态下发的flutter_assets中加载资源。

3.2.4 字体动态加载

字体属于一种特殊的资源,其有两种加载方式:

  1. 静态加载:在pubspec.yaml文件中声明的字体及为静态加载,当引擎初始化的时候,会自动从AssetManager中加载静态注册的字体资源。
  2. 动态加载:Flutter提供了FontLoader类来完成字体的动态加载。

当资源动态下发后,assets中已经没有字体文件了,所以静态加载会失败,我们需要改为动态加载。

3.2.5 运行时代码组织结构

整个方案的运行时部分涉及多个功能模块,包括产物下载、引擎初始化、资源加载和字体加载,既有Native侧的逻辑,也有Dart侧的逻辑。如何将这些模块合理的加以整合呢?平台团队的同学给了很好的答案,并将其实现为一个Flutter Plugin:flutter_dynamic(美团内部库)。其整体分为Dart侧和Android侧两部分,Dart侧提供字体和资源加载方法,方法内部通过method channel调到Android侧,在Android侧基于DynLoader提供的接口实现产物下载和资源加载的逻辑。

图17 FlutterDynamic结构图

四、方案的接入与使用

为了让大家了解上述方案使用层面的设计,我们在此把美团内部的使用方式介绍给大家,其中会涉及到一些内部工具细节我们暂不展开,重点解释设计和使用体验部分。由于Android和iOS的实现方案有所区别,故在接入方式相应的也会有些差异,下面针对不同平台分开来介绍:

4.1 iOS

在上文方案的设计中,我们介绍到包瘦身功能已经集成进入美团内部MTFlutter工具链中,因此当业务方在使用了MTFlutter后只需简单的几步配置便可实现包瘦身功能的接入。iOS的接入使用上总体分为三步:

1.引入Flutter集成插件(cocoapods-flutter-plugin 美团内部Cocoapods插件,进一步封装Flutter模块引入,使之更加清晰便捷):

gem 'cocoapods-flutter-plugin', '~> 1.2.0'

2.接入MTFlutterRoute混合业务容器(美团内部pod库,封装了Flutter初始化及全局路由等能力),实现基于“瘦身”产物的初始化:

Flutter 业务工程中引入 mt_flutter_route:

dependencies:
mt_flutter_route: ^2.4.0

3.在iOS Native工程中引入MTFlutterRoute pod:

binary_pod 'MTFlutterRoute', '2.4.1.8'

经过上面的配置后,正常Flutter业务发版时就会自动产生“瘦身”后的产物,此时只需在工程中配置瘦身模式即可完成接入:

flutter 'your_flutter_project', 'x.x.x', :thin => true

4.2 Android

4.2.1 Flutter侧修改

  1. 在Flutter工程pubspec.yaml中添加flutter_dynamic(美团内部Flutter Plugin,负责Dart侧的字体、资源加载)依赖。
  2. 在main.dart中添加字体动态加载逻辑,并替换默认资源加载器。
void main() async {
// 动态加载字体
await dynFontInit();
// 自定义资源加载器
runApp(DefaultAssetBundle(
bundle: dynRootBundle,
child: MyApp(),
));
}

4.2.2 Native侧修改

1.打包脚本修改

在App模块的build.gradle中通过apply特定plugin完成产物的删减、压缩以及上传。

2.在Application的onCreate方法中初始化FlutterDynamic。

3.添加Flutter页面跳转拦截。

在跳转到Flutter页面之前,需要使用FlutterDynamic提供的接口来确保产物已经下载完成,在下载成功的回调中来执行真正的跳转逻辑。

class FlutterRouteUtil {
public static void startFlutterActivity(final Context context, Intent intent) {
FlutterDynamic.getInstance().ensureLoaded(context, new LoadCallback() {
@Override
public void onSuccess() {
// 在下载成功的回调中执行跳转逻辑
context.startActivity(intent);
}
});
}
}

备注:如果App有使用类似WMRoute之类的路由组件的话,可以自定义一个UriHandler来统一处理所有的Flutter页面跳转,同样在ensureLoaded方法回调中执行真正的跳转逻辑。

4.添加引擎初始化逻辑

我们需要重写FlutterActivity的onCreate方法,在super.onCreate之前先执行自定义的引擎初始化逻辑。

public class MainFlutterActivity extends FlutterActivity {
@Override
protected void onCreate(Bundle savedInstanceState)
// 确保自定义引擎初始化完成
FlutterDynamic.getInstance().ensureFlutterInit(this);
super.onCreate(savedInstanceState);
}
}

五、总结展望

目前,动态下发的方案已在美团内部App上线使用,Android包瘦身效果到达95%,iOS包瘦身效果达到30%+。动态下发的方案虽然能显著减少Flutter的包体积,但其收益是通过运行时下载的方式置换回来的。当Flutter业务的不断迭代增长时,Flutter产物包也会随之不断变大,最终导致需下载的产物变大,也会对下载成功率带来压力。后续,我们还会探索Flutter的分包逻辑,通过将不同的业务模块拆分来降低单个产物包的大小,来进一步保障包瘦身功能的可用性。

六、作者简介

  • 艳东,2018年加入美团,到家平台前端工程师。
  • 宗文,2019年加入美团,到家平台前端高级工程师。
  • 会超,2014年加入美团,到家平台前端技术专家。

招聘信息

美团外卖长期招聘Android、iOS、FE 高级/资深工程师和技术专家。欢迎感兴趣的同学投递简历至:tech@meituan.com(邮件标题请注明:美团外卖技术团队)。


立即购买:淘宝网]]>
IT技术精华 2020-09-18 17:09:05
40944 科技爱好者周刊(第 125 期):数字人民币要取代谁 这里记录每周值得分享的科技内容,周五发布。

本杂志开源(GitHub: ruanyf/weekly),欢迎提交 issue,投稿或推荐科技内容。

周刊讨论区的帖子《谁在招人?》,提供大量就业信息,欢迎访问或发布工作/实习岗位。

封面图

西班牙加纳利岛的人们,在火山灰上挖坑种葡萄。火山灰排水快,种出来的葡萄更适合酿酒。(via

本周话题:数字人民币要取代谁

数字人民币,已经传了很久了,但它到底是什么东西,媒体却不提。

本周,人民银行的副行长写了一篇文章,第一段就回答了这个问题。

"数字人民币主要定位于流通中现金。......国家有必要利用新技术对现金进行数字化,为数字经济发展提供通用性的基础货币。"

现在清楚了,数字人民币根本不是加密货币,而是人民币现金的数字形式,主要用于网上支付。

但是,网上支付早不是问题了,支付宝和微信支付用得好好的,为什么央行还要推行数字人民币呢?上面那篇文章,同样解释了数字人民币的运作模式。

"中央银行在数字人民币体系中居于中心地位,负责向指定商业银行批发数字人民币并进行全生命周期管理,商业银行等机构负责面向社会公众提供数字人民币兑换流通服务。"

看到了吗,中央银行主管数字人民币,只有指定的商业银行才能对公众提供服务。也就是说,以后网上支付的时候,你不会只用微信支付和支付宝,还可以用商业银行的客户端。这就是说, 数字人民币就是商业银行的网上支付服务。

前一段时间,建行的数字人民币客户端泄漏。顶部的四个主要功能按钮是付款、收款、扫一扫和转款,正好验证了央行的说法。

我想,这就是中央银行想要推广数字人民币的主要原因。现在的局面是,两家互联网巨头,掌握了全国的数字支付,它们还都是私人公司。国有商业银行和银联的云闪付,根本拿不到市场份额。中央银行应该是比较担心这个,不愿数字支付被腾讯和阿里垄断,希望商业银行能挤进去。

这对消费者是有利的。两大巨头现在各占山头,阿里系不支持微信支付,腾讯系不支持支付宝。数字人民币出来以后,所有平台都必须支持(它是法定货币)。我猜想,将来应该只有一个统一的二维码,不管是用微信、支付宝还是商业银行的 App 扫描,都能支付。不像现在,商家都要贴两个二维码,蓝的一个、绿的一个。

但是,数字支付业务其实是无利可图的。它不像信用卡支付,提供方不仅赚不到手续费,还要往里面贴钱。维护硬件和开发软件都需要巨大的投入,而且对可靠性的要求特别高,服务不能中断,更不能出错。微信和支付宝其实都把扫码支付当作流量入口,而不是利润来源。

我很好奇,数字人民币的服务器,将来由谁维护?是不是中央银行以后也会有一个专门的 DevOps 部门?但是,这种赚不到钱、技术难度大、工程师薪水又极高的业务,国有单位能搞好吗?

大数据分析实战课

如今,全球早已步入数据时代,数据行业高速发展,相关岗位缺口已超150万,薪资超过传统岗位。据估计,未来十年,数据相关岗位将扩张5倍,人才缺口明显。

很多人希望进入数据分析行业,却经常遇到以下问题:

  • Excel、SQL、Python......一大堆数据分析工具,不知道该学哪个。
  • 只学了理论却没有实战经验,得不到企业的青睐。
  • 一想到面试就紧张,应聘时不知道怎么应对提问。

本周的课程资料是开课吧提供的 《大数据分析实战课》 ,4大实战项目,带你实际操作真实业务场景下的数据分析,以案例为导向,快速积累数据分析的实战经验。

  • 项目一:利用 Python 实现办公自动化。
  • 项目二:海量数据图形可视化。
  • 项目三:电商数据分析及可视化展示。
  • 项目四:玩转高精文本分析。

本课程由资深大数据专家潘海超老师亲授,让你在短时间内 Get 数据分析实战技能,迅速掌握 BAT、TMB 等互联网大厂主流工具,快速积累数据分析的实战经验。

《大数据分析实战课》现在限时优惠,原价599元, 现仅需 0 元。 名额有限,先到先得,微信扫描下面二维码立即报名。

资讯

1、电子风飞机

飞机一般使用螺旋桨或涡轮作为动力,最近麻省理工学院研发出利用电极产生离子风作为动力的飞机。早在上个世纪60年代,人们就已经发现电流会在薄电极和厚电极之间产生离子风(下图)。这是首次将这种现象用于飞机,让离子风成为推动飞机的气流。

它的好处是,由于动力来自电流,不需要机械动力,所以整个飞机可以没有会动的部件,从而降低噪音(飞机发动机的噪音非常大),缩减保养成本,以及减少污染。缺点是动力有限,目前只能让不到 10 公斤的小飞机起飞,还无法载人。

2、内装试纸的电子妊娠测试笔

一位推特用户最近购买了电子妊娠测试笔,瑞士精密诊断有限公司的产品。他将电子笔拆开以后,震惊地发现,里面居然装了试纸。真正起测试作用的是这张试纸,而不是电子设备。

电路的核心是几个光传感器,用来识别试纸显示的条纹,转成电信号,将测试结果显示在液晶屏幕上。这样做虽然使得测试结果更容易解读,但是复杂性增加了好多倍,价格整整贵了25倍。

3、纸张键盘

普渡大学的工程师发明了一种新技术,可以将任何纸张(旧报纸或打印纸)变成计算机键盘。具体做法是,先在纸张上涂一层特殊油墨,然后在油墨上设置电路。电路之间通过摩擦会产生电,而每次按钮都会造成摩擦,因此就为"键盘"提供了电。

所以,纸质键盘不需要电源,完全是自供电,再通过蓝牙连接到计算机或手机,从而变成输入设备。根据发明团队的设想,这种技术可以用在商品包装上,用来检验是否真品,或者查看食品保质期。

4、城市森林花园

成都有一个8栋楼居民小区,每栋楼30层,每一家住户都有40平米~100平米室外庭院,可以种花种菜,看上去像城市森林一样。

庭院的填土层有60厘米厚,在靠墙的地方还可以做一个向上50厘米的树池,这样在靠墙的地方便有1米多深的覆土,可以栽种4到5米高的树,并将树干固定在墙上,以防大风将树刮倒或使其摇晃,在其它不靠墙的地方才栽种1~2米的低矮植物、果树或灌木。物业还为每家安装了自动喷琳系统。

5、世界最大数码相机

智利维拉鲁宾天文台安装了一台世界最大的数码相机,高达32亿像素,用来拍摄整个天空的数十亿颗恒星和星系的位置,而且还会捕捉任何移动或闪烁的东西。

这台相机的光传感器由189个单独的 CCD 组成,正在加利福尼亚的实验室进行测试,已经组成完成。

为了测试它的性能,工程师们用它拍摄西兰花。如果一切顺利,它将在2022年下半年投入工作。

6、一句话消息

  • 网友将网线的一根铜绞线剪断,两端插入苹果。信号照样传输,网速非常好。

  • 美国空军开始试验,机器狗与士兵一起巡逻。机器狗的正式产品名叫做"自动无人地面车辆"。

  • 香港下月将拍卖一颗102.39克拉的钻石,这是有史以来第八颗被拍卖的超过100克拉的钻石。

  • 埃及政府9月6日宣布,出土了13具2500年前的木棺,自从下葬后就没有打开过。

  • 英国石油公司发表研究报告,认为世界石油消费量疫情前已经到达顶峰,此后可能会不断下降。

文章

1、核电池可以用于太空旅行(英文)

本文介绍了核电池的原理(利用放射性元素衰变产生的能量),以及用于太空旅行的可能性。

2、使用蒙特卡洛算法玩2048(英文)

2048 是一个4x4的方块游戏,只要两个相同数字相邻,就可以合并为一个数字。本文讲解了如何用蒙特卡洛算法进行简单模拟,让计算机自己解决2048游戏。

3、微前端"容器"---- microcosmos 实现(中文)

本文讨论了微前端的概念和实现,可以用来了解"微前端"这种新的做法。(@chuifengji 投稿)

4、如何将 Android 平板电脑用作第二台显示器(英文)

作者介绍如何将一块旧的安卓平板,当作电脑的副屏,使用 VNC 协议显示远程桌面。

5、for await ... of 语句解释(英文)

ES6 引入了 for await ... of 新语法,用来遍历异步迭代器,很多人还不熟悉。本文是对它的简单解释。

6、使用 React 钩子而不是类的6个理由(英文)

本文总结 React 钩子比类更有优势的6个方面,比如不再有 this 问题,也不用再绑定方法了。

7、AVIF 格式初探(英文)

AVIF 是一种开源图片格式,源自 AV1 视频格式。各个平台基本上都会支持,这篇文章对比了它与 JPEG 和 WebP 格式,结果很不错。

8、Node.js 如何下载流传输文件(英文)

本文介绍如何使用 Got 这个库,以流传输的形式(stream)下载文件。

9、从 Apple Photos 找出(机器认定的)最美照片(英文)

苹果的 Apple Photos 会对用户拍摄的每一张照片,运行机器学习模型来识别照片的内容,然后得出一个叫做"ZBEHAVIORALSCORE"的"总体美学评分",保存在本地的 SQLite 数据库里面。

作者对这个数据库使用 SQL 查询,找出计算机认为的某个主题(比如"鸟类")的最佳照片。

工具

1、Diffchecker

一个在线工具,可以检查文本、图片、PDF 之间的差异。

2、youtube-dlc

一个命令行工具,youtube-dl 的分叉,用来下载 Youtube 和其他视频网站的视频。分叉的主要原因是,youtube-dl 目前有点陷入停滞,很多 issue 没有回应,因此有人就另起炉灶,这个版本的开发现在非常活跃。

3、fastmac

有时候,你需要 Mac 电脑的环境,但是手边又没有 mac 电脑。这个工具利用 GitHub Actions 提供的资源,让你可以免费使用 Mac 的命令行环境,不过只能使用一小会。

4、Screely

一个在线工具,可以生成网页展示效果的示意图。

5、Poolside FM

一个在线的复古风格音乐电台。(@freezsun 投稿)

6、Mimestream

前苹果邮件客户端工程师 Neil Jhaveri 推出的 Gmail macOS 原生客户端,外观简洁大方,十分契合原生风格,参考介绍文章。(@dushi792 投稿)

7、real-live

一个开源的直播聚合收看平台。采用 Qt 作为前端框架,后端用到 Requests、MySQL、Redis技术等,目前支持在该平台收看 36 个视频直播、59 个高清电视频道和 74 个广播电台。(@parzulpan 投稿)

8、kibana-eye

ELK 平台(Elasticsearch,Logstash,Kibana)的定时任务管理工具,可以聚合处理日志,生成报表信息,通过邮件/钉钉等方式发送通知。(@CasterWx 投稿)

9、LofiMusic

一个开源的背景音乐播放网站。

10、BeeWare

一个 Python 工具,让你的 Python 代码可以打包成跨平台图形界面应用,尤其是可以打包成手机 App,支持 iOS、Android、Windows、MacOS、Linux、Web 等平台。

资源

1、Aggregated Awesome

GitHub 上面有很多 awesome 的仓库,收集某个主题的所有有用的内容。这个网页整理了一个 awesome 仓库的清单。

2、Fuel Collection

谷歌对大量日常物品进行了 3D 扫描,将数据放在网上,提供公开下载。

3、Prime Curios!

这个网站收集各种与质数相关的数字,比如 561 的平方加2和减2会得到两个连续的质数。

4、顶级域名列表

互联网号码分配局(IANA)官方的目前所有顶级域名(TLD)的列表,共有1508个。如果排除国家和地区的 TLD,则为1260个。

5、如何写一个简单的 C 语言解释器

一个英语教程,通过写一个简单的 C 语言解释器,理解编译原理。

6、WebRTC 示例仓库

WebRTC 是浏览器点对点通信的 API,这个仓库给出了各种使用示例,覆盖了主要的使用场景。

图片

1、黑河-腾冲线

中国地图上,从黑河到腾冲画一条线,这条线以东住着全国94%的人口。这是1935年由地理学家胡焕庸首次提出。

2、太阳系最高的火山

火星上面的奥林匹斯山(Olympus Mons)高达21公里,是珠穆朗玛峰的两倍半。它是目前已知太阳系最大的火山,也是最高的山峰之一。

文摘

1、塑料垃圾星球

随着石化工业的发展,塑料大规模进入人类生活,迄今不过只有60年的历史。但是,60年来,人类已经创造了83亿吨的塑料,其中大部分是一次性产品,最终都变成了垃圾。

83亿吨塑料之中,9%被回收,12%被焚化,剩下的79%就都成了塑料垃圾。这些垃圾有些堆积在垃圾填埋场中,还有一些就散落大自然中,其中相当一部分进入了海洋。

更可怕的是,塑料的增长速度大约是每15年翻一番,超过了其他所有人造材料。而且,塑料的使用周期特别短。钢材有一半用于建筑业,使用寿命长达数十年,而塑料生产出来,不到一年就有一半变成了垃圾,其中很大部分只用于包装。

塑料需要400多年的时间才能降解。据估计,每年有800万吨塑料流入海洋。到2050年,海洋中的塑料废物总重量,将多于鱼类的重量。与此同时,陆地上的垃圾填埋场那时必须填埋120亿吨塑料。人类将生活在一个遍布塑料垃圾的星球,其实现在已经是这样了。

2、固氮玉米

空气的最大成分(78%)是氮气,但是很奇怪,植物却普遍缺少氮元素。农作物必须施加氮肥,才能提高产量、改善品质。因为大多数植物无法直接通过氮气吸收氮元素。

只有少数植物不需要氮肥,主要是豆科类植物,自身就具有将空气中的氮气合成氮肥的能力。植物学家一直梦想将这种"固氮能力"移植到其他植物。全球种植面积最大的农作物是玉米,它就成为移植"固氮能力"的首选目标。

1980年代,一个美国农业科学家在墨西哥南部,发现了一些奇怪的玉米。这种玉米高达5、6米,比普通玉米高得多,但是需要6~8个月才成熟,比普通玉米的3个月长得多。最惊人的是,它不需要施加氮肥,就可以在贫瘠土地上长得很好。也就是说,这是全世界独一无二的具有"固氮能力"的玉米!

进一步研究,科学家发现这种玉米有气根,一部分根系暴露在空气中,这也是全世界唯一有气根的玉米。这些绿色和玫瑰色的气根,上面有透明的糖浆状粘液。科学家怀疑,这就是它们毋需施肥的秘密----这些粘液能固氮。换言之,玉米气根中有固氮菌;玉米利用固氮菌为自己施肥。

当年没有 DNA 工具来研究玉米如何固氮的细节,这一发现被搁置了20年。直到2005年,才用尖端技术,确认粘液中的细菌正在从空气中吸收氮,将其转化为玉米可以吸收的形式。

如今,经过十多年的基因分析,科学家发表了最新的研究成果,这种固氮特性可以移植到常规玉米中,这很可能会导致农业革命,取代一部分氮肥。

氮气合成为氮肥,可能是20世纪的最大成就之一。合成氮技术一共获得过三个诺贝尔奖。这是当之无愧的,据估计,从1908年至2008年间,农作物的单产增加了一倍以上,其中的一大原因就是人类会制造氮肥了。没有氮肥,为了生产同样数量的粮食,我们需要的耕地数量会比现在多四倍,世界人口也就不可能在20世纪大幅增长。

但是,氮肥生产需要消耗世界1%至2%的能源,并排放大量温室气体。合成氮通常会从田野冲入水道,导致大量藻类大量繁殖,从而吸收所有氧气,杀死鱼类和其他生物。如此多的氮进入河流和溪流,以至于世界河流的河口形成了大面积的死区。

言论

1、

2002年,我在中关村一间不算豪华的酒店里专访 NVIDIA CEO 黄仁勋的时候,他小声对我说,英特尔有 CPU,我们有 GPU。

他说得那么不自信。我也只是微笑着容忍他将 NVIDIA 和 Intel 相提并论。18年过去,NVIDIA 市值超过 Intel。

-- 刘韧

2、

2005年,我的毕业论文的《参考书目》部分包含了152个 URL。为了纪念毕业十五周年,我重新检查了这些 URL,其中82个不再起作用。我因此假定,互联网链接的腐烂率是57%。

--《测量链接腐烂率》

3、

李光耀在新加坡的成功,不仅是因为他的专制主义,而且还因为新加坡决策精英阶层的纯粹实用主义。他们没有固定不变的意识形态,而是选择合适的意识形态。

-- Hacker News 读者

4、

互联网软件行业的开发模式,已经从漫长的开发周期和专门的质量保证(QA)阶段,转移到了快节奏的连续集成/连续交付(CI / CD)管道,这样做极大地提高了迭代速度。

-- 《黑暗金丝雀模式的生产模式》

5、

你能在下面的一副扑克牌中,快速找到三个方块的那张牌吗?这就是用户面对大型菜单进行导航时的感受。

-- 《大菜单导航的解决方案》

6、

三年前,我们搬到伦敦教书,一直租房住,现在可以买房了。

-- 英国马丁·海尔教授(Martin Hairer)的获奖感言。他获得了今年的数学突破奖,奖金为300万美元,这是目前奖金最高的科学奖项。

7、

它还没有死,但确实已经写完了。

-- Moment.js 宣布停止开发,进入维护状态。

回顾

2019年的本周(第 74 期):信息的商业模式为什么不是收费

2018年的本周(第 22 期):猴子自拍,版权归谁

订阅

这个周刊每周五发布,同步更新在阮一峰的网络日志微信公众号

微信搜索"阮一峰的网络日志"或者扫描二维码,即可订阅。

(完)

文档信息

  • 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证
  • 发表日期: 2020年9月18日

立即购买:淘宝网]]>
IT技术精华 2020-09-18 09:09:03
40870 [原]修改Linux的swap空间实现进程注入 作者:dog250 发表于 2020/09/17 22:35:03 原文链接 [sourcelinkurl]
阅读:4

立即购买:淘宝网]]>
IT技术精华 2020-09-17 23:09:05
40850 Compare two tables in BigQuery As this answer, the best solution for comparing two tables in BigQuery is:

(
  SELECT * FROM table1
  EXCEPT DISTINCT
  SELECT * from table2
)

UNION ALL

(
  SELECT * FROM table2
  EXCEPT DISTINCT
  SELECT * from table1
)

But in my test, two tables with the same rows report difference by using the above snippet. Then I found out that the order of column names may be different, and the order of rows too. Then the better solution should be fixing the order of column names and rows:

(
  (
  SELECT col, col2, col3, col4
  FROM table1
  ORDER BY col1, col2
  )
  EXCEPT DISTINCT
  (
  SELECT col1, col2, col3, col4
  FROM table2
  ORDER BY
  col1, col2
  )
)
UNION ALL
(
  (
  SELECT col, col2, col3, col4
  FROM table2
  ORDER BY col1, col2
  )
  EXCEPT DISTINCT
  (
  SELECT col1, col2, col3, col4
  FROM table1
  ORDER BY
  col1, col2
  )
)

The post Compare two tables in BigQuery first appeared on Robin on Linux.


立即购买:淘宝网]]>
IT技术精华 2020-09-17 20:09:02
40729 [原]ROP(Return Oriented Programming)原理解析 作者:dog250 发表于 2020/09/17 00:05:26 原文链接 [sourcelinkurl]
阅读:13

立即购买:淘宝网]]>
IT技术精华 2020-09-17 03:09:04