PAYATHON 2026

iOS 集成支付宝支付 SDK 15.8.10 出现严重卡顿

支付小周

结论

这两个卡顿点都出现在支付宝 SDK 的 UIApplication+ExtendOpenUrl 内部,业务侧无法直接修改。不要通过 Hook、替换分类方法或修改 SDK 二进制来处理,否则可能影响支付跳转、应用审核和后续升级。

建议按以下顺序排查:

  1. 升级到支付宝官方当前提供的稳定版 SDK,并完整替换旧 Framework。
  2. 确保支付只能由用户操作触发,避免重复调用、轮询或并发发起支付。
  3. openURL: 相关操作必须在主线程执行。支付参数生成、签名和网络请求则应放到后台。
  4. 查看 Bugly 的完整调用栈,确认真正的阻塞来源。
  5. 如果升级后仍能稳定复现,将完整堆栈和复现条件提交给支付宝技术支持。SDK 内部的问题只能由支付宝修复。

为什么会出现卡顿

alipayCanOpenURL: 通常用于判断支付宝客户端或相关 URL Scheme 是否可用,openURL:options: 用于跳转到支付宝 App。它们最终都会调用系统提供的 URL 查询或打开能力。

Bugly 将卡顿归到这两个方法,并不代表方法本身执行了大量计算。可能的情况包括:

  • 主线程此前已经被网络请求、签名、JSON 处理或磁盘 I/O 阻塞;
  • 同一次支付被连续触发,导致程序反复检测 URL 或发起跳转;
  • 多个线程同时调用支付入口;
  • 旧版 SDK 的 URL 兼容处理在当前 iOS 系统上存在性能问题;
  • App 切换、系统服务响应或支付宝客户端状态导致 openURL: 返回较慢。

排查时需要结合卡顿持续时间、完整的主线程堆栈、发生频率和系统版本,不能只根据堆栈顶部的两个符号下结论。

优先升级支付宝 SDK

不能默认认为 15.8.10 是当前最新版。支付宝 SDK 的版本、下载渠道和升级说明可能发生变化,应以支付宝开放平台提供的最新稳定版及接入文档为准。

升级时不要只覆盖同名文件,应执行以下操作:

  1. 删除工程中原有的支付宝 SDK Framework、静态库和旧头文件引用。
  2. 按照新版文档重新引入依赖。
  3. 检查 Other Linker Flags、URL Scheme、白名单和回调配置。
  4. 执行 Clean Build Folder,清理 Derived Data 后重新构建。
  5. 在真机上分别测试已安装和未安装支付宝客户端的情况。
  6. 检查 Podfile.lock、工程依赖或最终 App 包,确认没有同时包含两个版本。

如果使用 CocoaPods,Pod 名称和版本应以支付宝官方文档为准,不要自行猜测版本号。

避免重复发起支付

支付入口需要增加状态保护,防止用户连续点击或业务回调被重复执行。下面的代码只演示如何控制调用,具体支付 API 以项目实际接入的支付宝 SDK 为准。

@interface PaymentManager ()

@property (atomic, assign) BOOL paying;

@end

@implementation PaymentManager

- (void)startPaymentWithOrderString:(NSString *)orderString {
    if (orderString.length == 0 || self.paying) {
        return;
    }

    self.paying = YES;

    dispatch_async(dispatch_get_main_queue(), ^{
        [[AlipaySDK defaultService]
            payOrder:orderString
            fromScheme:@"your_app_scheme"
            callback:^(NSDictionary *resultDic) {
                self.paying = NO;
                [self handlePaymentResult:resultDic];
            }];
    });
}

- (void)handlePaymentResult:(NSDictionary *)result {
    // 处理支付结果。
}

@end

实际项目还要处理支付完成回调、应用被系统终止,以及回到前台却未收到同步回调等情况。最终支付状态应以服务端异步通知或主动查询结果为准,不能只依赖客户端回调。

将支付前的耗时工作移出主线程

向服务端请求创建订单、解析数据等工作可以放在后台执行。拿到最终的 orderString 后,再回到主线程调用支付 SDK。

- (void)prepareAndStartPayment {
    if (self.paying) {
        return;
    }

    self.paying = YES;

    dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
        NSError *error = nil;

        // 示例:向业务服务端获取已经签名的支付宝订单字符串。
        NSString *orderString = [self requestSignedOrder:&error];

        dispatch_async(dispatch_get_main_queue(), ^{
            if (error || orderString.length == 0) {
                self.paying = NO;
                [self showPaymentError:error];
                return;
            }

            [[AlipaySDK defaultService]
                payOrder:orderString
                fromScheme:@"your_app_scheme"
                callback:^(NSDictionary *resultDic) {
                    self.paying = NO;
                    [self handlePaymentResult:resultDic];
                }];
        });
    });
}

不建议在客户端保存商户私钥并完成订单签名。通常应由业务服务端生成并签名订单,客户端只负责发起支付。

不要把 openURL: 简单移到后台线程

UIApplication 中与界面和应用跳转有关的 API 应在主线程调用。直接把 openURL: 或整个支付宝支付入口放到后台线程并不能可靠地改善性能,反而可能引发线程安全问题或异常行为。

如果业务代码需要自行打开 URL,可以使用系统公开接口:

NSURL *url = [NSURL URLWithString:urlString];
if (url == nil) {
    return;
}

dispatch_async(dispatch_get_main_queue(), ^{
    UIApplication *application = UIApplication.sharedApplication;

    if ([application canOpenURL:url]) {
        [application openURL:url
                     options:@{}
           completionHandler:^(BOOL success) {
               if (!success) {
                   // 处理打开失败。
               }
           }];
    }
});

支付宝支付流程仍应优先通过 AlipaySDK 提供的公开支付接口完成,不要绕过 SDK,自行拼接并打开支付 URL。

检查 URL Scheme 配置

如果应用主动使用 canOpenURL: 查询某些 Scheme,需要按照苹果的要求配置 LSApplicationQueriesSchemes。具体 Scheme 应以支付宝当前的接入文档为准,不要直接照搬来源不明的配置。

同时还要确认:

  • fromScheme 与应用注册的回调 Scheme 完全一致;
  • Scheme 没有与其他 SDK 或应用功能冲突;
  • 程序没有在启动、进入前台或页面刷新时持续调用 canOpenURL:;
  • 程序没有通过定时器反复检测是否安装了支付宝。

配置错误更常见的表现是跳转失败,但如果程序因此反复重试,也可能加重主线程卡顿。

结合完整调用栈定位

在 Bugly 中建议重点检查以下信息:

  • 卡顿发生时主线程的完整堆栈;
  • alipayCanOpenURL: 或 openURL:options: 的上层调用者;
  • 是否存在锁等待,例如 dispatch_sync、semaphore_wait 或 @synchronized;
  • 主线程是否执行了网络请求、文件读写或复杂序列化;
  • 问题是否在同一设备和系统版本上高频出现;
  • 卡顿是否集中发生在连续点击支付、切换 App 或未安装支付宝的场景。

也可以在业务支付入口记录耗时,用来判断程序是在调用 SDK 前已经卡住,还是进入 SDK 后才出现阻塞:

CFTimeInterval startTime = CACurrentMediaTime();

[[AlipaySDK defaultService]
    payOrder:orderString
    fromScheme:@"your_app_scheme"
    callback:^(NSDictionary *resultDic) {
        CFTimeInterval elapsed = CACurrentMediaTime() - startTime;
        NSLog(@"Alipay flow callback elapsed: %.3f s", elapsed);

        [self handlePaymentResult:resultDic];
    }];

这段时间也包含用户在支付宝中的操作时间,不能直接当作 SDK 的性能指标。要定位同步阻塞,需要分别记录调用前后的时间:

CFTimeInterval begin = CACurrentMediaTime();

[[AlipaySDK defaultService]
    payOrder:orderString
    fromScheme:@"your_app_scheme"
    callback:^(NSDictionary *resultDic) {
        [self handlePaymentResult:resultDic];
    }];

CFTimeInterval returnTime = CACurrentMediaTime();
NSLog(@"payOrder synchronous cost: %.3f ms",
      (returnTime - begin) * 1000.0);

不建议采用的处理方式

不要直接交换或覆盖以下分类方法:

alipayCanOpenURL:
openURL:options:

以下做法也不建议采用:

  • 修改支付宝 SDK 的反编译代码;
  • 使用 Method Swizzling 绕过 SDK 的 URL 处理;
  • 不经过 SDK,直接构造支付宝支付链接;
  • 为消除 Bugly 告警而屏蔽整个调用栈;
  • 在后台线程调用 UIApplication 跳转接口。

这些做法可能暂时改变监控结果,却会增加兼容性、支付回调、安全性和应用审核方面的风险。

如果使用最新稳定版仍能复现问题,应向支付宝技术支持提供 SDK 版本、iOS 版本、设备型号、完整卡顿堆栈、复现步骤和支付调用代码。如果确认同步阻塞发生在 UIApplication+ExtendOpenUrl 内部,最终仍需要支付宝修改 SDK 的实现。

备注:内容仅供参考。