同步补丁API

用户无需到App Store更新就能用到新的代码,原理说出来也简单,就是在APP打开的时候,调用SOT的API,通过网络同步到SOT的补丁,并且加载该补丁。加载补丁后,就会用新的代码替换掉原来APP的代码,实现无感知的更新。本文介绍同步补丁的API接口如何使用,开发者根据自己的需要来做出选择。

1.05版本之后的SotWebService.h里,有三个网站版的接口API,分别是SyncApplyCachedAndPullShipPullShip。其中Sync和ApplyCachedAndPullShip选择其一即可,PullShip可以和ApplyCachedAndPullShip配合着用。 ... 调用它们需要的参数都是一样的,分别是:

  • version_key:在网站上创建版本,会生成唯一的VersionKey字符串,传给API接口表示同步该版本的补丁。
  • is_dev:网站配置补丁状态时,可以选择调试,当is_dev是true的时候,才会同步到该补丁。例如做了一个新的补丁,可以先用调试模式测试,不会影响线上APP。测试通过后再进行灰度发布,确认没任何问题最后全量发布。 ...
  • cb:同步补丁结果的回调函数,成功拉取补丁,或者失败都会通过该函数回调,告诉调用方同步补丁的结果。有以下几种枚举:
    1. SotScriptStatusSuccess:成功同步到最新补丁并且成功加载了补丁
    2. SotScriptStatusFailure:同步补丁失败
    3. SotScriptStatusError:同步补丁失败
    4. SotScriptApplyError:同步了最新补丁,但补丁加载失败,可能是补丁文件下载有误或者补丁文件与APP不兼容
    5. SotScriptShipDisable:当前该版本没有启用的补丁
    6. SotScriptShipHasSyncNewer:仅用于ApplyCachedAndPullShip,PullShip,表明同步到了更新的补丁
    7. SotScriptShipAlreadyNewest:仅用于ApplyCachedAndPullShip,PullShip,表明本地补丁已经是最新的


下面分别介绍每个接口的使用场景和方式。

1. Sync

Sync是最简单的接口,每次调用时,会去同步最新的补丁,同步成功时就会直接加载该补丁,成功的回调结果是SotScriptStatusSuccess。

但假如手机没有网络,则无法同步到补丁信息。所以也不会加载任何补丁(即使之前有网络的时候,手机已经成功同步过补丁),回调会返回错误。

注意该接口去网站上拉取补丁,是有网络延迟的,所以该接口是个异步回调接口,只有回调SotScriptStatusSuccess之后,APP才开始用补丁的代码。所以通常首屏代码用该接口无法热更,因为在补丁还没有传输到手机上时,首屏代码都已经被调用了,那必然还是老的代码。所以请使用者注意调用时序的问题,下面是一个调用API的例子: ...

调用的时序如下图: ...

该接口可以在APP打开期间调用多次,每次都会同步最新的补丁状态,并且重新加载当前最新的补丁。例如APP一直在后台没有被杀死,当被唤醒到前台时,代码里有写进行一次Sync同步,就会同步和加载最新的补丁。虽然可以这么做,但我不建议,因为可能会引起一些奇怪的BUG,例如旧补丁新增了一些函数,而在新补丁中删掉了。

假如APP已经加载过了补丁,此时开发者在网站后台禁用所有补丁,假如APP没有被杀死,又进行了一次Sync同步,那么也不会把之前加载过的补丁卸载,只有完全退出并且重启APP,才不会去加载任何补丁。


2. ApplyCachedAndPullShip

上面介绍的Sync接口,使用起来比较简单,但是有两个缺点。一是没有网络的情况下无法使用,即使之前已经同步过了补丁。二是需要考虑网络延迟,导致一些调用时机比较早的代码,无法被替换,例如首屏初始化代码。

ApplyCachedAndPullShip接口可以解决上面的问题。调用该接口时,会直接加载上一次同步成功的补丁,因为上一次成功同步的补丁,已经被缓存到了本地。加载补丁的同时也会向网站发起一次补丁同步,并在回调中告知调用方,本地缓存的补丁是否还是最新的,通常会有以下三种结果:

  1. 回调SotScriptShipAlreadyNewest,表明本地缓存的补丁还是最新的,皆大欢喜,无需做什么。
  2. 回调SotScriptShipHasSyncNewer,表明之前本地补丁已经过时,并且已经把本次更新到的补丁缓存下来了,但不会立即加载,下次启动才会加载。如果有需要,可以此时请求用户把APP重启以加载最新补丁。
  3. 回调SotScriptShipDisable,说明无可用的补丁,此时会删除本地缓存的补丁,但不会卸载已经加载好的补丁,重启APP时不会再加载任何补丁。

下面是一个调用的例子: ... 代码:

    SotApplyCachedResult ApplyShipResult = [SotWebService ApplyCachedAndPullShip:@"1234567" is_dev:false cb:^(SotDownloadScriptStatus status)
    {
        if(status == SotScriptShipAlreadyNewest)
        {
            NSLog(@"SyncOnly SotScriptShipAlreadyNewest");
        }
        else if(status == SotScriptShipHasSyncNewer)
        {
            NSLog(@"SyncOnly SotScriptShipHasSyncNewer");
        }
        else if(status == SotScriptShipDisable)
        {
            NSLog(@"SyncOnly SotScriptShipDisable");
        }
        else
        {
            NSLog(@"SyncOnly SotScriptStatusFailure");
        }
    }];
    if(ApplyShipResult.Success)
    {
        if(ApplyShipResult.ShipMD5)
            NSLog(@"sot success apply cached ship md5:%@", ApplyShipResult.ShipMD5);
    }
    
当成功加载本地缓存的补丁时,则会马上替换掉老代码,所以只要在首屏代码调用前,调用该接口,则可以实现热更新首屏代码。

调用的时序如下图: ...

接口也可以被调用多次,每次调用也会卸载旧补丁并且加载新的缓存好的补丁,但不建议如此操作。

这个接口也并非完美,因为本次打开APP时使用的是之前缓存下来的补丁,所以可能不是最新的,新补丁需要推迟到下一次启动才能加载。


3. PullShip

为了让APP更快地同步到最新的补丁,提供了PullShip接口,该接口只会去网站上同步当前补丁状态,发现新补丁则会缓存下来,供下次启动时使用。

接口的调用方式和结果的意义和ApplyCachedAndPullShip接口是完全相同的。这两个接口很像,PullShip只是同步而不加载,ApplyCachedAndPullShip即加载也同步。


费用说明

这三个接口每次调用,都会发起一次补丁同步,消耗1瓦力。如果同步失败,例如网络有问题或者别的错误发生,则会自动重试一次。同步的请求因为网络原因而没有发送到网站,则不会被统计到,所以也不会消耗瓦力。

所有接口同步时,如果发现有更新的补丁时,需要消耗下载费用,补丁每100KB消耗3瓦力。之后都会把补丁文件缓存到本地,缓存过后下次同步,不会再消耗下载补丁的费用。