Chromium网络堆栈之Cronet详解

一、简介

Cronet 是 Chromium 网络堆栈,可作为库提供给 Android 应用。Cronet 利用多种技术来减少延迟并提高应用正常运行所需的网络请求吞吐量。

Cronet 库每天处理数百万人所用的应用(如 YouTube、Google 应用、Google 相册以及Google 地图导航和 Google 公交)的请求。

二、功能

1、协议支持

Cronet 本身支持 HTTP 协议、HTTP/2 协议和 QUIC 协议。

2、请求优先级

该库支持您为请求设置优先级标签。服务器可以使用优先级标签来确定处理请求的顺序。

3、资源缓存

Cronet 可以使用内存缓存或磁盘缓存来存储在网络请求中检索到的资源。后续请求会自动通过缓存提供。

4、异步请求

默认情况下,使用 Cronet 库发出的网络请求是异步的。您的工作器线程在等待请求返回时不会遭到屏蔽。

5、数据压缩

Cronet 支持使用 Brotli 压缩数据格式来压缩数据。

要了解如何在 Android 应用中使用 Cronet 库,请参阅下面的使用。您还可以浏览 GitHub 上的 Cronet 示例。

三、Cronet使用

此次介绍了如何使用 Cronet 库在您的 Android 应用中执行网络操作。Cronet 是 Chromium 网络堆栈,可作为库供在应用中使用。

1、在项目中设置库

要在您的项目中为 Cronet 库添加依赖项,请按以下步骤操作:

  • 在项目的顶层 build.gradle 文件中验证 Android Studio 是否包含对 Google 的 Maven 代码库的引用,如以下示例所示:
allprojects {
        repositories {
            ...
            google()
        }
    }
  • 找到应用模块的 build.gradle 文件,在 dependencies 部分中添加对 Cronet 的 Google Play 服务客户端库的引用,如以下示例所示:
dependencies {
        implementation 'com.google.android.gms:play-services-cronet:16.0.0'
    }

添加此依赖项后创建的 CronetEngine 对象将使用从 Google Play 服务加载的 Cronet。在创建 CronetEngine 对象之前,请调用 CronetProviderInstaller.installProvider(Context) 对象,以防止在 CronetEngine 创建期间因设备需要更新版 Google Play 服务等错误而抛出异常。

如果无法通过 Google Play 服务加载 Cronet,则只能使用 Cronet API 的一个性能不那么出色的实现。要使用此备用实现,请使用 org.chromium.net:cronet-fallback 并调用 new JavaCronetProvider(context).createBuilder()

2、创建网络请求

本部分介绍了如何使用 Cronet 库创建和发送网络请求。发送网络请求后,您的应用应处理网络响应。

2.1 创建并配置 CronetEngine 的实例

该库提供了 CronetEngine.Builder 类,您可以使用此类来创建 CronetEngine 的实例。以下示例展示了如何创建 CronetEngine 对象,分别用java和kotlin创建对象:

JAVA

  CronetEngine.Builder myBuilder = new CronetEngine.Builder(context);
  CronetEngine cronetEngine = myBuilder.build();

KOTLIN

val myBuilder = CronetEngine.Builder(context)
val cronetEngine: CronetEngine = myBuilder.build()

注意:建议您仅创建 CronetEngine 的一个实例。单个实例可以发送多个异步请求。此外,存储目录不支持多个 CronetEngine实例并发访问。如需了解详情,请参阅 setStoragePath()

您可以使用 Builder 类来配置 CronetEngine 对象,例如,您可以提供缓存和数据压缩等选项。如需了解详情,请参阅 CronetEngine.Builder

2.2 提供请求回调的实现

要提供回调的实现,请创建 UrlRequest.Callback 的子类,并实现所需的抽象方法,如以下示例所示:

JAVA

class MyUrlRequestCallback extends UrlRequest.Callback {
  private static final String TAG = "MyUrlRequestCallback";

  @Override
  public void onRedirectReceived(UrlRequest request, UrlResponseInfo info, String newLocationUrl) {
    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) {
    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) {
    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) {
    Log.i(TAG, "onSucceeded method called.");
  }
}

KOTLIN

private const val TAG = "MyUrlRequestCallback"

    class MyUrlRequestCallback : UrlRequest.Callback() {
        override fun onRedirectReceived(request: UrlRequest?, info: UrlResponseInfo?, newLocationUrl: String?) {
            Log.i(TAG, "onRedirectReceived method called.")
            // You should call the request.followRedirect() method to continue
            // processing the request.
            request?.followRedirect()
        }

        override fun onResponseStarted(request: UrlRequest?, info: UrlResponseInfo?) {
            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 fun onReadCompleted(request: UrlRequest?, info: UrlResponseInfo?, byteBuffer: ByteBuffer?) {
            Log.i(TAG, "onReadCompleted method called.")
            // You should keep reading the request until there's no more data.
            request?.read(ByteBuffer.allocateDirect(102400))
        }

        override fun onSucceeded(request: UrlRequest?, info: UrlResponseInfo?) {
            Log.i(TAG, "onSucceeded method called.")
        }
    }
    

注意:为简单起见,上一个代码示例创建了 UrlRequest.Callback的实现,该实现会在发生网络事件时将消息写入日志。要详细了解如何实现回调,请参阅处理网络响应。

2.3 创建一个 Executor 对象来管理网络任务

您可以使用 Executor 类执行网络任务。要获取 Executor 的实例,请使用返回 Executor 对象的 Executors 类的任一静态方法。以下示例展示了如何使用 newSingleThreadExecutor() 方法创建 Executor 对象:

JAVA

  Executor executor = Executors.newSingleThreadExecutor();

KOTLIN

val executor: Executor = Executors.newSingleThreadExecutor()
2.4 创建并配置 UrlRequest 对象

要创建网络请求,请调用 CronetEngine 的 newUrlRequestBuilder() 方法,并传递目标网址、回调类的实例以及 Executor 对象。newUrlRequestBuilder() 方法将返回 UrlRequest.Builder 对象,您可以使用该对象来创建 UrlRequest 对象,如以下示例所示:

JAVA

   UrlRequest.Builder requestBuilder = cronetEngine.newUrlRequestBuilder(
            "https://www.example.com", new MyUrlRequestCallback(), executor);

    UrlRequest request = requestBuilder.build();

KOTLIN

 val requestBuilder = cronetEngine.newUrlRequestBuilder(
            "https://www.example.com",
            MyUrlRequestCallback(),
            executor
    )

    val request: UrlRequest = requestBuilder.build()

您可以使用 Builder 类来配置 UrlRequest 的实例。例如,您可以指定优先级或 HTTP 谓词。

要启动网络任务,请调用请求的 start() 方法:

JAVA

  request.start();

KOTLIN

  request.start()

您可以按本部分中的说明使用 Cronet 来创建并发送网络请求。不过,为简单起见,UrlRequest.Callback 的实现示例仅向日志输出消息。以下部分介绍了如何提供支持更多实用场景(例如从响应中提取数据以及检测请求中的故障)的回调实现。

2.5 处理网络响应

您调用 start() 方法后,系统就会启动 Cronet 请求生命周期。您的应用应在生命周期内通过指定回调来管理请求。要详细了解生命周期,请参阅 Cronet 请求生命周期。您可以通过创建 UrlRequest.Callback 的子类并实现以下方法来指定回调:

  • onRedirectReceived()
    在服务器发出 HTTP 重定向代码以响应原始请求时调用。要遵循重定向到达新的目的地,请使用 followRedirect() 方法。否则,请使用 cancel() 方法。以下示例展示了如何实现该方法:

JAVA

  @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();
      }
    }
    

KOTLIN

 override fun onRedirectReceived(request: UrlRequest?, info: UrlResponseInfo?, newLocationUrl: String?) {
      // Determine whether you want to follow the redirect.
      ...

      if (shouldFollow) {
          request?.followRedirect()
      } else {
          request?.cancel()
      }
    }
  • onResponseStarted()
    在收到最后一组标头时调用。仅在遵循所有重定向后才调用 onResponseStarted() 方法。以下代码展示了该方法的实现示例:

JAVA

 @Override
    public void onResponseStarted(UrlRequest request, UrlResponseInfo info) {
      int httpStatusCode = info.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);
      }
      responseHeaders = info.getAllHeaders();
    }
    

KOTLIN

override fun onResponseStarted(request: UrlRequest?, info: UrlResponseInfo?) {
      val httpStatusCode = info?.httpStatusCode
      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)
      }
      responseHeaders = info?.allHeaders
    }

注意:Cronet 不会将状态代码 4xx 和 5xx 视为错误。您仍应尝试使用 read()方法读取响应,因为其中可能包含某些数据。您也可以使用 cancel() 方法取消请求。这些操作可确保请求进入最终状态。

  • onReadCompleted()
    在读取部分响应正文后调用。以下代码示例展示了如何实现该方法并提取响应正文:

JAVA

  @Override
    public void onReadCompleted(UrlRequest request, UrlResponseInfo info, 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);
    }

KOTLIN

override fun onReadCompleted(request: UrlRequest?, info: UrlResponseInfo?, 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()
    在网络请求成功完成后调用。以下示例展示了如何实现该方法:

JAVA

@Override
public void onSucceeded(UrlRequest request, UrlResponseInfo info) {
  // The request has completed successfully.
}

KOTLIN

 override fun onSucceeded(request: UrlRequest?, info: UrlResponseInfo?) {
        // The request has completed successfully.
    }
  • onFailed()
    在调用 start() 方法后,如果由于任何原因而导致请求失败,则调用此方法。以下示例展示了如何实现该方法并获取有关错误的信息:

JAVA

  @Override
    public void onFailed(UrlRequest request, UrlResponseInfo info, CronetException error) {
      // The request has failed. If possible, handle the error.
      Log.e(TAG, "The request failed.", error);
    }

KOTLIN

override fun onFailed(request: UrlRequest?, info: UrlResponseInfo?, error: CronetException?) {
        // The request has failed. If possible, handle the error.
        Log.e(TAG, "The request failed.", error)
    }
    
  • onCanceled()
    如果使用 cancel() 方法取消该请求,则调用该方法。调用该方法之后,就不会再调用 UrlRequest.Callback 类的其他方法。您可以使用此方法释放为处理请求而分配的资源。以下示例展示了如何实现该方法:

JAVA

@Override
public void onCanceled(UrlRequest request, UrlResponseInfo info) {
  // Free resources allocated to process this request.}

KOTLIN

  override fun onCanceled(request: UrlRequest?, info: UrlResponseInfo?) {
        // Free resources allocated to process this request.
        ...
    }

四、Cronet 请求生命周期

本部分介绍了使用 Cronet 库创建的请求的生命周期,以及如何使用该库提供的回调方法管理此类请求。

使用 Cronet 库创建的网络请求由 UrlRequest 类表示。以下概念对于理解 UrlRequest 生命周期至关重要:

  • 状态

状态是请求在特定时间所处的特定状况。使用 Cronet 库创建的 UrlRequest对象在其生命周期中会经历不同的状态。请求生命周期包括初始状态以及多个过渡状态和最终状态。

  • UrlRequest 方法

客户端可以根据 UrlRequest 对象的状态对其调用特定的方法。方法会将请求从一种状态转换为另一种状态。

  • Callback 方法

通过实现 UrlRequest.Callback 类的方法,您的应用可以接收有关请求进度的动态更新。您可以实现多种回调方法,从而调用
UrlRequest 对象的方法,将生命周期从一种状态转换为另一种状态。

以下列表介绍了 UrlRequest 生命周期的流程:

  1. 应用调用 start() 方法后,生命周期处于已开始状态。
  2. 服务器可能会发送重定向响应,该响应会将流程引导至 onRedirectReceived() 方法。在此方法中,您可以执行以下任一客户端操作:
  • 使用 followRedirect() 跟踪重定向。此方法会将请求恢复为已开始状态。
  • 使用 cancel() 取消请求。此方法会将请求引导至 onCanceled() 方法,借助该方法,应用可以在请求转换为已取消这一最终状态之前执行其他操作。在这里插入代码片
  1. 应用跟踪所有重定向后,服务器会发送响应标头,还会调用 onResponseStarted() 方法。请求处于 Waiting for read() 状态。应用应该会调用 read() 方法来尝试读取部分响应正文。调用 read() 之后,请求处于正在读取状态,并可能出现以下结果:
  • 读取操作已成功完成,但还存在更多可用数据。已调用 onReadCompleted(),且请求再次处于 Waiting for read() 状态。应用应再次调用 read() 方法,以继续读取响应正文。应用还可以使用 cancel() 方法停止读取请求。
  • 读取操作已成功完成,没有其他可用数据。已调用 onSucceeded() 方法,请求现在处于成功这一最终状态。
  • 读取操作失败。已调用 onFailed 方法,请求的最终状态为失败。

下图展示了 UrlRequest 对象的生命周期:
Chromium网络堆栈之Cronet详解_第1张图片Cronet 请求生命周期

参考链接https://developer.android.google.cn/guide/topics/connectivity/cronet

你可能感兴趣的:(网络)