在APP里打开一个小程序,背后发生了什么——小程序运行全生命周期管理

简介: 从APP运行角度的视角分享一下:当自己的APP引入小程序runtime之后,小程序的打开方式、启动模式、关闭方式和离线加载方式,如何优化小程序的运行模式~

封面.png

现在,很多APP都从H5混合开发升级为“Native+小程序”的开发架构,接下来从自身团队的开发经历出发,分享一下,如何当APP引入小程序之后,用户打开一个小程序后,背后的运行机制。
整体包括:打开方式、启动模式、关闭方式、预加载机制~


打开小程序的几种方式

APP里打开小程序有几种不同方式,分别对应不同的业务场景。

最常用的方式:通过小程序ID直接打开

FATAppletRequest *request = [[FATAppletRequest alloc] init];
request.appletId = @"小程序id";       // 必填
request.apiServer = @"服务器地址";    // 必填

[[FATClient sharedClient] startAppletWithRequest:request
                          InParentViewController:self
                                      completion:^(BOOL result, FATError *error) {
   
    NSLog(@"打开小程序结果: %@", error);
} closeCompletion:^{
   
    NSLog(@"小程序被关闭了");
}];

这个接口只需要两件事:小程序的ID和服务器地址。SDK 内部会处理下载、校验、启动的流程。如果本地已经有缓存的小程序包,SDK 会先打开本地版本,然后后台检查服务器端有没有新版本,有新版本就下载,下一次打开时用新版本。

扫码打开

扫描 FinClip 管理平台上的二维码,可以打开小程序的各种版本——正式版、体验版、审核版、真机调试版、预览版都可以。

FATAppletQrCodeRequest *qrcodeRequest = [[FATAppletQrCodeRequest alloc] init];
qrcodeRequest.qrCode = qrCode;  // 二维码里的内容

[[FATClient sharedClient] startAppletWithQrCodeRequest:qrcodeRequest
                                  inParentViewController:self
                                        requestBlock:^(BOOL result, FATError *error) {
   
    NSLog(@"校验二维码完成: %@", error);
} completion:^(BOOL result, FATError *error) {
   
    NSLog(@"打开完成: %@", error);
} closeCompletion:^{
   
    NSLog(@"关闭了");
}];

通过 URL Scheme 从外部唤起

有些场景希望从 Safari 或者其他 App 直接跳转到自己的 App 并打开某个小程序,可以通过 URL Scheme 实现。需要先在 APP 里配置 URL Types,格式是 fat + SDKKey 的 MD5 值。

// 在 AppDelegate 中实现
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options {
   
    if ([[FATClient sharedClient] handleOpenURL:url]) {
   
        return YES;
    }
    return NO;
}

外部调用的 URI 格式是:fat705b46f78820c7a8://applet/appid/617bb42f530fb30001509b27?path=pages/index/index&query=key%3Dvalue


冷启动 vs 热启动:发生了什么

冷热启动.png

理解"冷启动"和"热启动"的区别,是理解整个生命周期的前提。

冷启动:用户第一次打开,或者小程序被销毁后再次打开。此时 SDK 需要从服务器下载小程序包、校验签名、初始化 JS 引擎、渲染页面。首次打开的时候,这一步会比较慢,尤其是网络条件不好的时候。

热启动:用户之前已经打开过这个小程序,小程序还在后台挂着。这时候只需要把后台的小程序切换到前台,不需要重新下载和初始化,速度会快很多。

从 SDK 的角度,打开小程序时会发生这样的判断逻辑:

  1. 检查本地是否有缓存的小程序包
  2. 如果没有 → 下载小程序包 → 下载基础库 → 初始化 → 渲染
  3. 如果有 → 先打开本地包 → 后台检查新版本 → 有新版则下载备用

如果本地有缓存,冷启动的体验会好很多。


离线包:把首次启动变快

加载.png

冷启动慢,最直接的原因是要从服务器下载小程序包和基础库。SDK 支持"离线包"机制——把小程序包和基础库预先打包进 APP,在合适的时机(比如用户刚登录的时候)提前下载好。

FATAppletRequest *request = [[FATAppletRequest alloc] init];
request.appletId = @"小程序id";
request.apiServer = @"服务器地址";
// 小程序离线包地址,可以是 bundle 路径,也可以是沙盒路径
request.offlineMiniprogramZipPath = [[NSBundle mainBundle] pathForResource:@"api_demo" ofType:@"zip"];
// 基础库离线包地址
request.offlineFrameworkZipPath = [[NSBundle mainBundle] pathForResource:@"framework-3.0.49" ofType:@"zip"];

[[FATClient sharedClient] startAppletWithRequest:request
                          InParentViewController:self.window.rootViewController
                                      completion:^(BOOL result, FATError *error) {
   
    NSLog(@"打开小程序: %@", error);
} closeCompletion:^{
   
    NSLog(@"关闭小程序");
}];

注意:离线启动时,offlineMiniprogramZipPathofflineFrameworkZipPath 必须同时传,否则 SDK 会忽略离线配置,走正常的下载流程。

还有一个更自动的做法:初始化 SDK 的时候开启基础库预下载。

FATStoreConfig *storeConfig = [[FATStoreConfig alloc] init];
storeConfig.sdkKey = @"##sdkKey##";
storeConfig.sdkSecret = @"##sdkSecret##";
storeConfig.apiServer = @"##apiServer##";
// 开启基础库预下载
storeConfig.enablePreloadFramework = YES;
FATConfig *config = [FATConfig configWithStoreConfigs:@[storeConfig]];
[[FATClient sharedClient] initWithConfig:config error:nil];

开了这个选项之后,SDK 会在初始化时自动下载基础库到本地,下次打开任何小程序都会快很多。


热启动的参数控制

热启动有一个容易踩坑的地方:启动参数。

用户第一次打开小程序时带了参数(比如从某个营销页跳转过来),小程序已经打开了。用户退到后台,过了一会儿又从后台切回来,这时候如果带了新的启动参数,SDK 会怎么处理?

SDK 提供了 reLaunchMode 参数来控制这个行为,有四种模式:

  • FATReLaunchModeParamsExist:启动参数中包含 path、query 或 referrerInfo,并且其中某个参数不为空,则执行 reLaunch
  • FATReLaunchModeOnlyParamsDiff:启动参数与上一次的不同时,才执行 reLaunch
  • FATReLaunchModeAlways:每次热启动都执行 reLaunch
  • FATReLaunchModeNever:每次热启动都不执行 reLaunch

默认是 ParamsExist 模式,即只要带了新参数就 reLaunch。业务上如果对页面栈行为有要求,可以在初始化或每次调用 startAppletWithRequest 时显式设置这个参数。


关闭小程序不等于销毁小程序

很多开发者会混淆"关闭"和"销毁"这两个动作。

点击右上角胶囊的关闭按钮,或者调用 SDK 的关闭接口:

// 关闭指定的小程序(animated 是否动画,completion 关闭完成回调,closeCompletion 小程序关闭回调)
[[FATClient sharedClient] closeApplet:@"小程序id" animated:YES completion:^{
   
    NSLog(@"关闭完成");
} closeCompletion:^{
   
    NSLog(@"小程序被关闭了");
}];

// 关闭所有小程序
[[FATClient sharedClient] closeAllAppletsWithCompletion:^{
   
    NSLog(@"全部关闭了");
}];

关闭之后,小程序并没有从内存里删除——它只是退到了后台,进入了"挂起"状态。等用户下次再打开,会立即切换到前台,不需要重新下载。

如果要彻底清内存,需要调用销毁接口:

// 销毁指定小程序(会清掉内存和缓存)
[[FATClient sharedClient] clearMemeryApplet:@"小程序id"];

// 销毁所有小程序
[[FATClient sharedClient] clearMemoryCache];

注意:如果小程序正在显示中,直接调用销毁接口不会生效——需要先调用关闭接口把它退到后台,再调用销毁接口才行。这是 SDK 防止误操作的保护设计。


删除小程序:连缓存一起清掉

如果不只清内存,还想连本地存储的小程序包和配置信息一起清掉,需要调用删除接口:

// 删除指定小程序的所有本地信息
[[FATClient sharedClient] removeAppletFromLocalCache:@"小程序id"];

// 删除所有小程序的本地信息
[[FATClient sharedClient] clearLocalApplets];

删除之后再打开,SDK 会重新从服务器下载,等同于首次安装的效果。用户换账号、清缓存、需要强制重新初始化的时候,用这个接口。


批量预加载:改善用户体验

如果你的 APP 有多个小程序入口,用户可能会打开多个。可以提前把需要的小程序包下载好,减少等待时间。

// 批量下载小程序
[[FATClient sharedClient] downloadApplets:appIds
                               apiServer:apiServer
                                complete:^(NSArray *results, FATError *error) {
   
    // results 是一个数组,每个元素是 @{@"appId": xxx, @"success": xxx, @"needUpdate": xxx}
    // needUpdate 为 false 说明本地已经是最新版本,不需要重复下载
}];

这个接口会检查本地缓存,如果某个小程序已经是最新版本,就不会重复下载,只返回结果通知你。


iPad 多窗口模式:小程序不只是全屏打开

在 iPad 上,小程序可以不全屏打开。SDK 支持多种窗口模式,方便你同时操作小程序和原生页面。

SDK 支持以下几种模式:

  • fullScreen:默认全屏模式
  • currentContext:在指定的上下文控制器中显示,可以配合 UISplitViewController 使用
  • overCurrentContext:遮罩模式,不影响原有视图栈
  • pageSheet:半屏弹窗,不可设置大小
  • formSheet:居中弹窗,大小由系统决定
  • popover:气泡弹窗,有箭头指向触发按钮
  • custom:自定义窗口区域
// 以 pageSheet 模式打开小程序
request.presentationConfig = [FATAppletPresentationConfig pageSheetPresentationConfig];

如果你的 APP 主要面向 iPad 用户,这些窗口模式可以显著提升用户体验——用户不需要在小程序和 APP 原生页面之间来回切换,可以在同一个屏幕上同时操作。


先写到这里,如果感兴趣的话可以自行搜索了解

相关文章
|
11天前
|
人工智能 开发工具 iOS开发
Claude Code 新手完全上手指南:安装、国产模型配置与常用命令全解
Claude Code 是一款运行在终端环境中的 AI 编程助手,能够直接在命令行中完成代码生成、项目分析、文件修改、命令执行、Git 管理等开发全流程工作。它最大的特点是**任务驱动、终端原生、轻量高效、多模型兼容**,无需图形界面、不依赖 IDE 插件,能够深度融入开发者日常工作流。
3279 9
|
3天前
|
人工智能 自然语言处理 文字识别
阿里云百炼Qwen3.7-Max简介:能力、优势、支持订阅计划参考
Qwen3.7-Max是阿里云百炼面向智能体时代推出的新一代旗舰模型,对标GPT-5.5、Claude Opus 4.7等闭源旗舰。该模型支持百万级token上下文窗口,具备顶级推理能力、多模态搜索与视觉理解增强、流式输出低延迟响应等核心优势,覆盖编程、办公、长周期自主执行等复杂场景。同时支持OpenAI接口兼容,便于系统快速迁移。用户可通过Token Plan团队或节省计划等订阅方式灵活调用,适合企业级高要求场景使用。
阿里云百炼Qwen3.7-Max简介:能力、优势、支持订阅计划参考
|
13天前
|
Shell API 开发工具
Claude Code 快速上手指南(新手友好版)
AI编程工具卷疯啦!Claude Code凭借任务驱动+终端原生的特性,成了开发者的效率搭子。本文从安装、登录、切换国产模型到常用命令,手把手带新手快速上手,全程避坑,30分钟独立用起来。
3329 23
|
7天前
|
人工智能 Linux BI
国内用 Claude Code 终于不用翻墙了:一行命令搞定,自动接 DeepSeek
JeecgBoot AI专题研究 一键脚本:Claude Code + JeecgBoot Skills + DeepSeek 全平台接入 一行命令装好 Claude Code + JeecgBoot Skills + DeepSeek 接入,无需翻墙使用 Claude Code,支持 Wind
2360 4
国内用 Claude Code 终于不用翻墙了:一行命令搞定,自动接 DeepSeek
|
26天前
|
人工智能 JSON 供应链
畅用7个月无影 JVS Claw |手把手教你把JVS改造成「科研与产业地理情报可视化大师」
LucianaiB分享零成本畅用JVS Claw教程(学生认证享7个月使用权),并开源GeoMind项目——将JVS改造为科研与产业地理情报可视化AI助手,支持飞书文档解析、地理编码与腾讯地图可视化,助力产业关系图谱构建。
23598 15
畅用7个月无影 JVS Claw |手把手教你把JVS改造成「科研与产业地理情报可视化大师」
|
13天前
|
人工智能 JSON BI
DeepSeek V4-Pro 接入 Claude Code 完全实战:体验、测试与关键避坑指南
Claude Code 作为当前主流的 AI 编程辅助工具,凭借强大的代码理解、工程执行与自动化能力深受开发者喜爱,但原生模型的使用成本相对较高。为了在保持能力的同时进一步降低开销,不少开发者开始寻找兼容度高、价格更友好的替代模型。DeepSeek V4 系列的发布带来了新的选择,该系列包含 V4-Pro 与 V4-Flash 两款模型,并提供了与 Anthropic 完全兼容的 API 接口,理论上只需简单修改配置,即可让 Claude Code 无缝切换为 DeepSeek 引擎。
2842 3
|
5天前
|
人工智能 自然语言处理 安全
Claude Code 全攻略:命令大全+三种模式+记忆体系+实战工作流完整手册
Claude Code 是当前最流行的终端级 AI 编程助手,能够直接在命令行中完成代码生成、项目理解、文件修改、命令执行、错误修复等全流程开发工作。它不依赖图形界面、不占用额外资源,却能深度理解项目结构,自动生成规范代码,大幅提升研发效率。
924 2
|
11天前
|
存储 Linux iOS开发
【2026最新】MarkText中文版Markdown编辑器使用图解(附安装包)
MarkText是一款免费开源、跨平台的Markdown编辑器,主打所见即所得实时预览,支持Windows/macOS/Linux。内置数学公式、流程图、代码高亮、多主题及PDF/HTML导出,是Typora的轻量免费替代首选。(239字)

热门文章

最新文章