ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

ExoPlayer Cronet 扩展:基于 Chromium 网络栈的高性能 HttpDataSource 接入指南

ExoPlayer Cronet 扩展:基于 Chromium 网络栈的高性能 HttpDataSource 接入指南 ExoPlayer Cronet 扩展基于 Chromium 网络栈的高性能 HttpDataSource 接入指南【免费下载链接】ExoPlayerThis project is deprecated and stale. The latest ExoPlayer code is available in https://github.com/androidx/media项目地址: https://gitcode.com/gh_mirrors/ex/ExoPlayer本指南基于 ExoPlayer 仓库中的 extensions/cronet/README.md 及对应源码系统讲解 Cronet 扩展模块的定位、Gradle 接入方式、CronetDataSource的使用方法、三种 Cronet 实现Google Play Services / Embedded / Fallback的取舍以及CronetEngine的实例化策略。读完本文你将能够在自己的 ExoPlayer 播放器应用中无缝替换默认网络栈利用 HTTP/2、HTTP/3 over QUIC 等技术降低网络请求延迟并在缺少 Google Play Services 的设备上给出正确的降级方案。模块定位为播放器换上 Cronet 网络栈extension-cronet模块为 ExoPlayer 提供了一个基于Cronet的 HttpDataSource 实现即CronetDataSource。Cronet 是 Chromium 网络栈以 Android 库形式开放给应用使用的产物它原生支持 HTTP、HTTP/2 以及基于 QUIC 的 HTTP/3 协议通过多种技术降低网络请求延迟、提升吞吐量。官方文档指出Cronet 被包括 YouTube 在内的全球头部流媒体应用所采用也是 ExoPlayer 官方推荐的网络栈。在仓库源码中CronetDataSource的类声明位于 CronetDataSource.java它继承自BaseDataSource并实现HttpDataSource接口open()、read()、close()等方法均遵循 ExoPlayer 数据源的标准契约。需要特别说明的是Cronet 只处理 http(s) 协议的内容。如果应用还需要播放本地文件等其他非 http(s) 内容需要将 Cronet 扩展与其他数据源组合使用见下文快速接入一节。获取模块Gradle 依赖与本地构建方式一添加 Maven 依赖在应用模块的build.gradle中加入implementation com.google.android.exoplayer:extension-cronet:2.X.X其中2.X.X必须替换为具体的版本号且必须与其他媒体模块如exoplayer-core的版本保持一致。从当前仓库的 build.gradle 可以看到该模块内部依赖library-common与library-datasource对外发布的 artifactId 为extension-cronet并默认引入api com.google.android.gms:play-services-cronet:18.0.1即 Google Play Services 版本的 Cronet 实现。方式二克隆仓库本地依赖也可以克隆本仓库后在settings.gradle中引入extensions/cronet模块进行本地依赖具体步骤参见顶层 README。快速接入替换 DataSource.FactoryExoPlayer 的媒体组件通过DataSource实例请求数据而这些实例由应用代码实例化并注入的DataSource.Factory产生。因此接入 Cronet 扩展的核心动作就是把应用代码中的DataSource.Factory替换为CronetDataSource.Factory。仅播放 http(s) 内容如果应用只需要播放 http(s) 内容直接使用new CronetDataSource.Factory(cronetEngine, executor)需要同时播放本地文件等非 http(s) 内容使用DefaultDataSource.Factory将 Cronet 作为基础数据源工厂传入DefaultDataSource本身处理 file、asset、content 等 scheme遇到 http(s) 时才委托给基础工厂new DefaultDataSource.Factory( context, /* baseDataSourceFactory */ new CronetDataSource.Factory(cronetEngine, executor));Factory 构造参数说明CronetDataSource.Factory的构造方法接收两个参数见 CronetDataSource.javacronetEngine执行请求的CronetEngine不应传入从 Fallback 实现JavaCronetProvider获得的实例此时直接使用DefaultHttpDataSource效率更高executor处理响应的Executor。可以传入直接执行器direct executor以避免 Cronet 内部网络线程到响应处理线程之间的线程切换开销但此时必须保证响应处理逻辑足够轻量否则会拖慢整体网络性能。可链式调用的配置项CronetDataSource.Factory提供了丰富的链式配置方法均返回this见 CronetDataSource.java方法作用默认值setUserAgent(String)设置请求 User-Agent传null使用底层CronetEngine的默认 UAnullsetRequestPriority(int)请求优先级取值应为UrlRequest.Builder.REQUEST_PRIORITY_*常量REQUEST_PRIORITY_MEDIUMsetConnectionTimeoutMs(int)连接超时毫秒DEFAULT_CONNECT_TIMEOUT_MILLIS 8000mssetReadTimeoutMs(int)读取超时毫秒DEFAULT_READ_TIMEOUT_MILLIS 8000mssetResetTimeoutOnRedirects(boolean)重定向时是否重置连接超时falsesetHandleSetCookieRequests(boolean)重定向时是否将 Set-Cookie 响应头以 Cookie 请求头转发到重定向地址falsesetKeepPostFor302Redirects(boolean)对 POST 请求的 302 重定向是否保持 POST 方法与请求体falsesetContentTypePredicate(PredicateString)内容类型校验谓词被拒绝时在open()抛出InvalidContentTypeExceptionnullsetTransferListener(TransferListener)传输监听器nullsetDefaultRequestProperties(Map)默认请求头空两个超时常量定义在 CronetDataSource.java均为 8 秒。注意较旧的CronetDataSourceFactory基于CronetEngineWrapper类已被标记为Deprecated应改用CronetDataSource.Factory。选择 Cronet 实现三种方案的取舍要实例化CronetDataSource.Factory你需要一个CronetEngine而CronetEngine可以从多种 Cronet 实现中获取。官方建议应用只维护一个CronetEngine实例。Google Play Services默认方案模块默认依赖com.google.android.gms:play-services-cronet从 Google Play Services 中加载 Cronet 实现。优点应用体积增加可忽略不计实现由 Google Play Services 自动更新无需随应用发版。缺点在没有 Google Play Services 的设备上不可用。除非应用同时集成下文的其他替代实现否则此类设备上将无法创建CronetEngine。应用代码应在此场景下回退到DefaultHttpDataSource。Cronet Embedded内置方案Cronet Embedded 将完整的 Cronet 实现直接打包进应用。使用时额外添加依赖implementation org.chromium.net:cronet-embedded代价是应用体积大约增加 8MB因此不推荐大多数应用使用。以下场景可以考虑大量用户位于 Google Play Services 覆盖不到的市场你需要精确控制所用 Cronet 实现的版本。从仓库的 build.gradle 可见仪器化测试即依赖org.chromium.net:cronet-embedded:108.5359.79仅为测试构建以保证测试环境中存在应用打包的 Cronet 实现。Cronet Fallback回退方案还存在一个基于 Android 默认网络栈的 Fallback 实现通过添加依赖使用implementation org.chromium.net:cronet-fallback该实现不应与CronetDataSource配合使用因为此时直接用DefaultHttpDataSource更加高效。如果应用出于其他联网需求使用了 Cronet Fallback应采用下文更高级的CronetEngine实例化方式以便在得知CronetEngine来自 Fallback 实现时为播放器改用DefaultHttpDataSource而避免使用CronetDataSource。实例化 CronetEngineProvider 探测与择优最简单方式如果应用只使用 Google Play Services 版本的 Cronet可按 Cronet 官方发送简单请求指南直接构建CronetEngine。多实现共存的高级方式当应用可能包含多种 Cronet 实现时可以使用CronetProvider.getAllProviders枚举当前设备上可用的实现并通过名称区分它们CronetProviderInstaller.PROVIDER_NAMEGoogle Play Services 实现CronetProvider.PROVIDER_NAME_APP_PACKAGED应用内置Embedded实现CronetProvider.PROVIDER_NAME_FALLBACKFallback 实现。据此可以按自定义的优先级顺序遍历 provider逐个调用CronetProvider.createBuilder()尝试构建CronetEngine直到成功为止。这种方式还能让你感知CronetEngine是否来自 Fallback 实现从而在播放器路径上避开CronetDataSource同时继续在其他网络请求中使用 Cronet Fallback。仓库提供的工具方法仓库还提供了工具类 CronetUtil.javabuildCronetEngine(Context)与buildCronetEngine(Context, String userAgent, boolean preferGooglePlayServices)会过滤掉被禁用的 provider 与 Fallback provider同样基于Fallback 下用DefaultHttpDataSource更高效的考量剩余 provider 按优先级排序后逐个尝试构建应用内置实现优先级最高得分 1Google Play Services 其次默认得分 2设置preferGooglePlayServicestrue时提升为 0其他实现最低得分 3同优先级下比较版本号取较新者见 CronetUtil.java构建失败时会记录告警日志SecurityException提示检查android.permission.ACCESS_NETWORK_STATE权限UnsatisfiedLinkError提示检查应用是否打包了 Cronet 原生库见 CronetUtil.java全部失败时返回null调用方应据此回退到DefaultHttpDataSource。CronetUtil与CronetEngineWrapper目前均标记为Deprecated官方推荐直接实例化CronetEngine并传入CronetDataSource.Factory而不是使用包装类。若你的应用已为其他联网功能创建了CronetEngine直接复用它即可无需再调用这些工具方法。源码级原理CronetDataSource 的工作机制请求构建与请求头优先级buildRequestBuilder(DataSpec)见 CronetDataSource.java负责将DataSpec转换为 Cronet 的UrlRequest请求头按优先级从低到高合并defaultRequestProperties工厂默认属性→setRequestProperty设置的属性 →dataSpec.httpRequestHeaders后者覆盖前者类注释同样说明了这一顺序见 CronetDataSource.java根据DataSpec.position与length自动构造Range请求头实现范围请求指定了userAgent时追加User-Agent请求头POST 请求体通过 ByteArrayUploadDataProvider.java基于 CronetUploadDataProvider以字节数组形式上传带非空请求体的 HTTP 请求必须显式设置Content-Type头否则抛出异常见 CronetDataSource.java。打开与读取流程open(DataSpec)CronetDataSource.java启动UrlRequest并阻塞等待连接建立期间通过ConditionVariable与回调协作超时则抛出OpenException并附带cronetConnectionStatus基于UrlRequest.Status便于诊断。连接建立后依次处理非 2xx 状态码含 416 Range 越界特判、contentTypePredicate内容类型校验、服务器不支持 Range 时的手动跳过逻辑、压缩响应Content-Encoding非 identity的长度处理。读取路径使用 32KB 的 directByteBufferREAD_BUFFER_SIZE_BYTES见 CronetDataSource.java并额外提供面向 directByteBuffer的read(ByteBuffer)重载。重定向与回调处理内部UrlRequestCallbackCronetDataSource.java实现异步回调POST 请求遇到 307/308 重定向按行业标准直接报错不跟随302 时可根据keepPostFor302Redirects决定是否保持 POST启用handleSetCookieRequests时会将重定向响应中的Set-Cookie解析并以Cookie头附加到新请求onFailed中会将ERROR_HOSTNAME_NOT_RESOLVED归一化为UnknownHostException便于上层统一处理。测试与验证模块如何被验证该模块的测试覆盖相当完整可作为接入时的参考CronetDataSourceContractTest.java仪器化契约测试继承DataSourceContractTest通过CronetUtil.buildCronetEngine构建真实引擎在本地 HTTP 测试环境下验证CronetDataSource满足 ExoPlayer 数据源的全部契约CronetDataSourceTest.java单元测试使用 Mockito 模拟CronetEngine、UrlRequest等覆盖请求头合并、超时、重定向、错误映射等分支逻辑。注意事项与迁移说明降级策略必须实现在无 Google Play Services 且未集成 Embedded 的设备上CronetEngine无法创建。务必检测该情况并回退到DefaultHttpDataSource否则播放会失败。权限与原生库确保应用声明ACCESS_NETWORK_STATE权限使用 Embedded 实现时确认原生库已正确打包避免UnsatisfiedLinkError。单一引擎原则全应用只应持有一个CronetEngine实例复用已有引擎可避免额外资源开销。版本一致性extension-cronet的版本必须与项目中其他 ExoPlayer 模块完全一致。项目状态当前仓库中的com.google.android.exoplayer2代码已整体标记为Deprecated官方建议迁移到 androidx.media3其中包含相同的 ExoPlayer 代码并提供迁移脚本辅助CronetDataSource类注释中同样给出了这一说明见 CronetDataSource.java。新项目可直接在 media3 中查找对应的 Cronet 扩展进行接入。【免费下载链接】ExoPlayerThis project is deprecated and stale. The latest ExoPlayer code is available in https://github.com/androidx/media项目地址: https://gitcode.com/gh_mirrors/ex/ExoPlayer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表