[FMX] 为你的移动应用加入在线更新支持

程序永远与 Bug 相伴,即使不是因为 Bug,我们的程序也可能因为增加或调整功能等原因,需要进行升级。那么在移动应用中,实际上还是蛮简单的。

  • 第一关:在服务器端为升级准备必要的文件
    • 版本检查文件:应用需要下载此文件与本地的文件进行比较以确定是否需要更新,为了方便我们可以用 Json 格式来定义更新的应用的信息,比如:
      {
      "current":{
         "version":"2.1.0.2",
         "description":"更新说明:\r\n修正了一些内部错误",
         "url":"http://xxx.yy.com/update/appv2.apk"
         }
      }

      上面的这个 Json 格式仅仅是一个示意,我自己实际使用的要比这个复杂一点点。我自己实际使用的是服务器端使用 PHP 根据客户端应用的类型动态生成的一个更新用的 JSON,但大体意思和上面是一样的,只是多了几个额外的项目定义。上面的 url 下更新文件的下载路径,如果是iOS 应用,则是应用商店的新版本链接。

    • 将文件放到版本检查文件返回的下载地址中,以便客户端能够自动下载并更新(仅 Android,iOS 更新是直接跳到应用商店来完成更新)。
  • 第二关:版本比较
    • 在手机应用启动后获取版本检查文件,然后与本地应用的版本进行比较,以确定是否需要升级。我是通过 QWorker 提交了一个后台的异步作业,通过 THttpClient 来从服务器端获取版本检查的 JSON,然后将取回的 JSON 与应用的当前版本进行比较来确定是否需要升级的。这里提供一个关键函数,获取应用的当前版本号,至于版本比较,就自己写下就好。
      var
         AppSvc: IFMXApplicationService;
      begin
      if TPlatformServices.Current.SupportsPlatformService(IFMXApplicationService,
          AppSvc) then
          begin
          // AppSvc.AppVersion 就是应用的版本号了
          end;
      end;
    • 一般在升级之前,我们需要给用户一个提示以确定是否用户现在方便升级。我们一般用 TDialogService.MessageDlg 带回调函数的版本来完成这一操作。注意这里需要将下载的地址封装成一个 TObject 的子类,以便传递给 MessageDlg 的回调函数,我直接用的 TQJobExtData。
  • 第三关:下载并安装升级包
    • Android
      • 使用 THttpClient 下载升级包,这里我们使用 THttpClient 在后台异步下载升级包
      • 使用系统自带的安装包管理器安装新版本
    • iOS
      • 直接使用 OpenUrl  打开链接会自动跳转到应用商店去完成更新

这里我们给出一段参考代码:

procedure TForm1.DownloadUpdates(AJob: PQJob);
{$IFDEF ANDROID}
var
  AStream: TStream;
  AHttp: THttpClient;
  AReply: IHttpResponse;
  AFileName: String;
  ADownloaded: Boolean;
  AUrl: String;
{$ENDIF}
begin
{$IFDEF ANDROID}
  AFileName := System.IOUtils.TPath.GetDownloadsPath +
    System.IOUtils.TPath.DirectorySeparatorChar + '_update.apk';
  ADownloaded := false;
  AStream := TFileStream.Create(AFileName, fmCreate);
  AHttp := THttpClient.Create;
  try
    AUrl := AJob.ExtData.AsString;
    //如果要加入进度提示,可以加上这句话,指定一个回调函数
    //AHttp.OnReceiveData := DoUpdateProgress;
    AReply := AHttp.Get(AUrl, AStream);
    if AReply.StatusCode = 200 then
      ADownloaded := true;
  finally
    FreeObject(AHttp);
    FreeObject(AStream);
    if ADownloaded then
      OpenUrl('file://' + AFileName, 'application/vnd.android.package-archive');
  end;
{$ELSE}
  OpenUrl(AJob.ExtData.AsString);
{$ENDIF}
end;

上面用到的函数,FreeObject 在 QString 单元中有实现,OpenUrl 则在官网的以前的文章:OpenUrl 的跨平台实现,大家自己找下就好。

对于进度提示这块,一个参考代码:

procedure TForm1.DoUpdateProgress(const Sender: TObject;
AContentLength, AReadCount: Int64; var Abort: Boolean);
begin
  if TThread.GetTickCount - FLastUpdateProgressTick > 1000 then//FLastUpdateProgressTick 是类成员变量,记录上次更新提示的时间,每隔1秒更新一次
  begin
    FLastUpdateProgressTick := TThread.GetTickCount;
    //在主线程中显示进度,RunInMainThread 函数在 QWorker 中
    RunInMainThread(
      procedure
      begin
        LabelProgress.Text:='正在下载更新,' + IntToStr(AReadCount *
          100 div AContentLength) + '% 已完成(' + RollupSize(AReadCount) + ')',
          TTextAlign.Center, TTextAlign.Center;
      end);
  end;
end;

至此一个应用的在线升级模块也就完事了,移动平台的在线升级由于是操作系统内置的支持,所以不需要自己象 Windows 下一样完成文件的复制和应用的关闭操作,在升级过程中,它们会自动完成这一切。

分享到: