0%

QR code,Quick Response,一种矩阵条形码,二维条形码,在1994年由供职于日本电装公司Denso Wave(丰田子公司)一名职员腾弘原(Masahiro Hara)发明,灵感来源于围棋,用于追踪流水线上的汽车,二维码之所以能快速在汽车制造领域外流行是因为自身的快速读取性,高存储性,更低的容错率,不需要特殊的设备读取。

image

image

image

二维码里可包含地理信息,身份标识,网页地址,是对这些信息进行了编码

可编码的原始信息可分为四种:

编码模式 内容
Numeric 1234567890
Alphanumeric ABCDE…..Z 1234567890
Byte 0000111100001111
Kanji 漢字の部首・画数・読み方・筆順・意味などを調べることができる漢字辞典サイトで

每一种模式使用不同的方法把text转换为比特,尽可能使用效率高的方法,编码后的结果是一串比特,8bit为一个单位,称为data codeword。

二维码拆分

image

image

1 .Quiet zone: 二维码存放的区域,白色底板,用来和其他的图案隔离开来,比如被玷污的信封上,报纸上或者有过摩擦的纸箱表面

2.Finder patterns:左上,右上,左下的三个黑白正方形,用来确认是否为二维码,以及二维码的旋转方向

3.Alignment pattern: 在非正常情况下用来确保可以正常解码,比如印在凸面上,扫描角度不正等

4.Timing pattern:紫色连线部分,小黑白方块组成,用来辅助确认二维码里具体的信息(data cell),尤其是二维码污损的情况下

5.Version infomation:绿色部分,用来确认版本信息

6.Data Cells:剩余的黑白点部分,全为具体数据

容错率

Error Correction Level Error Correction Capability
L (low) Recovers 7% of data
M (medium) Recovers 15% of data
Q (quarter) Recovers 25% of data
H (high) Recovers 30% of data

容错率的特点是在原始数据后面加上Reed-Solomon Code。打个比方,一段原始数据里包括100个codeword,需要修复50个,那么需要在数据尾部挂上100个Reed-Solomon code(两倍),这样下来总数为200codeword,容错率为25%,容错等级为Q。也可以认为刚才的例子容错率为50%,鉴于在通常情况下数据部分会污损,所以还是按25%来。容错率越高,数据越大

PS:Reed-Solomon Code 是一种在制作音乐CD领域较为常用的数学方法,一开始发明用来卫星或者星际通讯降噪,可以在比特级纠错

确定数据使用的最小版本

最小的二维码为21 * 21像素,最大是177 * 177像素,不同的尺寸代表不同版本,21 * 21为version 1,25 * 25为version 2,4个像素为一个步进,以此类推,177 * 177为version 40。

每一个版本都有数据容量上限,数据容量取决于编码的信息长度和纠错级别,字符容量表展示了每个版本不同纠错级别的容量

打个比方,HELLO WORLD包含11个字符,使用Q级别纠错,版本1 Alphanumeric最大容量是16字符,那么版本一就是HELLO WORLD最低版本,依次类推

下表是二维码数据容量的上限

Encoding Mode Maximum number of characters a 40-L code can contain in that mode
Numeric 7089 characters
Alphanumeric 4296 characters
Byte 2953 characters
Kanji 1817 characters

添加模式标识

每一种编码模式机器需要通过在数据前添加标识来确定,HELLO WORLD的编码代码为表格第二行,0010

Mode Name Mode Indicator
Numeric Mode 0001
Alphanumeric Mode 0010
Byte Mode 0100
Kanji Mode 1000
ECI Mode 0111

添加个数标识

个数标识表示字符串长度,HELLO WORLD长度为11,二进制1011,版本1的比特位长度为9,在左边补齐0,结果为 00000 1011,添加在模式标识后为 0010,00000 1011

下面是各版本规定的比特位长度

Versions 1 - 9

Mode Bits
Numeric 10
Alphanumeric 9
Byte 8
Japanese 8

Versions 10 - 26

Mode Bits
Numeric 12
Alphanumeric 11
Byte 16
Japanese 10

Versions 27 - 40

Mode Bits
Numeric 14
Alphanumeric 13
Byte 16
Japanese 12

真实数据编码

HELLO WORLD Alphanumeric模式为例,参考编码表为

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
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
A 10
B 11
C 12
D 13
E 14
F 15
G 16
H 17
I 18
J 19
K 20
L 21
M 22
N 23
O 24
P 25
Q 26
R 27
S 28
T 29
U 30
V 31
W 32
X 33
Y 34
Z 35
36 (space)
$ 37
% 38
* 39
+ 40
- 41
. 42
/ 43
: 44

把字符串拆分为如下形式,O和空格在一起

1
HE,LL,O ,WO,RL,D

找到对应的数字

H → 17
E → 14

第一个乘以45,加第二个

(45 * 17) + 14 = 779

转换为11bit长的字符串,左边补0

779 → 01100001011

以此类推得到所有的数据,最后一位D -> 13 转换长度为6比特 001101

Mode Indicator Character Count Indicator Encoded Data
0010 000001011 01100001011 01111000110 10001011100 10110111000 10011010100 001101

转换为8-bit CodeWord

二维码要求数据必须填满,参考纠错表,此处,我们采用的是版本一,Q级别,可以有13个CW,每个CW为8-bit,总容量为13*8=104bit,到了这一步,我们的数据总长度为74,离满格还差34,需要在尾部添加四个0,最多四个,如果数据长度为102,则补齐两个0,以此类推,到现在我们的数据为

Mode Indicator Character Count Indicator Encoded Data Terminator
0010 000001011 01100001011 01111000110 10001011100 10110111000 10011010100 001101 0000

拆分补齐数据,使之长度为8的倍数

74 + 4 = 78bit,在末尾添加00成为80bit

前:

00100000 01011011 00001011 01111000 11010001 01110010 11011100 01001101 01000011 010000

后:

00100000 01011011 00001011 01111000 11010001 01110010 11011100 01001101 01000011 01000000

距离104还差24bit,使用Pad bytes继续填充

236 17
11101100 00010001

00100000 01011011 00001011 01111000 11010001 01110010 11011100 01001101 01000011 01000000 11101100 00010001 11101100

236 - 17 - 236 - 17 如此循环下去,直到填满为止,对应的十进制为

32,91,11,120,209,114,220,77,67,64,236,17,236

数据纠错编码

  • 数据分组

  • Reed-Solomon算法,包含多项式、伽罗瓦群论、对数、XOR计算等组合方法生成

    image

    image

image

根据算法产生的纠错码为

168 72 22 82 217 54 156 0 46 15 180 122 16

合起来为

Col 1 Col 2 Col 3 Col 4 Col 5 Col 6 Col 7 Col 8 Col 9 Col 10 Col 11 Col 12 Col 13
Block 1 32 91 11 120 209 114 220 77 67 64 236 17 236
Block 2
ECC1 168 72 22 82 217 54 156 0 46 15 180 122 16
ECC2

内容连起来,转换为二进制,类似于

1
01000011111101101011011001000110

尾部添加0

不同的版本在末尾添加0补全

QR Version Required Remainder Bits
1 0
2 7
3 7
4 7
5 7
6 7
7 0
8 0
9 0
10 0
11 0
12 0
13 0
14 3
15 3
16 3
17 3
18 3
19 3
20 3
21 4
22 4
23 4
24 4
25 4
26 4
27 4
28 3
29 3
30 3
31 3
32 3
33 3
34 3
35 0
36 0
37 0
38 0
39 0
40 0

在这里我们用第一版,不加零

生成矩阵

image

image

image

添加分隔线

img

img

添加对齐图案

img

version 2及其以上需要这个图案,经过查表可以得知,以左上角为原点,可以摆放四个位置,(6, 6), (6, 18), (18, 6) and (18, 18),不能遮挡,故右下角合格

INCORRECT

img

CORRECT

img

img

添加Timing Patterns

imgimg

img

开始和结束均为黑色

黑色模块和数据保留区

imgimg

左下角黑点 Dark Module和蓝色区域部分Reserved Areas

版本数据区域

imgimg

version 7及其以上必须包含版本信息,两个蓝色区域

添加文本数据

img

img

img

如果遇到了阻挡,绕过去

img

如果遇到了垂直的Timing Pattern,跳过去到下一列

img

Data Masking

为什么需要masking,因为要减少复杂度,提升机器的识别率

什么是masking

  • 黑白切换

Mask方法

  • 只mask数据区域

Mask种类

Mask Number If the formula below is true for a given row/column coordinate, switch the bit at that coordinate
0 (row + column) mod 2 == 0
1 (row) mod 2 == 0
2 (column) mod 3 == 0
3 (row + column) mod 3 == 0
4 ( floor(row / 2) + floor(column / 3) ) mod 2 == 0
5 ((row * column) mod 2) + ((row * column) mod 3) == 0
6 ( ((row * column) mod 2) + ((row * column) mod 3) ) mod 2 == 0
7 ( ((row + column) mod 2) + ((row * column) mod 3) ) mod 2 == 0

寻找最佳Mask

每一种mask加上去后进行四种惩罚算法验证,得分最低者为最合适的mask

  • The first rule gives the QR code a penalty for each group of five or more same-colored modules in a row (or column).
  • The second rule gives the QR code a penalty for each 2x2 area of same-colored modules in the matrix.
  • The third rule gives the QR code a large penalty if there are patterns that look similar to the finder patterns.
  • The fourth rule gives the QR code a penalty if more than half of the modules are dark or light, with a larger penalty for a larger difference.

这里举第一种惩罚算法为例

imgimg

img

350得分最低,选择第一种mask

添加格式信息

纠错级别有四个 L M Q H,Maks有八种,一共32种组合

格式信息是一个15-bit长的字符串

前5个bit包含纠错版本(2-bit)和mask版本(3-bit)

Error Correction Level Bits Integer Equivalent
L 01 1
M 00 0
Q 11 3
H 10 2
Mask Number If the formula below is true for a given row/column coordinate, switch the bit at that coordinate
0 (row + column) mod 2 == 0
1 (row) mod 2 == 0
2 (column) mod 3 == 0
3 (row + column) mod 3 == 0
4 ( floor(row / 2) + floor(column / 3) ) mod 2 == 0
5 ((row * column) mod 2) + ((row * column) mod 3) == 0
6 ( ((row * column) mod 2) + ((row * column) mod 3) ) mod 2 == 0
7 ( ((row + column) mod 2) + ((row * column) mod 3) ) mod 2 == 0

11000,剩下的十位重新使用Reed-Solomon算法生成ECC code,填进去二维码蓝色区域

The layout of format string bits, where 14 is the most significant bit and 0 is the least significant bit

无论哪个版本,格式信息总是在红色的区域

imgimg

生成版本信息

version 7及其以上版本,必须包含18bit长度的版本信息,6-bit表示版本号,12-bit代表纠错码,同样的算法生成

imgimg

蓝色区域,3*6格子,下图是一个version 7示例

img

顺序是从右下到左上填充

00 01 02
03 04 05
06 07 08
09 10 11
12 13 14
15 16 17
17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
0 0 0 1 1 1 1 1 0 0 1 0 0 1 0 1 0 0

添加合适mask(上面已经叙述过)

添加quiet zone

最终生成 HELLO WORLD 二维码,1-Q,Alphanumeric

img

完。

image

附录:

条形码:一维码,一种包含物料详情的机读型的光学标签

image

ASCII Table:

image

image

在线进制转换网站 https://tool.lu/hexconvert/

参考:https://www.nayuki.io/page/creating-a-qr-code-step-by-step

https://en.wikipedia.org/wiki/QR_code

https://www.explainthatstuff.com/how-data-matrix-codes-work.html

https://www.thonky.com/qr-code-tutorial/introduction

​ 今天找《code》封面时看到一个个人博客,推荐了作者自己读过的最好的几本技术书籍,其中包括了《code》,还有大名鼎鼎的《The C Programming Language》,在此收藏一下,日后有时间去拜读一遍,也许是never,:-)

​ 看完书籍推荐,顺便看了下作者的about,法国Lyon(里昂?)出生,Romans-Sur-Isere长大,2004在Grenoble University获得CS Master学位,在巴黎做了一年程序员后,为了逐梦和学英语,使用Work Holiday Visa去了多伦多,计划呆五个月,最终呆了9年。在多伦多的第一份工作是busboy

image

不久后回归程序员,在Rogers和QuickPlay供职,然后成为一家创业公司Memset Software的老板。

2012年获得加拿大国籍,拥有加拿大和法国双国籍。

2014年入职谷歌,在加州落脚。

出过两本书,Game Engine Black Book: Wolfenstein 3DGame Engine Black Book: DOOM

完。

​ 以CRT(cathode-ray tube)阴极射线管显示器为例,把计算机数据信号转换并传给CRT显示的部件叫显示适配器(video display adapter),适配器所在的线路板就是大家所熟知的显卡(video board)

image

image

image

二维图像显示看起来有点复杂,简单来说图像是由一束光(beam)快速连续扫过整个屏幕生成的,从屏幕左上角开始,水平扫到右边,到头另起一行重新开始,直到最右下角结束。每一条水平的线叫做扫描线,扫完整个屏幕生成一张图像,每秒生成六十张图像,这个速度足够快,不会出现一闪一闪体验不好,也就是通常所说的卡顿。为什么选择60Hz而不是其他,可以看这里,大意是CRT因为构造原理关系对磁场非常敏感,为了降低交流电磁场的影响,选择和北美交流电一样的频率。我国交流电受前苏联影响是50Hz。

image

早期的显示器(电视机)是黑白的,因为原理和成本相比彩色会比较简单便宜,0.5v代表黑色,2v代表白色,这两个电压之间表示灰色

image

接下来让我们把目光从电视机转向计算机,从计算机的角度来看,生成一张图像,最方便的办法是把图像分解为一个网格(rectangular grid),每一个格子叫做一个像素pixel (picture element)

image

由于早期电视显示带宽4.2MHz的限制,显示分辨率最高为320*200,横向320像素,纵向200像素,每一个像素在黑白之间切换,一共可以显示64000个像素。

现在我们想在这些格子里显示一些文本,一次性能显示多少内容呢?这个取决于每一个字符用多少个像素来实现。下图是一个8*8像素的实现

image

上图中显示的是常用的一些ASCII码,在内存中,占用的大小是7bit,如果要显示,对应占用的大小就是64bit,这64bit所代表的数值也可以看作是字符的一种码。按照这种定义,我们可以在320*200分辨率的屏幕上显示每行40字,一共25行的文本内容。

image

为了动态显示,显示适配器配备一个RAM来存要显示的内容,CPU可以访问更新数据,也包含一个字符串生成器,里面记录着ASCII码对应的像素排列格式,只读形式,字符的像素排列一旦定下里,不能随意改动(ROM)。

image

在八十年代,硬件成本非常昂贵,7bit字符对应64bit显示,会显得有点大,为了节约成本,进化出了另一种对应的显示方式

image

左边每一行是10bit,右边是8bit,每行对应,左边前7位依然是ASCII码,后三位代表第几行,从上往下数一共8行,最终的效果是

image

image

在文本输入过程中,会有一个游标紧跟其后,来提醒用户下一个字符应该出现在哪里,行和列的坐标数据存放在显示适配器上一个8bit的寄存器(register)里,CPU进行实时计算更新

image

image

在IBM1981年发售第一款PC时搭配的显示器(上图)每行显示80个字符,一共25行,每行显示80个字符是因为IBM早期的计算机使用打孔卡片,上面一次性最多显示80个

image

到了1987年,IBM和Apple计算机图形显示分辨率都提升为640*480 (4:3,爱迪生电影公司制定的标准),随着后面像素的提升,800 * 600,1024 * 768,1280 * 960,都是按照这个比例。

后面随着技术的发展和需求的提升,出现了图形化界面,显卡的功能从text only进化到多媒体

image

在早期的显卡工作过程中,cpu把数据写入显存的同时,还在画图,包括富文本的大小和字体,因此比仅显示文本的显卡拥有更多的内存,上面举例的显示分辨率有64000个像素,每个像素为1bit,显示黑与白,对应的内存就为64000bit大小,8000字节。在实际使用过程中,需要显示的图像不仅仅是非黑即白,也需要显示黑白中间的颜色,也就是灰色,每个像素就需要扩大为1字节(8bit),00h代表黑色,FFh代表白色,中间的值代表不同颜色的灰,内存扩大为64000字节(byte)。

image

如果需要显示彩色,我们知道任何颜色都是由三原色组成,Red,Green,Blue,RGB

image

image

每个像素三个bit,可以呈现出八种颜色,最终呈现的效果是

image

在现实生活中,色彩的种类更加丰富,把每个像素点上升到3字节,每个字节代表一种原色,每一种原色都有256种程度表示,组合起来一共可以显示1677,7261种颜色,这被称作full color,真彩色

image

image

在显示器成像方面,有两种成像的方式,vector和raster

image

显示发展到这一步,交互呼之欲出

鼠标是怎么移动的?在第一台GUI Alto计算机上,屏幕的分辨率是606*808,一共489648像素,每一个像素对应一个bit,颜色为黑白,需要64KB显存。在显存写入数据,经过计算呈现在屏幕上,移动鼠标时,底部的滚轮通过传感器输入坐标,显存根据坐标在不同的位置计算01的变化,呈现鼠标移动的效果,如果鼠标点击了按钮,对应位置的比特通过计算来响应按钮的点击事件。这种新颖的交互方式同时也催生了世界上第一款面向对象的语言–Small Talk

image

image

说到这里,顺便提一下bitmap,屏幕上像素显示的图像如果想存起来,最简单的方法就是对应的矩阵像素点在内存中对应存起来,这种数据类型存起来以后就是bitmap

image

存起来以后发现文件很大,并且很多数据是重复的,比如图中有一大片蓝天和白云

image

我们可以存储1000这个数字和一个蓝点来表示蓝色像素有一千个,更加节省bit,这就是压缩算法的基本思想,为了节省空间。有的压缩算法会导致数据有损,但损失可接受,不影响图片效果,比如最常见的JPEG(Joint Photography Experts Group)

OCR

前面讲过,ASCII码转为像素形式显示,显卡内部的ROM有一一对应关系,如果逆向过来,已知一个像素排列阵型(pixel pattern),如何确定对应的ASCII码呢?通常来说有些困难,因为通常得到的bitmap不一定和ROM预先存储的一样,需要一些软件算法来尽量还原,这就是OCR(optical character recognition)。简单阐述大致原理是根据每一种特定的排列通过特定的算法生成一列哈希值,然后去和已知图像的哈希值对比,最接近的便是首选答案。

image

衍生问题:显示卡顿优化

一秒播放六十张图片,低于这个频率视觉上会有卡顿,每16.7毫秒需要生成一张图片,图片生成需要CPU计算生成bitmap,交给GPU渲染,然后放到frame缓冲区等待显示到屏幕上

CPU方面:减少计算量

  • 视图轻量级(减少点)
  • 视图布局简单(减少点)
  • 减少修改属性
  • 合理使用线程,避免线程卡顿
  • 富文本处理合理
  • 图片解码合理

GPU方面:减少渲染量

  • 视图层级简单(减少点)

  • 渲染图片少(减少点)

  • 渲染图片小(减少点)

  • 合理使用透明度

  • 避免离屏渲染(阴影,圆角,遮罩)(减少点)

完。

PS:如果文中某些图片加载失败,请参考这里设置host文件

参考链接:

https://en.wikipedia.org/wiki/Code:_The_Hidden_Language_of_Computer_Hardware_and_Software

https://blog.infolink.com.tw/2021/rediscover-pixel-dpi-ppi-and-pixel-density/

​ 今天看耗子哥新文如何做一个有质量的技术分享,文末做了一个分享示例

For example, if you want to sharing a topic about Docker. the following outlines would be good one:

  • What’s the major problems need to solve. (Provision, Environment, Isolation etc.)
  • The Alternative solutions. (Puppet/Chef/Ansible, VM, LXC etc.)
  • The Best Solution – Docker. Why?
  • Docker’s key techniques – image, cgroup, union fs, namespace…
  • Docker’s Pros/Cons
  • Further reading list.

感觉吸收新知识也是同理,建立一个问题,引起好奇心,然后顺理成章往下推演,看现在的最佳解决方案(知识)是怎么经过筛选得来的。

下面是自己收集的一些tips供自己参考,持续更新:

  • teach someone else
  • learn in short bursts of time
  • take notes by hand
  • take a study nap
  • the human brain processes images 60,000 times faster than text, and 90 percent of information transmitted to the brain is visual

参考:learn anything faster

Humans Process Visual Data Better

​ 一开始想添加一个博客评论系统,方便交流,参考的是kid的博客,选择最简单的方案 utteranc.es,hexo已经有插件集成,使用的是npm安装。安装完成后在博客底部成功出现评论框

comment-box.jpg

点击github登录时出现了访问失败,找不到资源

image

经过测试,确定原因是自己域名没有https的原因,链接添加参数后默认以https访问出现请求失败情况。首先想到的是去阿里万网看自带的服务,价格有些小贵image

为了秉持互联网免费原则,确定了Netlify方案,根据教程配置完成后大概等了半天时间,https域名就可以正常使用了。

没有尝试的方案:

最后,感谢蓝伟洪小蘑菇配合测试评论系统。

​ 在纯flutter应用里打开网页,目前流行两个插件:

  • flutter_webview_plugin (以下简称三方web)

    • 特点

      1.实测中滑动相对流畅

      2.bug较少

      3.不在flutter的widget tree中,视图在app最顶层,通过channel通知来实现显示和隐藏,全局是一个单例

      4.非官方

  • webview_flutter(以下简称官方web)

    • 特点

      1.镶嵌在widget tree中,方便在flutter中控制

      2.键盘弹出有部分bug

      3.官方支持

由于历史原因和开发成本考虑,继续沿用三方web作为浏览器,遇到一个需求,就是需要打开网页的时候秒开,实现类似今日头条的效果,经过调研和结合flutter特性,最终方案定型为下图

image

image

​ 在实现iOS过程中比较顺利,在appdelegate启动的时候加载WebViewController放在后台待命,MainViewController新增一个导航在合适的时候来push WebViewController

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
    lazy var webViewEngine:FlutterEngine = {
let engine = FlutterEngine(name: "webViewEngineName")
engine.run(withEntrypoint: "webViewMain")
return engine
}()

lazy var webViewFlutterVC:FlutterViewController = {
let webFlutterVC = FlutterViewController(engine: self.webViewEngine, nibName: nil, bundle: nil)
webFlutterVC.view.backgroundColor = UIColor.white
return webFlutterVC
}()

lazy var registerWebFlutterVC: Void = {
CopiedPluginRegistrant.register(with: self.webViewFlutterVC)
}()


override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
//启动WebViewController
_ = self.registerWebFlutterVC

return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}

同样的原理框架在实现安卓的时候因为实现原理不同,做了改动:

  1. 两个页面均由activity实现,在安卓app启动的时候需要指定一个activity作为单例launcher,把web activity作为launcher先启动,等内部webview预热完成,再启动main activity

manifest文件配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<activity
android:name=".FlutterWebViewActivity"
android:launchMode="singleInstance"
android:theme="@style/activityTheme">

<!--启动先预热第二引擎页面,oncreate里面再切换回app主页-->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

<!--启动的时候加背景图防止黑屏-->
<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background" />


</activity>

  1. 在预热web activity过程中,如果不注意两个activity engine注册插件先后顺序和冲突,会出现flutter和原生交互找不到method方法报错,需要按照下面的顺序来

    • 在Application onCreate方法里预热web引擎,automaticallyRegisterPlugins一定要传false

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      public class MyApp extends FlutterApplication {
      @Override
      public void onCreate() {
      super.onCreate();

      //启动预加载一个引擎for 单例 webview 不自动注册插件
      FlutterEngine webEngine = new FlutterEngine(this,null,false);
      FlutterEngineCache.getInstance().put(WebEngine.ENGINE_ID,webEngine);

      }
      }
    • 继承FlutterActivity类,覆写获取engine方法,拿到已经预热好的engine

      1
      2
      3
      4
      5
      //加载预热的引擎,在my_app类中创建
      override fun provideFlutterEngine(context: Context): FlutterEngine? {
      //return super.provideFlutterEngine(context)
      return FlutterEngineCache.getInstance().get(WebEngine.ENGINE_ID)
      }
    • 执行指定的entry point

      1
      2
      3
      4
      override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
      runEntrypoint(flutterEngine)
      super.configureFlutterEngine(flutterEngine)
      }

这样,两端就都可以实现H5“秒开”体验。

完。

症状:

除了电源按钮,键盘和触摸板全部失灵,触摸板可以按下去,鼠标无法滑动,外接usb键盘鼠标可以使用

处理办法:

重置NVRAM

步骤:首先按住 option+commamd+P+R键,再按住电源键开机,等待听duang的开机声,听到四声后,松开按键,按电源键开机,恢复正常

参考:https://www.jianshu.com/p/51d6142b564a

完。

报错原文:

1
2
Cloning into '/var/folders/nd/n2mbp09n1lqbyyf586wntcww0000gn/T/d20210428-5764-pwxnoe'...
fatal: unable to access 'https://github.com/SDWebImage/SDWebImage.git/': LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to github.com:443

原因分析:

本地git使用代理连接github,代理出了问题

问题解决:

让代理恢复正常或者不使用代理

1
2
git config --global --unset http.https://github.com.proxy
git config --global --unset https.https://github.com.proxy

去除了http 和 https的代理

完。

参考:https://www.jianshu.com/p/388c2914f22a

​ WK is run in a separate process so that it can draw on native Safari JavaScript optimizations,this means WK loads web pages faster than UIWeb,UIWeb uses traditional JS engines which are basic interpreters,Apple’s Nitro engine uses a 4 tier compiling strategy which can execute JS 40 times faster than a traditional JS interpreter.

Nitro means Nitromethane, is a kind of fuel for car engine.

source:

早上开车等红绿灯时想到几个月前写的一个算法,应该可以更简单,今天重新写了一遍

  • 寻找下一个数字,例如输入345,输出354

    思路:从最右边位和上一位对比,如果大,就往前移,移动完,尾数排序,从小到大,保证次大

  • 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
//字符串分成数组
NSMutableArray *convertStringToArray(NSString *str){
NSMutableArray *marr = [NSMutableArray array];
for (NSInteger i = 0; i < str.length; i++) {
[marr addObject:[str substringWithRange:NSMakeRange(i, 1)]];
}
return marr;
}

//数组拼成字符串
NSString *convertArrayToString(NSArray *arr){
NSMutableString *mstr = [NSMutableString string];
for (NSString *str in arr) {
[mstr appendString:str];
}
return mstr;
}

//冒泡排序 从小到大
NSString *bubbleSort(NSString *str){
NSArray *sortedArray = convertStringToArray(str);
NSMutableArray *marr = [NSMutableArray arrayWithArray:sortedArray];
//最后一位的索引
unsigned long j = marr.count-1;
for (;j>0;j--) {

BOOL didSort = NO;
//第一轮结束
for (int i=0; i<j; i++) {
NSString *pre = marr[i];
NSString *next = marr[i+1];
if (pre.integerValue > next.integerValue) {
[marr exchangeObjectAtIndex:i+1 withObjectAtIndex:i];
didSort = YES;
}
}

if (!didSort) {
break;
}
}

NSMutableString *mstr = [NSMutableString string];
for (NSString *str in marr) {
[mstr appendString:str];
}
return mstr;

}


NSString *findNextLargerNum(NSString *oriNum){
NSString *resultStr = oriNum;
NSMutableArray *oriNumMArr =[NSMutableArray arrayWithArray:convertStringToArray(oriNum)];

for (int j = oriNumMArr.count-1; j>0; j--) {

BOOL getResult = NO;
NSString *last = oriNumMArr[j];
for (int i = j-1; i>=0; i--) {
NSString *pre = oriNumMArr[i];
if (last.integerValue > pre.integerValue) {
[oriNumMArr exchangeObjectAtIndex:i withObjectAtIndex:j];
NSRange range = NSMakeRange(i+1, oriNumMArr.count - 1 - i);
NSString *surfixStr = convertArrayToString([oriNumMArr subarrayWithRange:range]);
NSString *surResultStr = bubbleSort(surfixStr);
NSString *preStr = convertArrayToString([oriNumMArr subarrayWithRange:NSMakeRange(0,i+1)]);
resultStr = [NSString stringWithFormat:@"%@%@",preStr,surResultStr];
getResult = YES;
break;
}
}


if (getResult) {
break;
}

}
return resultStr;
}
使用:
1
2
3
4
NSString *str = findNextLargerNum(@"8976");
NSLog(@"======%@",str);
//打印结果
======9678