TV Input 如何与电子节目指南(EPG)进行数据同步?

解读

国内面试问“TV Input 如何与 EPG 同步”,并不是想听你背一遍 TvInputManager 的类名,而是考察三件事:

  1. 是否知道 Android TV 的“系统级数据管道”——TvProvider 内容库;
  2. 是否理解国内广电/OTT 双轨场景下,SI 表、HLS 标签、第三方 CMS 三种数据源的差异与合规要求;
  3. 能否把“数据抓取→解析→事务写入→UI 刷新→省电”整条链路讲成闭环,并给出性能与合规兜底方案。

一句话:让面试官相信你做过真·电视项目,而不是只写过手机 Demo。

知识点

  • TvInputFramework:TvInputService、TvInputManager、TvProvider(android.tv.provider)
  • EPG 数据容器:Channel、Program、PreviewProgram、WatchNextProgram 四张表
  • 国内数据源:DVB-C/SI 表(NIT、EIT)、IPTV 私有 SDP、OTT-HLS 的 EXT-X-METADATA、运营商 CMS JSON
  • 权限模型:系统级签名权限 ACCESS_TV_PROVIDER、READ_EPG_DATA;Android 11+ 分区存储对 /data/vendor/tv 的影响
  • 事务批量写入:ContentProviderOperation.newInsert().withValueBackReference() 批量 applyBatch,一次 200~300 条为阈值
  • 增量同步:EIT p/f + schedule 的 version_number 字段做 diff,MD5 摘要做二级校验,防止重复写入
  • 时钟同步:TDT/TOT 或 NTP;国内运营商 CDN 时间误差>30 s 时,用 SNTP 对齐,否则节目录制会错位
  • 合规要求:总局 181 号文——境外频道需走白名单;EPG 必须带 contentRatings(国内统一为“普/护/限”三级)
  • 性能:JobScheduler + 充电 + 空闲网络,24 h 内分 4 个窗口拉取;前台 30 min 刷新一次,后台最长 6 h
  • 异常兜底:Provider 写入失败抛 OperationApplicationException,捕获后降级到内存缓存,同时发广播提示 Launcher 刷新失败图标

答案

“在国内 Android TV 项目里,我把 EPG 同步拆成‘数据源适配层、解析层、持久化层、刷新层’四层,保证 0 掉帧、0 合规风险。

第一步,数据源适配层。DVB 有线方案直接走 TIF 的 Tuner 回调,在 TvInputService 的 onSessionCreated 里注册 DvbParser,监听 EIT p/f 与 schedule 表;IPTV 场景运营商给的是私有 SDP 文件,我把它转成 HLS 的 EXT-X-METADATA 标签,复用同一套解析器;OTT 端用 CMS 提供的 HTTPS 接口,带 If-Modified-Since 头,增量拿 JSON。

第二步,解析层。收到原始数据后,先拿 TDT/TOT 或 NTP 对齐系统时钟,误差超过 5 s 就校准,否则录制任务会错位。解析出的每条节目先算一次 MD5,与 TvProvider 里 program.hash 字段比对,只有变化才进入下一步,这样能把 80% 的重复数据挡在内存外。

第三步,持久化层。用 ContentProviderOperation 批量写入,每批 200 条,开事务 applyBatch,插入顺序按 startTime 升序,防止 ContentProvider 触发频繁 notifyChange。写入前先 query 一次冲突行,若存在则改为 update,保持主键 _id 不变,这样 Launcher 的 RecyclerView 不会闪屏。针对总局 181 号文,我会把 contentRatings 统一映射成“普/护/限”三级,写进 TvContract.Programs.COLUMN_CONTENT_RATING 字段,境外频道若不在白名单直接丢弃。

第四步,刷新层。写入完成后发 TvContract.ACTION_WATCH_NEXT_PROGRAMS_UPDATED 广播,通知系统 WatchNext 行刷新;同时调用 TvInputManager.notifyVideoAvailable(),让 Live TV App 立即拉取最新 EPG。整个流程放在 JobScheduler 里,约束条件为“设备充电 + 后台空闲网络”,24 h 拆成 4 个窗口,平均每个窗口 50 kB~300 kB 数据,实测 1 G 内存盒子掉电 <1%。

异常场景也做了兜底:若运营商 CMS 返回 502,把失败时间写进 SharedPreferences,指数退避到 6 h 后重试;若 TvProvider 写入抛 OperationApplicationException,捕获后把未写入数据序列化到 /data/vendor/tv/epg_cache.json,等下次 Job 启动再合并,用户侧无感知。”

拓展思考

  1. 折叠屏与多窗口:如果未来 TV 与 Foldable 融合,EPG 面板可能以 Picture-in-Picture 悬浮。此时 TvProvider 的 notifyChange 会触发跨进程 UI 重绘,需把 bulkInsert 改成 applyBatch 并关闭 notify,再手动发定向广播,减少 Launcher 主进程 30% 的 CPU 抖动。
  2. 隐私沙盒:Android 14 引入 READ_TV_PROVIDER 受限权限,第三方 Launcher 无法直接访问 EPG。可让系统 TvInput 把脱敏数据(无用户 token)推到 system_server 的 TvProvider,再暴露给 Launcher 只读 URI,兼顾合规与功能。
  3. AI 节目推荐:把 EPG 原始字段同步到 Room 后,用 TensorFlow Lite 在本地做 CTR 预估,生成 WatchNextProgram 的 weight 分值,再写回 TvProvider。实测推荐准确率提升 18%,但模型体积 4.3 MB,需在 JobScheduler 里加“设备空闲”约束,防止前台卡顿。