初探Flutter(二) 状态管理

一、三种常见的状态管理

  • Widget 管理自己的状态。
  • Widget 管理子 Widget 状态。
  • 混合管理(父 Widget 和子 Widget 都管理状态)。

1.1 Widget 管理自己的状态

1.1.1 适用场景

如果状态是有关界面外观效果的,例如颜色、动画,那么状态最好由 Widget 本身来管理。

1.1.2 例子

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
class TapboxA extends StatefulWidget {
TapboxA({Key? key}) : super(key: key);

@override
_TapboxAState createState() => _TapboxAState();
}

class _TapboxAState extends State<TapboxA> {
bool _active = false;

void _handleTap() {
setState(() {
_active = !_active;
});
}

Widget build(BuildContext context) {
return GestureDetector(
onTap: _handleTap,
child: Container(
child: Center(
child: Text(
_active ? 'Active' : 'Inactive',
style: TextStyle(fontSize: 32.0, color: Colors.white),
),
),
width: 200.0,
height: 200.0,
decoration: BoxDecoration(
color: _active ? Colors.lightGreen[700] : Colors.grey[600],
),
),
);
}
}

Read more…

初探Flutter(一) Widget

一、Widget

1.1 canUpdate方法

canUpdate(...)是一个静态方法,它主要用于在 widget 树重新build时复用旧的 widget。

  • 根本作用:是否用新的 widget 对象去更新旧UI树上所对应的Element对象的配置。
  • 判断规则:只要newWidgetoldWidgetruntimeTypekey同时相等时就会用new widget去更新Element对象的配置,否则就会创建新的Element

1.2 Flutter中的四棵树

1.2.1 基本联系与职责

  1. 根据 Widget 树生成一个 Element 树,Element 树中的节点都继承自 Element 类。
  2. 根据 Element 树生成 Render 树(渲染树),渲染树中的节点都继承自 RenderObject 类。
  3. 根据渲染树生成 Layer 树,然后上屏显示,Layer 树中的节点都继承自 Layer 类。
  • 布局和渲染逻辑在 Render 树中。
  • Element 是 Widget 和 RenderObject 的粘合剂。

1.2.2 例子

1
2
3
4
5
6
7
8
9
Container( // 一个容器 widget
color: Colors.blue, // 设置容器背景色
child: Row( // 可以将子widget沿水平方向排列
children: [
Image.network('https://www.example.com/1.png'), // 显示图片的 widget
const Text('A'),
],
),
);

Read more…

基于 LocalWebServer 实现 WKWebView 离线资源加载

一、背景

笔者在《WKWebView》一文中提到过,WKWebView 在独立于 app 进程之外的进程中执行网络请求,请求数据不经过主进程,因此,在 WKWebView 上直接使用 NSURLProtocol 无法拦截请求。所以如果需要使用到拦截请求,有种可行地方案是使用苹果开源的 Webkit2 源码暴露的私有API(详见原文第3小节:NSURLProtocol问题)。
但使用私有API,必然带来以下几个问题:

  • 审核风险
  • 拦截http/https时,post请求body丢失
  • 如使用ajax hook方式,可能存在 post header字符长度限制Put类型请求异常

由此看来,在 iOS11 WKURLSchemeHandler [探究] 到来之前,私有API并不那么完美。
所幸通过寻找发现,iOS系统上具备搭建服务器能力,理论上对实现 WKWebView 离线资源加载 存在可能性。

二、分析

基于iOS的local web server,目前大致有以下几种较为完善的框架:

  • CocoaHttpServer (支持iOS、macOS及多种网络场景)
  • GCDWebServer (基于iOS,不支持 https 及 webSocket)
  • Telegraph (Swift实现,功能较上面两类更完善)

因为目前大部分APP已经支持ATS,且国内大部分项目代码仍采用OC实现,故本文将以 CocoaHttpSever 为基础进行实验。
Telegraph 是为补充 CocoaHttpSever 及 GCDWebServer 不足而诞生,对于纯Swift项目,推荐使用 Telegraph

Read more…

关于 WKWebView 适配

一、分析

在iPhone 6s、iOS 10.3.2中,对 http://www.qq.com 进行10次请求,得到如下数据:

次数 UIWebView 内存消耗 WKWebView 内存(APP)消耗 UIWebView 请求耗时 WKWebView 请求耗时
1 67.47 MB 0.81 MB 4.13 s 0.80 s
2 58.23 MB 0.86 MB 1.16 s 0.54 s
3 57.83 MB 0.50 MB 1.14 s 0.56 s
4 59.38 MB 0.88 MB 1.08 s 1.07 s
5 59.70 MB 0.75 MB 1.07 s 0.71 s
6 64.05 MB 0.83 MB 1.47 s 0.65 s
7 59.45 MB 0.81 MB 1.11 s 0.63 s
8 57.55 MB 0.45 MB 1.15 s 0.64 s
9 58.47 MB 0.77 MB 1.17s 0.75 s
10 58.89 MB 0.84 MB 1.11 s 0.70 s

UIWebView平均内存消耗:54.13 MB
WKWebView平均(APP)内存消耗:0.75 MB
UIWebView平均请求耗时:1.46 s
WKWebView平均请求耗时:0.7 s

综上可得:WKWebView在请求耗时上为UIWebView的50%左右,内存上更是完胜。但实际上 WKWebView是一个多进程组件,网络请求以及UI渲染在其它进程中执行。仔细观察会发现:加载时,App进程内存消耗虽非常小甚至反而大幅下降,但Other Process的内存占用会增加。所以:在UIWebView上当内存占用太大的时候,App Process会crash;而在WKWebView上当总体的内存占用比较大的时候,WebContent Process会crash,从而出现白屏现象。

Read more…