Delphi Android APP 自动更新: 使用 RADStudio10.3.3 并在 Android 10 测试通过

Delphi Android APP 自动更新: 使用 RADStudio10.3.3 并在 Android 10 测试通过

我的理想 : 只需要将新的 APP 复制到网站的下载地址里, 其他的什么也不想做

编写过程 : 艰难地从 N 个坑里爬出来后, 终于惊险地过关 ^_^

原理:

过程: function CheckInstalled : Boolean;

检查 APP_TIME 与 APK_TIME 的值, 一致就表示已经成功安装, 不一致就继续检查

通过 APP 与 APK 版本检查是否一致, 如果是, 设标志 APP_TIME=APK_TIME, 并删除 APK 文件

非常重要: Options -> Application -> Version Info -> versionName  版本号不变即使最新也不会执行安装

A. 每天的首次开启 APP 时, 则是这样做(必须获取信息成功, 否则每次都是首次)

1.  通过 fso.json 获取 APK 的信息, 执行 2

2.  检查本地 download 目录 APK 文件, 时间不一致就执行3, 否则直接执行4

3.  下载, 成功后修改 apk 文件时间, 与服务器一致, 再执行 4

4.  CheckInstalled 检查是否成功安装, 未成功安装的就调用 APK 安装

B. 每天的再次开启 APP 时, 只执行 CheckInstalled 检查

========================================

开工:  如果你的APP下载地址是  http://127.0.0.1/app/android/myapp.apk

========================================

1. 整理 ASP 服务器 (本人是Win7)

1.1 复制 fso.json 到 ASP 服务器根目录

1.2 修改 IIS 配置

[处理程序映射] - 添加脚本映射

请求路径=*.json

可执行文件=%windir%\system32\inetsrv\asp.dll

名称=JSONClassic

请求限制 设成与 ASPClassic 的一样

[MIME类型] - 添加, (如果已做请忽略)

文件扩展名:   .apk

MIME类型:     application/vnd.android.package-archive

1.3 测试 fso.json 效果   http://127.0.0.1/fso.json?p=/app/android/myapp.apk

结果是:

{"path" : "/app/android/myapp.apk", "filelist" : [{"name" : "myapp.apk", "size" : "123456", "attr" : "32", "time" : "2020/05/22 12:00:00"}]}

就可以了.

======================================

2. 修改你的 APP 项目:

2.1  加入 AndroidUpdating.pas 到你的 APP 项目

2.2  你的 APP 主窗口(TMainForm)引用 AndroidUpdating 单元

uses

...,

AndroidUpdating;

provate

FUpdating : TAndroidUpdating;

//  释放指针过程: NotifyFreeUpdating

procedure TMainform.NotifyFreeUpdating;

begin

FreeAndNil(FUpdating); //  同步释放: (不建议, 等于是在类内部过程中调用释放)

//  异步释放: (建议) 通过 TTimer 来进行, 过程自己去写

end;

//    执行版本检查与自动更新, 写到最后一行比较好

procedure TMainform.FromCreate(Sender : TObject);

begin

...

FUpdating := TAndroidUpdating.Create(Self, 'http://127.0.0.1/app/android/myapp.apk', NotifyFreeUpdating);

end;

2.3  修改 APP 项目设置 (建议在 All Configurations - Android 修改) 以下必须要勾选

Options - Application - Entitlement List - Secure File Sharing

Options - Application - Uses Premissions - Write external stoage

Options - Application - Uses Premissions - Request install packages

2.4 修改 APP 项目文件夹内的 AndroidManifest.template.xml 在 Application 中加入一行

android:usesCleartextTraffic="true"

即:

<application

...

android:usesCleartextTraffic="true">

说明: 如果是 http:// 必须加, 如果是 https:// 就不用, 自动去选

======================================

3. 收工!


{=====================================

功能:  Delphi Android APP Auto Update

版权:  csdn 账号: sczyq  QQ :79896009

授权:  免费, 如果你觉得好, 就给我点赞

=====================================}

unit AndroidUpdating;

interface

uses

System.Classes,

System.Net.URLClient,

System.Net.HttpClient,

System.Net.HttpClientComponent;

type

TAndroidUpdating = class(TComponent)

private

FNetHTTP : TNetHTTPClient;

FHome : String;

FRemoteFile : String;

FLocalFile : String;

FConfigureFile : String;

FConfigure : TStringList;

FNotifyFree : TThreadMethod;

procedure HTTPRequestDownload(const Sender: TObject; const AResponse: IHTTPResponse);

procedure HTTPRequestFileInfo(const Sender: TObject; const AResponse: IHTTPResponse);

procedure HTTPError(const Sender: TObject; const AError: string);

function CheckInstalled : Boolean;

procedure DoNotifyFree;

procedure ExecuteInstall;

public

constructor Create(AOwner : TComponent; APath : String; AFreeEvent : TThreadMethod); reintroduce;

destructor Destroy; override;

end;

implementation

uses

System.Types, System.SysUtils, System.JSON, System.IOUtils,

System.Generics.Collections,

Androidapi.Helpers,

Androidapi.JNI.App,

Androidapi.JNI.Net,

Androidapi.JNI.JavaTypes,

Androidapi.JNI.Support,

Androidapi.JNI.GraphicsContentViewText;

const

APK_DATE = 'apk.date';         //  最后检查日期, 同一天只成功检查一次

APK_TIME = 'apk.time';         //  APK 文件时间, 获取成功后写入

APP_TIME = 'app.time';         //  APP 安装成功后, 校验一至则等于 APK_TIME

constructor TAndroidUpdating.Create(AOwner : TComponent;

APath : String; AFreeEvent : TThreadMethod);

var

I : Integer;

begin

inherited Create(AOwner);

FConfigure := TStringList.Create;

I := APath.IndexOf('://');

if I > 0 then Inc(I,4)

else

begin

APath := 'http://' + APath;

I := 7;

end;

I := APath.IndexOf('/', I);

if I > 0 then

begin

FHome := APath.Remove(I);

FRemoteFile := APath.Remove(0, I);

end

else FHome := APath;

FLocalFile := TPath.Combine(TPath.GetDownloadsPath, ExtractFileName(FRemoteFile));

FConfigureFile := TPath.Combine(TPath.GetHomePath, 'updating.configure');

if FileExists(FConfigureFile) then

FConfigure.LoadFromFile(FConfigureFile);

FNetHTTP := TNetHTTPClient.Create(Self);

FNetHTTP.ConnectionTimeout := 3000;

FNetHTTP.ResponseTimeout := 8000;

FNetHTTP.Asynchronous := True;

FNetHTTP.OnRequestError := HTTPError;

//  当天未获取过更新

if StrToIntDef(FConfigure.Values[APK_DATE], 0) < Trunc(Now) then

begin

FNetHTTP.OnRequestCompleted := HTTPRequestFileInfo;

FNetHTTP.Get(FHome + '/fso.json?p=' + FRemoteFile);

end

else

begin

CheckInstalled;

DoNotifyFree;

end;

end;

destructor TAndroidUpdating.Destroy;

begin

FreeAndNil(FNetHTTP);

FreeAndNil(FConfigure);

inherited;

end;

procedure TAndroidUpdating.HTTPError(const Sender: TObject; const AError: string);

begin

FNetHTTP.OnRequestCompleted := nil;

DoNotifyFree;

end;

procedure TAndroidUpdating.HTTPRequestFileInfo(const Sender: TObject;

const AResponse: IHTTPResponse);

function ItemValue(JSON : TJSONValue; Item : String) : String;

var

V : TJSONValue;

begin

SetLength(Result, 0);

V := TJSONObject(JSON).GetValue(Item);

if Assigned(V) then

Result := V.Value;

end;

var

I : Integer;

JV : TJSONValue;

JA : TJSONArray;

S : String;

IsDownload : Boolean;

begin

IsDownload := False;

FNetHTTP.OnRequestCompleted := nil;

JV := TJSONObject.ParseJSONValue(AResponse.ContentAsString(TEncoding.UTF8));

JV := TJSONObject(JV).GetValue('filelist');

if Assigned(JV) and (JV is TJSONArray) then

begin

JA := TJSONArray(JV);

for I := 0 to JA.Count - 1 do

begin

if SameText(ItemValue(JA.Items[I], 'name'), ExtractFileName(FLocalFile)) then

begin

S := ItemValue(JA.Items[I], 'time');

if S.IndexOf('/') > 0 then

S := StringReplace(S, '/', '-', [rfReplaceAll]);

FConfigure.Values[APK_DATE] := IntToStr(Trunc(Now));

FConfigure.Values[APK_TIME] := S;

FConfigure.SaveToFile(FConfigureFile);

if not SameText(FConfigure.Values[APP_TIME], S) then

begin

if FileExists(FLocalFile) then

begin

S := FormatDateTime('YYYY-MM-DD HH:NN:SS', TFile.GetLastWriteTime(FLocalFile));

if SameText(FConfigure.Values[APK_TIME], S) then

ExecuteInstall

else TFile.Delete(FLocalFile);

end;

if not FileExists(FLocalFile) then

begin

IsDownload := True;

FNetHTTP.OnRequestCompleted := HTTPRequestDownload;

FNetHTTP.Get(FHome + FRemoteFile);

end;

end;

break;

end;

end;

end;

if not IsDownload then

DoNotifyFree;

end;

procedure TAndroidUpdating.HTTPRequestDownload(const Sender: TObject;

const AResponse: IHTTPResponse);

var

F : TFileStream;

D : TDateTime;

begin

FNetHTTP.OnRequestCompleted := nil;

try

F := TFileStream.Create(FLocalFile, fmCreate);

try

AResponse.ContentStream.Position := 0;

F.CopyFrom(AResponse.ContentStream, AResponse.ContentStream.Size);

finally

FreeAndNil(F);

if TryStrToDateTime(FConfigure.Values[APK_TIME], D) then

TFile.SetLastWriteTime(FLocalFile, D);

end;

ExecuteInstall;

except

TFile.Delete(FLocalFile);

DoNotifyFree;

end;

end;

function TAndroidUpdating.CheckInstalled : Boolean;

var

pm : JPackageManager;

InfoP, InfoA : JPackageInfo;

begin

Result := SameText(FConfigure.Values[APP_TIME], FConfigure.Values[APK_TIME]);

if not Result then

if FileExists(FLocalFile) then

begin

pm := TAndroidHelper.Context.getPackageManager();

InfoP := pm.getPackageInfo(TAndroidHelper.Context.getPackageName, 0);

InfoA := pm.getPackageArchiveInfo(StringToJString(FLocalFile), TJPackageManager.JavaClass.GET_ACTIVITIES);

if Assigned(InfoP) and Assigned(InfoA) then

begin

if SameText(JStringToString(InfoP.versionName), JStringToString(InfoA.versionName)) then

begin

FConfigure.Values[APP_TIME] := FConfigure.Values[APK_TIME];

FConfigure.SaveToFile(FConfigureFile);

TFile.Delete(FLocalFile);

Result := True;

end;

end;

end;

end;

procedure TAndroidUpdating.DoNotifyFree;

begin

if Assigned(FNotifyFree) then

FNotifyFree;

end;

procedure TAndroidUpdating.ExecuteInstall;

var

LFile: JFile;

LIntent: JIntent;

LNet_Uri : JNet_Uri;

begin

if not CheckInstalled then

begin

LFile := TJFile.JavaClass.init(StringToJString(FLocalFile));

LIntent := TJIntent.Create;

if TOSVersion.Check(8, 0) then

LIntent.setAction(TJIntent.JavaClass.ACTION_INSTALL_PACKAGE)

else LIntent.setAction(TJIntent.JavaClass.ACTION_VIEW);

LIntent.addFlags(TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK);

if TOSVersion.Check(7, 0) then

begin

LIntent.addFlags(TJIntent.JavaClass.FLAG_GRANT_READ_URI_PERMISSION);

LNet_Uri := TAndroidHelper.JFileToJURI(LFile);

end

else LNet_Uri := TJnet_Uri.JavaClass.fromFile(LFile);

LIntent.setDataAndType(LNet_Uri, StringToJString('application/vnd.android.package-archive'));

TAndroidHelper.Activity.startActivity(LIntent);

end;

DoNotifyFree;

end;

end.

(0)

相关推荐