1

Using Delphi 10 Seattle Update 1 to create an Android application.

Basic goal is to have an application the pops up a notification every few (4) hours to remind the user to get up and move.

I have created the basic UI and created the background service. In the service I can use a TNotificationCenter to post a notification just fine, but I need to post a notification at a regular interval.

Based on suggestions found at the following sites...

I realized that I cannot use a TTimer in and Android Service. I have also tried to create a TTask, TThread.CreateAnonymousThread, and an old fashioned TThread descendant from both the AndroidServiceStartCommand and the AndroidServiceCreate event.

All of them throw the same error after the sleep command finishes. "Project MoveProof.apk raised exception class Segment fault (11)"

Here is the code I am using now....

unit UnitServiceMain;

interface

uses
    System.SysUtils
  , System.Classes
  , System.Android.Service
  , System.Notification

  , AndroidApi.JNI.GraphicsContentViewText
  , Androidapi.JNI.Os

  ;

type
  TServiceThread = class;

  TNotes = Array of String;

  TAndroidServiceDM = class(TAndroidService)
    NotificationCenterNotes: TNotificationCenter;
    function AndroidServiceStartCommand(const Sender: TObject; const Intent: JIntent; Flags, StartId: Integer): Integer;
    procedure AndroidServiceCreate(Sender: TObject);
    procedure AndroidServiceDestroy(Sender: TObject);
  private
    FNotes: TArray<string>;
    FThread: TServiceThread;
  public
    var Running: Boolean;

    Procedure LoadArray;
    Property Notes:TArray<string> read FNotes;
  end;

  TServiceThread = class(TThread)
  public
    Procedure Execute; override;
    Procedure DoNotification;
  end;

var
  AndroidServiceDM: TAndroidServiceDM;

implementation

{%CLASSGROUP 'FMX.Controls.TControl'}

{$R *.dfm}

Uses
    System.Threading
  , Androidapi.JNI.App
  ;

{ TServiceThread }

procedure TServiceThread.DoNotification;
Var
  NoteId: Integer;
  MyNotification: TNotification;
begin
  while (AndroidServiceDM <> nil) and AndroidServiceDM.Running do
    Begin
      try
        if (AndroidServiceDM <> nil) and AndroidServiceDM.Running then
          Begin
            AndroidServiceDM.NotificationCenterNotes.CancelAll;
            NoteID := Random(High(AndroidServiceDM.Notes));
            MyNotification := AndroidServiceDM.NotificationCenterNotes.CreateNotification;
            try
              MyNotification.Name := 'LoveNoteMessage'+InttoStr(NoteID);
              MyNotification.EnableSound := False;
              MyNotification.Number := NoteID;
              MyNotification.Title := 'Michael Said...';
              MyNotification.AlertBody := AndroidServiceDM.Notes[NoteID];
              AndroidServiceDM.NotificationCenterNotes.PresentNotification(MyNotification);
            finally
              MyNotification.DisposeOf;
            end;
          End;
      except
        on Exception do
          // Need to log this...
      end;
    end;
end;

procedure TServiceThread.Execute;
begin
  inherited;
  Sleep( 20000 );
  Synchronize(DoNotification);
end;

procedure TAndroidServiceDM.LoadArray;
begin
  if Length(FNotes) = 0 then
    Begin
      FNotes := TArray<string>.Create
      (
        'Get up and move.',
        'Time to keep moving.',
        'Lets take a walk.',
        'Move.'
      );
    End;
end;

procedure TAndroidServiceDM.AndroidServiceCreate(Sender: TObject);
begin
  Randomize;
  LoadArray;
end;

procedure TAndroidServiceDM.AndroidServiceDestroy(Sender: TObject);
begin
  FThread.Terminate;
  FThread := Nil;
end;

function TAndroidServiceDM.AndroidServiceStartCommand(const Sender: TObject; const Intent: JIntent; Flags, StartId: Integer): Integer;
begin
  JavaService.stopSelf;
  Result := TJService.JavaClass.START_STICKY;
  if not Running then
    begin
      Running := True;
      FThread := TServiceThread.Create(False);
    end;
end;


end.

I am not that experienced with threads, so maybe I am doing something wrong with synchronization. Any help would be appreciated.

Community
  • 1
  • 1
  • 2
    You don't need a service for this. Just use [AlarrmManager](http://developer.android.com/reference/android/app/AlarmManager.html). On a personal note, *why* would you bother creating new apps in Delphi? AndroidStudio is free, fully supported and easy to find help for. For cross-platform development, there are much better tools available. FWIW, I've developed in Delphi since version one (1995). It's simply the wrong tool for mobile. – 323go Feb 17 '16 at 04:32
  • Segfault(11) is the equivilent of an access violation on Windows. You are accessing invalid memory. Why are you calling `JavaService.stopSelf()`? And why are you running a `while` loop inside a `Synchronize()`'ed procedure? – Remy Lebeau Feb 17 '16 at 06:17
  • @323go - AlarmManager looks like it is the way to go. I develop Desktop OSX/Win apps so I already have Delphi and i am just starting to learn mobile, so I am sticking close to "home" for now. – Michael Galalher Mar 01 '16 at 05:05
  • @RemyLebeau - the **while** was just left over from an earlier attempt to get something to work. Should have removed it. – Michael Galalher Mar 01 '16 at 05:06
  • 2
    @323go I also prefer Android Studio, but at work we have a lot of delphi libraries (functions that need to run at the client side) and XE lets you use them in your android application, so it's a natural choice for our company. No need to re-invent the wheel. – nurettin May 11 '17 at 07:27

2 Answers2

0

There's no need to put your DoNotification in a thread. Just setup a TTask with an eternal loop in your AndroidServiceCreate, and then check the elapsed time, and when it's passed, call the DoNotification then.

  T := TTask.Run (procedure
  begin
    TimeNow := Now;
    Count := 0;
    while true do
    begin
      sleep(20000);
      if SecondsBetween(TimeNow, Now) >= Floor(4 * 60 * 60) then
      begin
        DoNotification;
        TimeNow := Now;
      end;
    end;
  end);

Your notification looks more complicated than it needs to be, and the unnecessary loop you used there might be the cause of your segmentation fault.

  myNotification := NotificationCenter1.CreateNotification;
  try
    MyNotification.Name := 'ServiceNotification';
    MyNotification.Title := 'Android Service Notification';
    MyNotification.AlertBody := 'hello host, I'm your service'
    MyNotification.FireDate := Now;
    NotificationCenter1.PresentNotification(MyNotification); 
  finally
    myNotification.DisposeOf;
  end;

The good rule of thumb for Android services in Delphi is to write the simplest code you can to get the job done and no more.

Freddie Bell
  • 2,186
  • 24
  • 43
-1

You don't need a service to do this, as 323go suggested.

a service should only be used when it is definitely needed because it consumes system resources and drains battery life, so use them on a need to only basis.

You can use the native AlarmManager.setRepeating method that will create a native recurring notification in a custom interval specified by you, the solution however is Android specific

refer to my answer here Custom notification interval

Just note that for your case that MyNotification.RepeatIntervalinMills is the interval in Milliseconds that you want your notifications to fire in so in your case of 4 Hours it would be

MyNotification.RepeatIntervalinMills := 14400000 // That is 4 hours in Milliseconds 
Community
  • 1
  • 1
Peter-John Jansen
  • 603
  • 1
  • 7
  • 26
  • Just setting a custom interval doesn't really work for me because I need a different message each time, and an event to do other work in. AlarmManager does look like the way to go, but I cannot fins any Delphi examples yet. – Michael Galalher Mar 01 '16 at 05:08