Chromium内核原理之cronet独立化
《Chromium内核原理之blink内核工作解密》
《Chromium内核原理之多进程架构》
《Chromium内核原理之进程间通信(IPC)》
《Chromium内核原理之网络栈》
《Chromium内核原理之网络栈HTTP Cache》
《Chromium内核原理之Preconnect》
《Chromium内核原理之Prerender》
《Chromium内核原理之cronet独立化》
1.Cronet介绍
2.Cronet编译
3.Cronet使用3.1 使用Cronet发送request
3.2 Cronet request 生命周期4.Cronet源码架构分析
1.Cronet介绍
Cronet是作为库提供给Android应用程序的Chromium网络堆栈。 Cronet利用多种技术来减少延迟并提高应用程序需要工作的网络请求的吞吐量。
Cronet Library每天处理数百万人使用的应用程序请求,例如YouTube,Google App,Google Photos和Maps - Navigation&Transit。
Cronet具有以下特点:
- 1.Protocol support
Cronet本身支持HTTP,HTTP / 2和QUIC协议。 - 2.Request prioritization
该库允许您为请求设置优先级标记。服务器可以使用优先级标记来确定处理请求的顺序。 - 3.Resource caching
Cronet可以使用内存或磁盘缓存来存储在网络请求中检索到的资源。后续请求将自动从缓存中提供。 - 4.Asynchronous requests
默认情况下,使用Cronet Library发出的网络请求是异步的。在等待请求返回时,不会阻止您的工作线程。 - 5.Data compression
Cronet使用Brotli压缩数据格式支持数据压缩。
2.Cronet编译
- 首先下载完整的chromium内核的源码:https://www.chromium.org/developers/how-tos/get-the-code
- 构建Cronet以进行开发和调试:
为了开发和调试目的而构建Cronet:首先,gn用于创建针对目标平台的忍者文件,然后执行ninja文件来运行构建。 - Android / iOS builds
$ ./components/cronet/tools/cr_cronet.py gn --out_dir=out/Cronet
如果构建主机是Linux,则将构建Android二进制文件。如果构建主机是macOS,则将构建iOS二进制文件。
注意:这些命令在out / Cronet中删除先前执行的gn命令的输出。如果省略--out_dir,则输出目录默认为out / Debug用于调试版本,out / Release用于版本构建(见下文)。
如果指定了--x86选项,则为Intel x86体系结构构建本机库,如果未指定,则输出目录默认为out / Debug-x86。这对于在移动模拟器上运行非常有用。 - Desktop builds (targets the current OS)
gn gen out/Cronet
$ ninja -C out/Cronet cronet_package
- 为发布构建Cronet mobile
$ ./components/cronet/tools/cr_cronet.py gn --release
$ ninja -C out/Release cronet_package
- 为其他架构构建
默认情况下,会生成ARMv7 32位可执行文件。要生成针对其他体系结构的可执行文件,请修改cr_cronet.py的gn_args变量以包含:
For ARMv8 64-bit: target_cpu="arm64"
For x86 32-bit: target_cpu="x86"
For x86 64-bit: target_cpu="x64"
3.Cronet使用
3.1 使用Cronet发送request
如果要使用Cronet框架,需要按照下面的步骤来实现其功能:
-
1.创建和配置CronetEngine的实例;
该库提供了一个CronetEngine.Builder类,您可以使用它来创建CronetEngine的实例。以下示例显示如何创建CronetEngine对象:
CronetEngine.Builder myBuilder = new CronetEngine.Builder(context);
CronetEngine cronetEngine = myBuilder.build();
注意:
建议只创建一个CronetEngine实例。单个实例可以发送多个异步请求。此外,存储目录不支持多个CronetEngine实例的并发访问。有关更多信息,请参阅setStoragePath()。
您可以使用Builder类来配置CronetEngine对象,例如,您可以提供缓存和数据压缩等选项。有关更多信息,请参阅CronetEngine.Builder。
-
2.提供request回调的实现;
要提供回调的实现,请创建UrlRequest.Callback的子类并实现所需的抽象方法,如以下示例所示:
class MyUrlRequestCallback extends UrlRequest.Callback {
private static final String TAG = "MyUrlRequestCallback";
@Override
public void onRedirectReceived(UrlRequest request, UrlResponseInfo info, String newLocationUrl) {
android.util.Log.i(TAG, "onRedirectReceived method called.");
// You should call the request.followRedirect() method to continue
// processing the request.
request.followRedirect();
}
@Override
public void onResponseStarted(UrlRequest request, UrlResponseInfo info) {
android.util.Log.i(TAG, "onResponseStarted method called.");
// You should call the request.read() method before the request can be
// further processed. The following instruction provides a ByteBuffer object
// with a capacity of 102400 bytes to the read() method.
request.read(ByteBuffer.allocateDirect(102400));
}
@Override
public void onReadCompleted(UrlRequest request, UrlResponseInfo info, ByteBuffer byteBuffer) {
android.util.Log.i(TAG, "onReadCompleted method called.");
// You should keep reading the request until there's no more data.
request.read(ByteBuffer.allocateDirect(102400));
}
@Override
public void onSucceeded(UrlRequest request, UrlResponseInfo info) {
android.util.Log.i(TAG, "onSucceeded method called.");
}
}
-
3.创建Executor对象来管理网络请求任务
您可以使用Executor类来执行网络任务。要获取Executor的实例,请使用Executors类的一个返回Executor对象的静态方法。以下示例说明如何使用newSingleThreadExecutor()方法创建Executor对象:
Executor executor = Executors.newSingleThreadExecutor();
-
4.创建和配置UrlRequest对象
要创建网络请求,请调用CronetEngine的newUrlRequestBuilder()方法,该方法传递目标URL,回调类的实例和执行程序对象。 newUrlRequestBuilder()方法返回一个UrlRequest.Builder对象,您可以使用该对象创建UrlRequest对象,如以下示例所示:
UrlRequest.Builder requestBuilder = cronetEngine.newUrlRequestBuilder(
"https://www.example.com", new MyUrlRequestCallback(), executor);
UrlRequest request = requestBuilder.build();
您可以使用Builder类来配置UrlRequest的实例。例如,您可以指定优先级或HTTP谓词。有关更多信息,请参阅UrlRequest.Builder。
要启动网络任务,请调用请求的start()方法:
request.start();
按照本节中的说明,您可以使用Cronet创建和发送网络请求。但是,为简单起见,UrlRequest.Callback的示例实现仅将消息输出到日志。以下部分显示如何提供支持更多有用方案的回调实现,例如从响应中提取数据和检测请求中的故障。
-
5.处理response
调用start()方法后,将启动Cronet请求生命周期。您的应用应通过指定回调来管理生命周期中的请求。要了解有关生命周期的更多信息,请参阅Cronet请求生命周期。您可以通过创建UrlRequest.Callback的子类并实现以下方法来指定回调:
处理response,有几种非常重要的回调函数:
onRedirectReceived()
、onResponseStarted()
、onReadCompleted()
、onSucceeded()
、onFailed()
、onCanceled()
onRedirectReceived()
:
当服务器发出HTTP重定向代码以响应原始请求时调用。要跟随重定向到新目标,请使用followRedirect()方法。否则使用cancel()方法。以下示例显示了如何实现该方法:
@Override
public void onRedirectReceived(UrlRequest request, UrlResponseInfo info, String newLocationUrl) {
// Determine whether you want to follow the redirect.
…
if (shouldFollow) {
request.followRedirect();
} else {
request.cancel();
}
}
onResponseStarted()
:
收到最后一组标题时调用。只有在遵循所有重定向后才会调用onResponseStarted()方法。以下代码显示了该方法的示例实现:
@Override
public void onResponseStarted(UrlRequest request, UrlResponseInfo responseInfo) {
int httpStatusCode = responseInfo.getHttpStatusCode();
if (httpStatusCode == 200) {
// The request was fulfilled. Start reading the response.
request.read(myBuffer);
} else if (httpStatusCode == 503) {
// The service is unavailable. You should still check if the request
// contains some data.
request.read(myBuffer);
}
mResponseHeaders = responseInfo.getAllHeaders();
}
从Cronet的角度来看,状态代码4xx和5xx不被视为错误。您仍应尝试使用read()方法读取响应,因为它可能包含一些数据。您也可以使用cancel()方法取消请求。这些操作可确保请求进入最终状态。
onReadCompleted()
:
只要读取了部分响应主体,就会调用它。以下代码示例演示如何实现该方法并提取响应正文:
@Override
public void onReadCompleted(UrlRequest request, UrlResponseInfo responseInfo, ByteBuffer byteBuffer) {
// The response body is available, process byteBuffer.
…
// Continue reading the response body by reusing the same buffer
// until the response has been completed.
byteBuffer.clear();
request.read(myBuffer);
}
onSucceeded()
:
网络请求成功完成时调用。以下示例显示了如何实现该方法:
@Override
public void onSucceeded(UrlRequest request, UrlResponseInfo responseInfo) {
// The request has completed successfully.
}
onFailed()
:
调用start()方法后,如果请求因任何原因失败,则调用此方法。以下示例显示如何实现该方法并获取有关错误的信息:
@Override
public void onFailed(UrlRequest request, UrlResponseInfo responseInfo, CronetException error) {
// The request has failed. If possible, handle the error.
Log.e(TAG, "The request failed.", error);
}
onCanceled()
:
如果使用cancel()方法取消请求,则调用此方法。调用后,不会调用UrlRequest.Callback类的其他方法。您可以使用此方法释放分配用于处理请求的资源。以下示例显示了如何实现该方法:
@Override
public void onCanceled(UrlRequest request, UrlResponseInfo responseInfo) {
// Free resources allocated to process this request.
…
}
3.2 Cronet request 生命周期
使用Cronet Library创建的网络请求由UrlRequest类表示。以下概念对于理解UrlRequest生命周期非常重要:
States:
状态是请求在特定时间处于的特定条件。使用Cronet Library创建的UrlRequest对象在其生命周期中移动通过不同的状态。请求生命周期包括初始状态,以及多个过渡状态和最终状态。
UrlRequest methods:
客户端可以根据状态调用UrlRequest对象上的特定方法。这些方法将请求从一个状态移动到另一个状态。
Callback methods:
通过实现UrlRequest.Callback类的方法,您的应用可以接收有关请求进度的更新。您可以实现回调方法来调用UrlRequest对象的方法,该方法将生命周期从一个状态转移到另一个状态。
下面描述了UrlRequest的生命周期:
- 1.应用程序调用start()方法后,生命周期处于“已启动”状态。
- 2.服务器可以发送重定向响应,该响应将流转移到onRedirectReceived()方法。在此方法中,您可以执行以下客户端操作之一:
- 使用followRedirect()跟随重定向。此方法将请求恢复为已启动状态。
- 使用cancel()取消请求。此方法将请求带到onCanceled()方法,其中应用程序可以在请求移动到Canceled final状态之前执行其他操作。
- 3.应用程序跟随所有重定向后,服务器发送响应头并调用onResponseStarted()方法。请求处于Waiting for read()状态。应用程序应该调用read()方法来尝试读取部分响应主体。调用read()后,请求处于读取状态,其中有以下可能的结果:
- 读取response是成功的,但有更多的数据可用。调用onReadCompleted()并且请求再次处于Waiting for read()状态。应用程序应再次调用read()方法以继续读取响应正文。应用程序也可以使用cancel()方法停止读取请求。
- 读取response是成功的,没有更多的数据可用。调用onSucceeded()方法,请求现在处于Succeeded最终状态。
读取response失败了。调用onFailed方法,请求的最终状态现在为Failed。