How to detect the termination of a program in Andr

2020-07-23 06:09发布

问题:

When a Windows program terminates it calls event handlers like OnClose, OnDestroy and the destructor Destroy. When I want to save some INI settings these are the places to be. I wrote event handlers for all these events but they are not processed when I terminate the program.

Does anyone know where I should place the code to be executed when an Android program terminates? I strongly suspect that this applies to iOS as well.

Update

Johan's answer works for Android as well, although reality is slightly more complicated than his examples. The nice thing was that it forced me into TApplicationEvents, something I'd never heard of. As is custom not documented by Embarcadero but the code of FMX.Platform is interesting enough. Several ApplicationEvents are defined of which three seem of interest: aeEnteredBackground, aeWillBecomeInactive and aeWillTerminate. As they are not documented I presumed that they did what their names suggested: signalling that a background state has been reached, that it wil start to go to background and that it wil (very) soon terminate. I adapted Johan's code as follows:

  function TForm2.AppEvent (AAppEvent: TApplicationEvent; AContext: TObject) : Boolean;
  begin
  // do something here for when the app is sent to background
     case AAppEvent of
     (1)   TApplicationEvent.aeEnteredBackground:  ;// Something for OnDeactivated
                                                    // which does not exist
     (2)   TApplicationEvent.aeWillBecomeInactive: if Assigned (OnDeactivate)
                                                      then OnDeactivate (Self);
     (3)   TApplicationEvent.aeWillTerminate:      if Assigned (OnClose)
                                                      then OnClose (Self);
     end; // case
     Result := True; // let iOS/Android know it worked...
  end; // AppEvent //

When I label the events 1, 2 and 3 experiments with the debugger showed the following: forcing the application to the background generates a sequences of events: 2, 1, 1, 2. Once I even got 2, 2, 1, 1, 2, 2. If your code should be executed once, then take your precautions. But Better is: the aeWillTerminate does what it advertizes: it sends a signal when the application is terminated. Time to do so is likely to be brief and I will test whether it suffices to write a TIniFile.

I tried this code in Win32 as well and that does not work. The AppEvent is not fired. That forces me to test the code immediately on my tablet which takes some time. Pity.

回答1:

In iOS applications seldom close but enter a background mode.
This is why the OnClose event does not fire. I suspect that killing an app by clicking on the 'x' in the taskmanager actually forcefully terminates the app, but haven't tested this. in any case this use case is too rare to code against.
In Android things work pretty much the same.

Luckily Anders Ohlsson has written a very informative blog post about this subject, see here: http://blogs.embarcadero.com/ao/2013/05/01/39450.
The following post builds on that to catch the actual backgrounding https://forums.embarcadero.com/message.jspa?messageID=558241

The trick is to register for application events. See: http://docwiki.embarcadero.com/Libraries/XE5/en/FMX.Platform.TApplicationEvent

Some sample code for iOS don't have Android handy, sorry.
Copy from the above forum:

unit Unit1;

interface

uses
  System.SysUtils, System.Classes, FMX.Forms, FMX.Platform;

type
TForm1 = class(TForm)
  procedure FormCreate(Sender: TObject);
private
public
  function AppEvent(AAppEvent: TApplicationEvent; AContext: TObject) : Boolean;
end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

function TForm1.AppEvent(AAppEvent: TApplicationEvent; AContext: TObject) : Boolean;
begin
  if AAppEvent = TApplicationEvent.aeEnteredBackground then begin
    // do something here for when the app is sent to background
  end;
  Result := True; // let iOS know it worked...
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  AppEventSvc: IFMXApplicationEventService;
begin
  if TPlatformServices.Current.SupportsPlatformService(IFMXApplicationEventService, IInterface(AppEventSvc)) then
    AppEventSvc.SetApplicationEventHandler(AppEvent);
end;

end. 

Obviously these events should have triggered sensible event handlers in FMX.Platform.TApplication but they don't. http://docwiki.embarcadero.com/Libraries/XE5/en/FMX.Forms.TApplication_Events
Perhaps you should extend TApplication to add these eventhandlers so that sanity can be preserved.
I recommend filing a QA report.

Here's a suggestion for the extended TApplication class.

type
  TnotifyApplication = class(FMX.Platfrom.TApplication) 
  private
    FOnStop: TnotifyEvent;
  protected
    procedure AppEvent(AAppEvent: TApplicationEvent; AContext: TObject): boolean;
    procedure SetOnStop(value: TNotifyEvent);
    procedure DoOnStop;
  public
    property OnStop: TNotifyEvent read FOnStop write SetOnStop;
  end;

implementation

procedure TNotifyApplication.SetOnStop(value: TNotifyEvent);
begin
  if Assigned(value) then begin  
    //register for the notification to call AppEvent
  end else begin
    //
  end;
end;

procedure TNotifyApplication.DoOnStop;
begin
  if Assigned(FOnStop) then FOnStop(self);
end;

procedure TNotifyApplication.AppEvent(AAppEvent: TApplicationEvent; AContext: TObject) : Boolean;  
begin
  //call the relevant do... Call depending in the exact event. 


回答2:

When the user leaves your activity(Screen), the system calls onStop() to stop the activity. If the user returns while the activity is stopped, the system calls onRestart(), quickly followed by onStart() and onResume(). Hence writing the code and placing it in the body of onStop() method is recommended.

http://developer.android.com/images/training/basics/basic-lifecycle-stopped.png