【问题标题】:Delphi 10 Seattle Background Service and ThreadsDelphi 10 Seattle 后台服务和线程
【发布时间】:2016-02-17 04:19:14
【问题描述】:

使用 Delphi 10 Seattle Update 1 创建 Android 应用程序。

基本目标是让应用每隔几 (4) 小时弹出一次通知,提醒用户起床和移动。

我已经创建了基本 UI 并创建了后台服务。在服务中,我可以使用 TNotificationCenter 来发布通知,但我需要定期发布通知。

根据在以下网站上找到的建议...

我意识到我不能在 Android 服务中使用 TTimer。我还尝试从 AndroidServiceStartCommand 和 AndroidServiceCreate 事件创建 TTask、TThread.CreateAnonymousThread 和老式 TThread 后代。

在 sleep 命令完成后,它们都会抛出相同的错误。 《Project MoveProof.apk 引发异常类Segment fault(11)》

这是我现在使用的代码......

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.

我对线程没有那么丰富的经验,所以也许我在同步方面做错了。任何帮助将不胜感激。

【问题讨论】:

  • 您不需要为此提供服务。只需使用AlarrmManager。就个人而言,为什么您会费心在 Delphi 中创建新应用程序? AndroidStudio 是免费的,完全受支持且易于查找帮助。对于跨平台开发,有更好的工具可用。 FWIW,我从第一版(1995 年)开始在 Delphi 中开发。这只是移动设备的错误工具。
  • Segfault(11) 相当于 Windows 上的访问冲突。您正在访问无效的内存。你为什么打电话给JavaService.stopSelf()?为什么要在Synchronize()'ed 过程中运行while 循环?
  • @323go - AlarmManager 看起来是要走的路。我开发桌面 OSX/Win 应用程序,所以我已经有了 Delphi,而且我刚刚开始学习移动设备,所以我现在坚持靠近“家”。
  • @RemyLebeau - while 刚刚从较早的尝试中遗留下来。应该删除它。
  • @323go 我也更喜欢 Android Studio,但是在工作中我们有很多 delphi 库(需要在客户端运行的函数),XE 允许你在你的 android 应用程序中使用它们,所以它是是我们公司的自然选择。无需重新发明轮子。

标签: android multithreading delphi service firemonkey


【解决方案1】:

无需将您的 DoNotification 放入线程中。只需在你的 AndroidServiceCreate 中设置一个带有永恒循环的 TTask,然后检查经过的时间,当它通过时,然后调用 DoNotification。

  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);

您的通知看起来比它需要的更复杂,您在那里使用的不必要的循环可能是您的分段错误的原因。

  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;

Delphi 中 Android 服务的良好经验法则是编写最简单的代码来完成工作,仅此而已。

【讨论】:

【解决方案2】:

正如 323go 建议的那样,您不需要服务来执行此操作。

仅应在绝对需要时使用服务,因为它会消耗系统资源并耗尽电池寿命,因此仅在需要时使用它们。

您可以使用本机 AlarmManager.setRepeating 方法,该方法将在您指定的自定义间隔内创建本机重复通知,但是解决方案是特定于 Android 的

参考我的回答Custom notification interval

请注意,对于您的情况,MyNotification.RepeatIntervalinMills 是您希望通知触发的间隔(以 毫秒 为单位),因此在 4 小时的情况下,它将是

MyNotification.RepeatIntervalinMills := 14400000 // That is 4 hours in Milliseconds 

【讨论】:

  • 仅仅设置一个自定义间隔对我来说真的不起作用,因为我每次都需要一条不同的消息,以及一个用来做其他工作的事件。AlarmManager 看起来确实不错,但我不能find 任何 Delphi 示例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多