优化 Android SDK 中的流程与付费墙获取
在 Android 上,一次可靠的流程或付费墙获取需要做到三点:快速渲染、返回针对目标受众的实验变体,以及在网络较慢时优雅地降级到备用方案。以下规则涵盖了实现这些目标所需的时机选择、缓存策略与备用方案。
以下规则假定 Adapty.activate() 和 Adapty.identify() 均已完成。详情请参阅 Android SDK 调用顺序。
注意事项与常见陷阱
| 应该这样做 | 不要这样做 | 原因 |
|---|---|---|
获取即将显示的版位,或使用 preloadFlows(SDK 4.1+)预热缓存。 | 在启动时自行并发调用多个 getFlow。 | 自己编写的预取请求会阻塞主线程并导致黑屏。preloadFlows 专为此场景设计,会自动并发执行批量请求。 |
在归因数据有机会解析后再调用 getFlow——例如在 activate 后等待 1–2 秒,或在 setOnProfileUpdatedListener 触发后调用。 | 在 Application.onCreate() 中调用 getFlow。 | 此时归因数据尚未到达,流程会按默认目标受众解析,静默跳过市场细分和 ASA 个性化配置。 |
设置 loadTimeout 并为每个版位配置备用付费墙。 | 无限等待 getFlow 返回。 | 没有超时限制时,网络较差的用户会看到空白屏幕,直到网络恢复——或者直接关掉应用。 |
有关 fetchPolicy 和 loadTimeout 参数的说明,请参阅获取付费墙和产品;有关选择合适版位的说明,请参阅版位。
预加载版位
preloadFlows 和 preloadFlowsForDefaultAudience 从 SDK 4.1 版本起可用。
preloadFlows 会提前缓存流程 JSON,每个版位发起一次请求。之后的用法与平时一样:通过 getFlow 获取流程,通过 getFlowConfiguration 获取其视图配置。
fetchPolicy 决定后续 getFlow 优先从哪一层读取数据,而不是能否访问缓存:
ReturnCacheDataElseLoad优先读取预加载的副本,仅在缓存为空时才发起网络请求。ReturnCacheDataIfNotExpiredElseLoad(maxAgeMillis)行为相同,但仅在副本的存储时间不超过maxAgeMillis时生效。- 默认策略
ReloadRevalidatingCacheData优先走网络,仅在请求失败或超时时回退到预加载的副本。
无论哪种方式,预加载都有价值,只是效果不同:缓存优先策略会直接省去网络请求,而默认策略则保留请求,同时获得一个可供回退的热缓存副本。
在您知道本次会话需要哪些版位、但暂时不想展示时使用——例如,在 activate 和 identify 完成之后,为用户尚未点击的按钮背后的流程预加载。
参数:
placementIds(必填):需要预加载的版位。空白 ID 和重复 ID 将被忽略。loadTimeout(可选):超时时间作用于批次中的每个版位,而非整个批次。默认为 5 秒,低于 1 秒的值将被自动提升至 1 秒。
以下行为需注意:
- 该回调仅在所有版位都尝试完毕后才会触发,并统一报告各版位的失败情况。某个版位的失败不会阻止其他版位的处理。
- 如果某个版位超时、遇到服务器错误或网络错误,SDK 会回退到该版位的备用变体。其他类型的失败则按原样上报。
- 预加载仅用于预热缓存,不会返回内容——你仍需调用
getFlow来显示内容。
预加载涵盖的内容
流程到达屏幕的过程分为多个层次。预加载仅覆盖第一层,与 getFlow 的作用完全相同:
| 层次 | 由谁获取 | 是否被预加载预热 |
|---|---|---|
| Flow JSON —— 所选实验变体、其产品 ID 及远程配置 | getFlow | 是 |
| UI 布局 —— 屏幕的结构、样式和文本 | getFlowConfiguration | 否 |
| 图片,包括用于替代视频元素的静态帧 | getFlowConfiguration,在后台处理 | 否 |
| 视频文件 | 系统播放器,在屏幕渲染时加载 | SDK 不缓存 |
getFlowConfiguration 会等待布局加载完成,因此即使已预加载,首次请求某个布局时仍需要一次完整的网络往返。SDK 随后会将该布局保存到自身的磁盘缓存中,缓存在应用重启后依然有效,并且在发起任何网络请求之前会优先读取缓存,所以这次开销只发生在首次请求时,而非每次请求。一旦 SDK 获取到布局,便会独立启动图片缓存:该过程不会阻塞页面展示,也不提供任何回调或错误来通知缓存完成时机。
查找哪个版位失败了
回调会接收一个覆盖整批请求的 AdaptyPreloadPlacementsError,错误码为 REQUEST_FAILED(2005)。要查看各个版位的失败详情,请读取其 preloadErrors 属性——这是一个以版位 ID 为键的映射:
Adapty.preloadFlows(listOf("onboarding", "main_paywall")) { error ->
if (error is AdaptyPreloadPlacementsError) {
error.preloadErrors.forEach { (placementId, placementError) ->
// log or retry the individual placement
}
}
}
preloadErrors 仅存在于 AdaptyPreloadPlacementsError 上,因此请先确认类型——其他 AdaptyError 来自版位级别失败以外的原因。
跳过目标受众细分
如需在完全不等待目标受众细分的情况下预热缓存,请使用默认受众实验变体。该方法不需要 loadTimeout:
Adapty.preloadFlowsForDefaultAudience(listOf("main_paywall")) { error -> }
从应用包中显示第一屏媒体内容
流程会从 Adapty 下载图片和视频。若要在第一屏立即显示媒体内容,可改为从应用包中提供。这样可以复用已经打包在应用中的素材,例如现有原生用户引导界面的视觉资源。
- 在 Flow 与付费墙编辑工具中,为图片或视频设置自定义媒体 ID。在那里上传的文件将作为备用文件保留。
- 将文件添加到应用的
res/raw或assets文件夹中。 - 使用
getFlowView创建流程视图时,在customAssets中传入该 ID 对应的本地文件:
// "welcome_video" is the custom media ID set in the Flow & Paywall Builder
val bundledAssets = AdaptyCustomAssets.of(
"welcome_video" to
AdaptyCustomVideoAsset.file(
FileLocation.fromResId(requireContext(), R.raw.welcome),
preview = AdaptyCustomImageAsset.file(
FileLocation.fromResId(requireContext(), R.drawable.welcome_poster),
),
resolution = AdaptyCustomVideoAsset.Resolution(width = 1080, height = 1920),
),
)
val flowView = AdaptyUI.getFlowView(
activity,
flowConfiguration,
products,
eventListener,
insets,
bundledAssets,
)
捆绑文件会增加应用的下载体积,因此只捆绑用户首屏看到的媒体资源。
未捆绑的媒体同样会立即显示:视图配置中为每张图片(包括视频的静帧封面)内置了一份低分辨率预览图,在完整文件加载完成前持续展示。
完整的 customAssets 参考文档,请参阅自定义资源。
针对弱网环境进行调优
对于网络状况持续较差的市场(农村地区、交通沿线、受路由影响的地区):
- 除首次请求外,每次获取时将
fetchPolicy设置为AdaptyPlacementFetchPolicy.ReturnCacheDataElseLoad。 - 在 Adapty 看板中为每个版位配置备用付费墙。
- 将
loadTimeout设置为 3–5 秒,并在超时触发时接受备用内容。 - 不要将流程显示挂起在
getProfile上。独立调用getFlow,避免因用户画像加载缓慢而阻塞 UI。