前言
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
|
参考