程序永远与 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 更新是直接跳到应用商店来完成更新)。
- 版本检查文件:应用需要下载此文件与本地的文件进行比较以确定是否需要更新,为了方便我们可以用 Json 格式来定义更新的应用的信息,比如:
- 第二关:版本比较
- 在手机应用启动后获取版本检查文件,然后与本地应用的版本进行比较,以确定是否需要升级。我是通过 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。
- 在手机应用启动后获取版本检查文件,然后与本地应用的版本进行比较,以确定是否需要升级。我是通过 QWorker 提交了一个后台的异步作业,通过 THttpClient 来从服务器端获取版本检查的 JSON,然后将取回的 JSON 与应用的当前版本进行比较来确定是否需要升级的。这里提供一个关键函数,获取应用的当前版本号,至于版本比较,就自己写下就好。
- 第三关:下载并安装升级包
- Android
- 使用 THttpClient 下载升级包,这里我们使用 THttpClient 在后台异步下载升级包
- 使用系统自带的安装包管理器安装新版本
- iOS
- 直接使用 OpenUrl 打开链接会自动跳转到应用商店去完成更新
- Android
这里我们给出一段参考代码:
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 下一样完成文件的复制和应用的关闭操作,在升级过程中,它们会自动完成这一切。