移动端(Android、iOS)接入Cronet实践

QUIC协议

QUIC, a multiplexed stream transport over UDP 是Chromium使用的通信协议,是基于UDP实现的类似于 TCP+TLS+HTTP/2 的协议。也是HTTP 3.0的设计方案。有兴趣的话大家可参考文档: Playing with QUIC

Chromium项目是开源的,The Chromium Projects(http://dev.chromium.org/chromium-projects) 文档详细介绍了Chromium项目的实现原理,以及如何获取源码并进行编译。

Cronet 库是Chrome使用的移动端网络库。支持 HTTP、HTTP/2 以及 QUIC 协议。支持 Android 和 iOS 平台。 其编译工具是 gn 和 ninja,类似于 cmake 与 make 的关系。 下面介绍 Cronet 库的编译及编译注意事项。

获取Chromium源码

可以参考官方文档:Checking out and building Chromium for Mac

获取源码之前,首先需要下载安装 depot_tools 工具。在一个适当的目录下clone depot_tools包:

1
$ git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git

depot_tools的路径(最好是绝对路径,~需替换为$HOME)加进环境变量PATH中,假设 depot_tools 工程在/path/to/depot_tools目录下:

1
export PATH=$PATH:/path/to/depot_tools

如果从来没有下载过Chromium的代码的话,为源码创建一个文件夹并下载源码:

1
2
mkdir chromium && cd chromium
fetch --no-history chromium # 可能花费30min或几小时,依网络速度而不同

–no-history 可以节省代码下载时间,它忽略仓库的历史信息;整个代码量较大,约 14G,且需要翻墙,1M 左右的速度需要 20~30 分钟。若中间拉取失败,可以执行 gclient sync 继续拉取, 拉取结束后,该目录会生成一个 src 目录,包含 cronet 源码。

获取源码是很漫长的过程,Chromium项目的源码有8G。我花了2个半小时才完成。中途遇到了download_from_google_storage.py 下载文件没反应的问题,参考了Chromium的源码获取与编译(2018-06-06)

最终chromium/src代码大小如下:

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
$ du -d 1 -h | sort -h
48K ./google_update
56K ./build_overrides
184K ./apps
208K ./styleguide
420K ./gin
424K ./rlz
480K ./cloud_print
540K ./crypto
564K ./jingle
596K ./infra
632K ./dbus
684K ./sql
876K ./printing
916K ./fuchsia
916K ./url
1.0M ./ipc
1.0M ./pdf
1.1M ./skia
2.1M ./google_apis
3.4M ./storage
3.7M ./sandbox
5.0M ./courgette
6.2M ./build
6.5M ./headless
7.4M ./chromecast
7.8M ./android_webview
7.8M ./mojo
9.2M ./device
9.2M ./testing
10M ./docs
12M ./cc
13M ./ppapi
15M ./gpu
17M ./chromeos
18M ./services
19M ./remoting
20M ./extensions
22M ./base
27M ./ash
41M ./native_client_sdk
49M ./ios
75M ./ui
78M ./buildtools
80M ./media
94M ./content
98M ./out
112M ./net
158M ./tools
295M ./components
737M ./v8
784M ./chrome
1007M ./.git
2.0G ./native_client
13G ./third_party
19G .

命令行翻墙,需要使用 http proxy 配置:

1
2
3
4
5
6
export http_proxy=http://127.0.0.1:8118 
export https_proxy=http://127.0.0.1:8118

// git代理,我没有使用
git config --global http.proxy 'socks5://127.0.0.1:1080'
git config --global https.proxy 'socks5://127.0.0.1:1080'

启动 privoxy 转换 socks5 为 http proxy:

1
privoxy --no-daemon /usr/local/etc/privoxy/config

编译Cronet

Cronet的源码位于 src/components/cronet 目录,官方编译流程可参考Cronet build instructions,编译环境要求:

  • Linux:python 2.7.5,及 jdk 8,较高版本由于接口不兼容需避免使用。
  • Mac: python 2.7.5,jdk 8,以及 xcode。 进入 chromium/src 目录,执行下面命令

Android / iOS builds

1
$ ./components/cronet/tools/cr_cronet.py gn --out_dir=out/Cronet   # 生成 ninja 文件

如果主机是linux,build的是Android的库。如果主机是MacOS,build的是ios库。以下命令在Mac上会生成 cronet 静态库,目录 obj/components/cronet/ios/libcronet*.a

Desktop builds (targets the current OS)

1
gn gen out/Cronet

Running the ninja files

输入 ninja 文件执行编译,必须指定为 cronet_package

1
$ ninja -C out/Cronet cronet_package

这个命令会编译 cronet 模块,及其依赖的所有模块,包括base,crypto,boringssl,protobuf,url等。编译Cronet库,最终文件可以在out/Cronet/cornet中寻找。

生成的文件

iOS 库:

  • out/MyCronet/obj/components/cronet/ios/libcronet.a, 大小为 89M;
  • out/MyCronet/obj/components/cronet/ios/libcronet_static.a, 大小为 17M;
  • out/MyCronet/obj/components/cronet/ios/libcronet_deps_complete.a,大小为 1G。

Android 库:

  • out/Cronet-android/lib.java/components/cronet/android/ 所有 jar 包在此目录下,一般不用;
  • out/Cronet-android/cronet/ 需要使用cronet库的java API的so、及jar包都在此目录下
  • out/Cronet-android/libcronet.77.0.3825.0.so,strip 后的库,6M;
  • out/Cronet-android/lib.unstripped,未 strip 的库在此目录下。50.9M;
  • out/Cronet-android/gen/components/cronet/android/cronet_jni_registration.h,该文件便是自动生成的JNI头文件

使用Cronet

Debug Log

C++层的日志可以通过 adb logcat 输出. 但是默认只显示 FATAL 级别的日志。如果想要更改日志输出级别,可以通过以下命令打开chromium日志输出:

See VLOG(1) and VLOG(2) logging:

1
$ adb shell setprop log.tag.CronetUrlRequestContext VERBOSE

See VLOG(1) logging:

1
$ adb shell setprop log.tag.CronetUrlRequestContext DEBUG

See NO (only FATAL) logging:

1
$ adb shell setprop log.tag.CronetUrlRequestContext NONE

Network Log

NetLog 是 Chromium 网络模块的日志:NetLog: Chrome’s network logging system。可以使用下面两行代码操作 dump 出 chromium 网络模块的日志:

1
2
CronetEngine.startNetLogToFile()
CronetEngine.stopNetLog()

Chromium网络模块的日志输出内容是 JSON 格式的,所以需要主动调用 stopNetLog() 方法保证 JSON日志的完整性。

导出的log文件可以使用 https://netlog-viewer.appspot.com/#import 分析。如果打不开,可以使用 Chromium NetLog dump viewer 这个Chrome扩展程序。

GN语法

参考 GN Quick Start guideGN Language and Operation,建议大家先了解下target的概念,因为gn命令操作的基本上都是target。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 列出指定构建目录下所有的targets
$ gn ls out/Default

// 查看当前环境的配置参数
$ gn args --list out/Default

// 显示关于一个给定target或config的信息。
// usage: gn desc <out_dir> <label or pattern> [<what to show>] [--blame] [--format=json]
$ gn desc out/Default net

// 查找两个taregets之间的依赖路径
$ gn path out/Default //base //net --all

// 查找反向的依赖(也就是依赖此target的其他targets)
$ gn refs out/Default/ //net

// 检查头文件依赖的有效性
$ gn check out/Default/ //net

Cronet request lifecycle

The Cronet request lifecycle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ ninja -help
usage: ninja [options] [targets...]

if targets are unspecified, builds the 'default' target (see manual).

options:
--version print ninja version ("1.8.2")

-C DIR change to DIR before doing anything else
-f FILE specify input build file [default=build.ninja]

-j N run N jobs in parallel [default=6, derived from CPUs available]
-k N keep going until N jobs fail [default=1]
-l N do not start new jobs if the load average is greater than N
-n dry run (don't run commands but act like they succeeded)
-v show all command lines while building

-d MODE enable debugging (use -d list to list modes)
-t TOOL run a subtool (use -t list to list subtools)
terminates toplevel options; further flags are passed to the tool
-w FLAG adjust warnings (use -w list to list warnings)

参考资料

Powered by Hexo and Hexo-theme-hiker

Copyright © 2013 - 2019 iTimeTraveler All Rights Reserved.

访客数 : | 访问量 :