程序猿小风扇

合抱之木,生于毫末。

0%

一、基本概念

所谓路由管理,就是管理页面之间如何跳转,通常也可被称为导航管理。Flutter 中的路由管理和原生开发类似,无论是 Android 还是 iOS,导航管理都会维护一个路由栈,路由入栈(push)操作对应打开一个新页面,路由出栈(pop)操作对应页面关闭操作,而路由管理主要是指如何来管理路由栈。

二、MaterialPageRoute

2.1 基本定义

MaterialPageRoute 是 Material组件库提供的组件,它可以针对不同平台,实现与平台页面切换动画风格一致的路由切换动画:

  • 对于 Android,当打开新页面时,新的页面会从屏幕底部滑动到屏幕顶部;当关闭页面时,当前页面会从屏幕顶部滑动到屏幕底部后消失,同时上一个页面会显示到屏幕上。
  • 对于 iOS,当打开页面时,新的页面会从屏幕右侧边缘一直滑动到屏幕左边,直到新页面全部显示到屏幕上,而上一个页面则会从当前屏幕滑动到屏幕左侧而消失;当关闭页面时,正好相反,当前页面会从屏幕右侧滑出,同时上一个页面会从屏幕左侧滑入。
1
2
3
4
5
6
MaterialPageRoute({
WidgetBuilder builder,
RouteSettings settings,
bool maintainState = true,
bool fullscreenDialog = false,
})
  • builder 是一个WidgetBuilder类型的回调函数,它的作用是构建路由页面的具体内容,返回值是一个widget。我们通常要实现此回调,返回新路由的实例。
  • settings 包含路由的配置信息,如路由名称、是否初始路由(首页)。
  • maintainState 默认情况下,当入栈一个新路由时,原来的路由仍然会被保存在内存中,如果想在路由没用的时候释放其所占用的所有资源,可以设置maintainStatefalse
  • fullscreenDialog 表示新的路由页面是否是一个全屏的模态对话框,在 iOS 中,如果fullscreenDialog为 true,新页面将会从屏幕底部滑入(而不是水平方向)。
阅读全文 »

一、三种常见的状态管理

  • 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],
),
),
);
}
}
阅读全文 »

一、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'),
],
),
);
阅读全文 »

一、什么是期权

期权,是指一种合约,该合约赋予持有人在某一特定日期或该日之前的任何时间以固定价格购进或售出一种资产的权利。期权是一种权利。持有人享有权利但不承担相应的义务。

1.1 一个简单的例子

Apple公司预计2022年9月10日发行最新款的iPhone14,而就在此时一些零售商推出了“6000元换购iPhone14”的换购券,每张售价50元。这个例子中,购买了该购物券,便是享有了一项权利,即到2022年9月10日当天,可以自行决定是否以6000元的价格换购iPhone14,本质上享有的是一种自主权利,这便是期权的雏形。

1.2 几个基本要素

还是以上面这个例子,以期权的视角来看看对应哪些基本要素:

要素 对应内容
合约标的资产 iPhone14
合约单位 1台
行权价 6000元
到期日 2022年9月10日
交割方式 实物交割

到目前为止,大部分应该容易被理解。除了行权价这个概念,这里稍后再作解释。继续回到现实角度来看,什么情况下选择行使换购券权利更划算:

iPhone14官方发行价 直接购买的成本 使用换购券的成本 使用换购券收益
6500 6500 6000 + 50 = 6050 6500 - 6050 = 550
6100 6100 6000 + 50 = 6050 6100 - 6050 = 50
6050 6050 6000 + 50 = 6050 6050 - 6050 = 0
6000 6000 6000 + 50 = 6050 6000 - 6050 = -50
5500 5500 6000 + 50 = 6050 5500 - 6050 = -550

不难分析,只有当iPhone14的发行价 > 6050,使用换购券才划算。而随着发行价越高,使用换购券的收益率越高,换句话说就是越贵越划算。

阅读全文 »

一、从一个简单的例子开始

在《Go语言圣经》的第一章有这么一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import (
"fmt"
"log"
"net/http"
)

func sayHello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hello world!")
}

func main() {
http.HandleFunc("/", sayHello)
err := http.ListenAndServe(":9090", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}

这是一个基于 HTTP 协议的 WEB 服务,通过监听 9090端口 来响应客户端的请求;从上面可以看出,编写一个 WEB 服务器很简单,只要调用 http 包的两个函数就可以了;并且实际上这个 WEB 服务内部支持高并发,也就是在同一时间可以响应来源多个客户端的请求;Go 大道至简的设计理念,对于长期沉浸在 C/C++ 程序员是一种极大的解放。

那么,Go 是如何实现WEB高并发的?带着这个问题,开始一步步来分析。

阅读全文 »

背景

笔者在《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

阅读全文 »

分析

在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,从而出现白屏现象。

阅读全文 »