前言
Cronet 是 Chromium 浏览器中用于网络请求的库,支持 HTTP1/2 、 SPDY 、 QUIC 等协议。并且还支持移动端。
需要注意的是编译 Android 需要在 Linux 下进行,并且只支持 Ubuntu 和 Debian 的发行版,其它的不支持。
下载
由于 Cronet 是 Chromium 的一部分,所以我们需要下载 Chromium 的源码。
Chromium 是谷歌开源的项目,所以下载 Chromium 需要用到谷歌提供的工具 depot_tools 来进行下载
1
2
|
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
export PATH="$PATH:$(pwd)/depot_tools" # 设置环境变量,可以写到 .bashrc 中,这样就不用每次都执行一遍
|
如果到了这一步没有出现问题,就可以进行下一步,如果下载不下来,需要配置 git 代理
depot_tools 下载好了,就可以下载 Chromium 的源码了,这就要用到 depot_tools 中的 fetch 工具了
1
2
|
mkdir ~/chromium && cd ~/chromium
fetch --nohooks --no-history android
|
--no-history 可以忽略历史,加快下载
--nohooks 会在 fetch 结束后帮你执行下载一些依赖的二进制。
这个过程比较漫长,取决于网速,网速快大概一个小时可以搞定,慢一点有可能会失败。
失败了可以看看 chromium 文件夹中有没有 .gclient ,如果有可以使用 gclient sync 继续下载,如果没有那就需要重新 fetch 。
代码下载好之后需要安装一些额外的依赖,谷歌也提供了工具,我们只需要直接执行就可以
1
2
|
cd ~/chromium/src
./build/install-build-deps-android.sh
|
编译
Debug 版本
1
2
3
|
cd ~/chromium/src
./components/cronet/tools/cr_cronet.py gn --out_dir=out/Cronet
ninja -C out/Cronet cronet_package
|
编译一遍大概在30分钟的样子,还不算很久
Release 版本
1
2
3
|
cd ~/chromium/src
./components/cronet/tools/cr_cronet.py gn --release
ninja -C out/Release cronet_package
|
指定编译的CPU架构
在进行编译的时候默认是 ARMv7 32位,如果你需要指定编译的CPU架构,需要在 chromium/src/out/Cronet/args.gn 中设置CPU的架构
通常有以下几个值
- ARMv8 64位:
target_cpu="arm64"
- ARMv7 64位:
target_cpu="arm"
- x86 64位:
target_cpu="x64"
- x86 32位:
target_cpu="x32"
使用
编译之后所有的 jar 在 out/Cronet/cronet/ 目录下, so 在 out/Cronet/cronet/libs 目录下
新建一个 Android 项目,把 cronet_api.jar 、 cronet_impl_common_java.jar 、 cronet_impl_native_java.jar 、 cronet_impl_platform_java.jar 拷贝到 app/libs 下面
然后需要把 armeabi-v7a/libcronet.95.0.4638.50.so 拷贝到 src/main/jniLibs 接下来就可以开始用 cronet 进行网络请求了
在 MainActivity 中加入如下代码
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
|
private ByteArrayOutputStream mBytesReceived = new ByteArrayOutputStream();
private WritableByteChannel mReceiveChannel = Channels.newChannel(mBytesReceived);
public void cronetRequest() {
ExecutorService executorService = Executors.newFixedThreadPool(4);
CronetEngine engine = new CronetEngine.Builder(this)
.setStoragePath(context.getFilesDir().getAbsolutePath())
.enableHttpCache(CronetEngine.Builder.HTTP_CACHE_DISK_NO_HTTP, 100 * 1024)
.enableHttp2(true)
.enableQuic(true)
.enableBrotli(true)
.build();
engine.newUrlRequestBuilder("https://httpbin.org/get", new UrlRequest.Callback() {
@Override
public void onRedirectReceived(
UrlRequest request, UrlResponseInfo info, String newLocationUrl) {
Log.d(TAG, "onRedirectReceived");
request.followRedirect();
}
@Override
public void onResponseStarted(UrlRequest request, UrlResponseInfo info) {
Log.i(TAG, "Response Started");
request.read(ByteBuffer.allocateDirect(64 * 1024));
}
@Override
public void onReadCompleted(UrlRequest request, UrlResponseInfo info, ByteBuffer byteBuffer) {
byteBuffer.flip();
Log.i(TAG, "onReadCompleted");
try {
mReceiveChannel.write(byteBuffer);
} catch (IOException e) {
Log.i(TAG, "IOException during ByteBuffer read. Details: ", e);
}
byteBuffer.clear();
request.read(byteBuffer);
}
@Override
public void onSucceeded(UrlRequest request, UrlResponseInfo info) {
Log.i(TAG, "Request Completed, status code is " + info.getHttpStatusCode()
+ ", total received bytes is " + info.getReceivedByteCount());
Log.d(TAG, mBytesReceived.toString());
}
@Override
public void onFailed(UrlRequest request, UrlResponseInfo info, CronetException error) {
Log.i(TAG, "****** onFailed, error is: " + error.getMessage());
if (mHttpCallback != null)
mHttpCallback.onResponse(new HttpRequestAdapter(request),
info.getHttpStatusCode(), new HttpResponseAdaptor(info, null));
}
}, executorService).build().start();
}
|
运行之后就可以在控制台看到输出结果了
如果你碰到 java.lang.UnstatisfiedLinkError: dlopen failed: library "libcronet.95.0.4638.50.so" not found 需要在 app/build.gradle 中加入如下配置
1
2
3
4
5
6
7
|
android {
defaultConfig {
ndk {
abiFilters 'armeabi-v7a'
}
}
}
|
切换到指定的tag
由于 chromium 的代码随时都可能更新,所以我们希望可以在一个稳定的tag上进行开发,这就需要把源码切换到指定的tag上,并且需要把 depot_tools 也切换到匹配的版本上。
切换到指定的tag上
假设我们需要切换到 95.0.4638.50
1
2
3
|
cd ~/chromium/src
git fetch origin 95.0.4638.50
git checkout 95.0.4638.50
|
chromium 切换之后,我们需要把 depot_tools 也切换到对应的版本上,可以通过如下命令
1
2
3
4
|
cd ~/chromium/src
COMMIT_DATE=$(git log -n 1 --pretty=format:%ci)
cd ~/depot_tools
git checkout $(git rev-list -n 1 --before="$COMMIT_DATE" main)
|
同步依赖
depot_tools 切换好之后,需要把依赖也同步到对应的版本。
由于 gclient 会自动更新 depot_tools ,所以在同步依赖之前需要禁用 depot_tools 的自动更新,然后再用 gclient 进行同步。
1
2
3
4
|
export DEPOT_TOOLS_UPDATE=0 # 禁用 depot_tools 自动更新
cd ~/chromium/src
git clean -ffd # 清空git工作目录,以免发生冲突
gclient sync -D --force --reset --with_branch_heads
|
碰到的错误
FAILED: gen/components/cronet/android/generate_javadoc.zip
1
2
3
4
5
6
7
8
9
|
FAILED: gen/components/cronet/android/generate_javadoc.zip
python3 ../../components/cronet/tools/generate_javadoc.py --output-dir cronet
--input-dir ../../components/cronet --overview-file cronet/README.html
--readme-file ../../components/cronet/README.md
--depfile gen/components/cronet/android/generate_javadoc.d
--zip-file gen/components/cronet/android/generate_javadoc.zip
--android-sdk-jar ../../third_party/android_sdk/public/platforms/android-31/android.jar
--support-annotations-jar lib.java/third_party/androidx/androidx_annotation_annotation.jar
--input-src-jar cronet/cronet_api-src.jar
|
该问题是没有安装 JDK 导致的,所以只需要安装一下 JDK 就可以解决了
1
|
sudo apt install openjdk-8-jdk
|
参考