8.5 KiB
8.5 KiB
视频首页数据获取流程(以首款换皮应用 FunyMee 为参考)
本文描述框架侧在典型换皮应用(首款为 FunyMee)中,从冷启动到「视频首页」可展示分类与模板列表的数据链路与调用顺序。FunyMee 宿主 UI 不在本仓库内;流程以 client_proxy_framework 中的 FrameworkAuthService、ExtConfigRuntime、VideoHomeRuntime、ImageApi 为准,与代码注释中引用的线网接口(如 GET /v1/image/img2video/tasks)一致。
1. 前提与配置
ClientBootstrap.initFromAsset:加载skin_config.json,构建SkinConfig并ApiClient.init,使后续请求走统一代理、AES 与 V2 包装(见lib/src/bootstrap/client_bootstrap.dart)。FrameworkAuthService.init+start:宿主实现AuthServiceCallbacks(设备 ID、签名等),由框架编排登录与common_info(见lib/src/services/auth_service.dart)。skin_config.json中与首页相关的片段:extConfig:keys.showVideoMenu映射到线网布尔(示例中为go_run/need_wait等,见lib/src/config/skin_config.example.json);items与taskItemMapping决定common_info下发的运营位卡片如何解析为与任务列表同形的ExtConfigItem。videoHome:imagesTabLabel、imagesTabFirst控制「Images」虚拟 Tab 的文案及与接口分类的排序(见lib/src/config/skin_config.dart)。
fieldMapping:FunyMee 等换皮下线网字段与逻辑字段不一致时,响应体会先经映射再进入实体解析(注释示例见lib/src/services/image_api.dart)。
2. 总览时序
sequenceDiagram
participant Host as 宿主 main / UI
participant Boot as ClientBootstrap
participant Auth as FrameworkAuthService
participant User as UserApi / ProxyClient
participant Ext as ExtConfigRuntime
participant VH as VideoHomeRuntime
participant Img as ImageApi
Host->>Boot: initFromAsset(skin_config)
Host->>Auth: init(callbacks); start()
Auth->>User: POST /v1/user/fast_login
User-->>Auth: userToken, userId
Auth->>User: GET /v1/user/common_info
User-->>Auth: extConfig 等
Auth->>Ext: applyCommonInfoSuccess
Auth->>VH: hydrateAfterCommonInfo (异步)
VH->>Img: GET /v1/image/img2video/categories
Img-->>VH: 分类列表
VH->>VH: 合并 Tabs(接口分类 + ext items → Images)
VH->>Img: GET /v1/image/img2video/tasks?categoryId=…
Img-->>VH: 当前分类模板列表
3. 分阶段说明
3.1 启动与快速登录
- 延迟与重试策略由
FrameworkAuthService.start控制(默认启动延迟、登录重试等)。 UserApi.fastLogin:POST /v1/user/fast_login。线网内层 headers 保留pkg,但不注入User_token(见UserApi/ProxyClient注释)。Querytype未传时默认为gg(Google Play 归因)。FrameworkAuthService强制type=gg:referer优先 Play Install Referrer,取不到时用utm_source=google-play&utm_medium=organic。- 成功后框架将返回的
userToken写入ApiClient.instance.setUserToken,此后代理请求自动附带pkg与User_token(见ProxyClient行为与UserApi说明)。
3.2 归因上报(与首页数据并行准备)
- 在拉取
common_info之前,若存在 Adjust / Play 等 referrer,会依次调用UserApi.referrer(POST /v1/user/referrer)上报,不阻塞后续common_info的成功与否结论,但属于同一_reportReferrersAndLoadCommonInfo流程(见auth_service.dart)。
3.3 通用信息与 extConfig
UserApi.getCommonInfo:GET /v1/user/common_info,query 含app(iOS/Android 后端渠道,与skin_config.backend一致)、pkg、userId、deviceId等(见user_api.dart)。- 成功时调用
ExtConfigRuntime.applyCommonInfoSuccess:- 将
extConfig与本地extConfig.defaults浅合并; - 按
extConfigKeySchema解析为ExtConfigData(含showVideoMenu、items等)(见ext_config_runtime.dart、ext_config_models.dart)。
- 将
ExtConfigRuntime.commonInfoSucceeded:宿主可用「登录完成且common_info成功」再展示主业务界面(见ext_config_runtime.dart注释建议)。
3.4 视频首页运行时:水合(hydrate)
在 common_info 成功后,FrameworkAuthService 会 fire-and-forget 调用 VideoHomeRuntime.hydrateAfterCommonInfo(不阻塞 loginComplete 的完成)(见 auth_service.dart)。
hydrateAfterCommonInfo 的进入条件与行为(见 video_home_runtime.dart):
| 条件 | 行为 |
|---|---|
userId 为空,或 ExtConfigData.showVideoMenu != true |
VideoHomeRuntime.reset,不拉分类、无 images Tab |
| 否则 | 置 snapshot.loading = true,再请求分类列表 |
水合步骤概要:
ImageApi.getCategoryList→GET /v1/image/img2video/categories,得到服务端分类(CategoryItem,含id/name)。- 从
ExtConfigRuntime.data读取items,过滤ExtConfigItem.isUsableOnHome,非空则存在 Images 虚拟 Tab,文案为AppConfig.videoHomeImagesTabLabel(来自skin_config.videoHome.imagesTabLabel)。 - Tab 顺序:由
videoHomeImagesTabFirst决定是先 Images 还是先接口分类。 - 默认选中 Tab:默认选中第一个服务端分类(非 Images);若仅有 Images Tab 则下标为 0;并发水合与用户提前切换时有保护逻辑(见
video_home_runtime.dart内注释)。 - 水合结束后调用
VideoHomeRuntime.ensureTabItems(selectedTabIndex),为当前 Tab 拉取模板数据(见下节)。
若分类接口失败且没有任何 Tab 可构建,snapshot.error 会携带失败信息。
3.5 按 Tab 拉取模板列表
- Images Tab:数据直接来自
ExtConfigData.items(已在水合前解析),不再请求img2video/tasks。 - 服务端分类 Tab:首次选中该 Tab 时,
VideoHomeRuntime.ensureTabItems调用ImageApi.getImg2VideoTasks(categoryId: id)→GET /v1/image/img2video/tasks?categoryId=...(与 FunyMee 文档一致)。结果通过ExtConfigItem.fromTaskItem写入VideoHomeSnapshot.networkItemsByCategoryId,并按分类 id 缓存,避免重复请求。
4. 宿主 UI 对接要点
- 监听状态:
VideoHomeRuntime.snapshot、VideoHomeRuntime.selectedTabIndex为ValueNotifier,可用ValueListenableBuilder或类似方式驱动顶栏 Tab 与内容区。 - 切换 Tab:切换
selectedTabIndex后应调用VideoHomeRuntime.ensureTabItems(newIndex)(框架水合末尾已对初始 Tab 调用一次),以按需加载该分类下的模板列表。 - 登录 / common_info 失败:
ExtConfigRuntime.commonInfoSucceeded == false或userId为空时,不会进入视频首页水合;宿主应展示错误态或重试入口。
5. 相关 HTTP 接口汇总
| 顺序 | 方法 | 路径 | 用途 |
|---|---|---|---|
| 1 | POST | /v1/user/fast_login |
设备登录,取得 token |
| 2 | POST | /v1/user/referrer |
可选,归因上报 |
| 3 | GET | /v1/user/common_info |
开关与 extConfig(含首页运营位 items) |
| 4 | GET | /v1/image/img2video/categories |
视频首页顶栏服务端分类 |
| 5 | GET | /v1/image/img2video/tasks |
某分类下模板列表(query:categoryId) |
所有经 ProxyClient 的业务请求均走 skin_config 中的 proxyPath,body 为加密后的代理载荷;响应经解密与 fieldMapping 还原为逻辑字段后再解析实体。
6. 代码索引(便于跳转)
| 模块 | 路径 |
|---|---|
| 启动换皮配置 | lib/src/bootstrap/client_bootstrap.dart |
| 登录与 common_info 编排 | lib/src/services/auth_service.dart |
| 用户接口 | lib/src/services/user_api.dart |
| 图/视频分类与任务列表 | lib/src/services/image_api.dart |
| extConfig 运行时 | lib/src/config/ext_config_runtime.dart |
| 视频首页 Tab 与缓存 | lib/src/config/video_home_runtime.dart |
| extConfig / items 解析 | lib/src/config/ext_config_models.dart |
| JSON 配置示例 | lib/src/config/skin_config.example.json |
7. 与《创建新换皮应用》的关系
从零搭建宿主工程、资产路径与 main 初始化顺序,仍以 create_new_skin_app.md 为准;本文仅补充 「视频首页」在框架内的数据流,与 FunyMee 首推路线一致。