Android の StartService のアプリを Delphi で作ってみた

Android にはバッググラウンドで UI 無しで動作するサービスという仕組みがあります

スレッドはアプリケーションが動作している間だけ、裏で別の作業を行なうのですが、サービスはアプリケーションが動作していないときでも独立して裏で動作することができます

[Android Developer の Android Service のドキュメント](https://developer.android.com/guide/components/services?hl=ja)を読むと、サービスには StartService と BindService の２種類があります

StartService - アプリケーションによって開始され、実行(処理)が完了した際には、自身で停止する必要がある
BindService - 他から使われている限り実行し続け、誰からも使われなくなったら、サービスを破棄する

この記事では StartService を使ったアプリを Delphi で作ります
使用したのは RAD Studio で、バージョンは 10.2.3 Tokyo Enterprise Edition です
もちろん Delphi の Community Edition でも作成できます

## どんなアプリを作るの？
アプリ側でボタンを押したらサービスが起動するアプリ
サービス側では通知を行い StartService なので、自身で停止します
Android の通知については、[Delphi で Android Notification (通知)](https://qiita.com/CYonezawa/items/ced7afc16e401870fad5) をご参照くださいませ

作成の手順は
1. メインのアプリの画面を作成する
2. サービスを作成する
3. メイン側にサービスを呼び出す処理を追加する
4. メイン側のアプリにサービスを追加する
5. アプリを実行する
です

## メインのアプリの画面を作成する
新規アプリケーションを作成して、ボタンを配置するだけの簡単なお仕事
![service02.png](https://qiita-image-store.s3.amazonaws.com/0/164551/50ca7808-6a49-db03-1840-6bc257c34555.png)

## サービスを作成する
プロジェクトグループに追加という形で作成します
##### 1. 上記のメインの設計画面が表示されている状態で、プロジェクトマネージャ上で、プロジェクトグループ名(ProjectGroup1など)を選択して、マウスの右ボタンをクリックします
表示されたポップアップメニューから「新規プロジェクトを追加」を選択します
![service01.png](https://qiita-image-store.s3.amazonaws.com/0/164551/79d5dc3b-2b1a-4e62-e8eb-2ca5ef10d15f.png)

##### 2. 表示された新規作成のダイアログ上で、Delphi プロジェクトの Android サービスを選択して[OK]ボタンをクリックします
![service04.png](https://qiita-image-store.s3.amazonaws.com/0/164551/4946befd-f782-c008-449a-6f046be22ce6.png)

##### 3. Android サービスのダイアログが表示されます
ローカルサービスを選択して [OK]ボタンをクリックします
![service05.png](https://qiita-image-store.s3.amazonaws.com/0/164551/2be247d6-0bd5-c6d7-68c0-6f445b5280e3.png)

##### 4. 新しいプロジェクトが追加されます
追加されたプロジェクトのユニットファイル（ここでは Unit2）のタブをクリックして、設計画面にします
##### 5. 画面上はデータモジュールのようなコンテナ画面（TDM: TAndroidService）で、今回は通知を行なうので、ここに非ビジュアルコンポーネントである TNotificationCenter コンポーネントをツールパレットからドラックアンドドロップして貼り付けます
![service03.png](https://qiita-image-store.s3.amazonaws.com/0/164551/01ac0262-760a-aeb0-05e0-7ef00e4845e8.png)

##### 6. コンテナである DM(TDM) の OnStartCommand のイベントにサービスが起動された際の処理を記述します
また　StartService なので、自身で停止するコードも記述します

```pascal
// uses に Androidapi.Jni.app を追加すること
function TDM.AndroidServiceStartCommand(const Sender: TObject;
  const Intent: JIntent; Flags, StartId: Integer): Integer;
var
  mNotice: TNotification;
begin
  // サービスの開始
  Sleep(15000);  // 15秒待ってから通知する

  // 通知
  mNotice := NotificationCenter1.CreateNotification;
  mNotice.Title := 'サービスから通知';
  mNotice.AlertBody := 'チュミミーン';
  NotificationCenter1.PresentNotification(mNotice);

  // 自身で終了
  JavaService.stopSelf;

  // StartService は result を返す
  Result := TJService.JavaClass.START_NOT_STICKY;
  mNotice.DisposeOf;
end;
```

15 秒待たせたのは、単にサービスが動いていることを証明するスクショを取りたかっただけですw

##### 7. すべて保存して、ビルドを行なっておきます (実行ではない)
サービス側はコレで完了です

## メイン側にサービスを呼び出す処理を追加する
設計画面の Unit1 側のタブをクリックして、メイン側に切り替えておきます

##### 1. サービスのユニットをメイン側に追加する
メニューから「ファイル|使用するユニット」を選択
![service06.png](https://qiita-image-store.s3.amazonaws.com/0/164551/8cb61421-2182-3730-7697-18e946e6e720.png)

unit2 を選択して「OK」をクリックします
![service07.png](https://qiita-image-store.s3.amazonaws.com/0/164551/19edd72e-ee6d-477d-a954-47112da748cb.png)

##### 2. Button1 の OnClick イベントにサービスを呼び出す処理を記述します
サービスを呼び出すために

- uses に System.Android.Service を追加します
- private 宣言に、TLocalServiceConnection の宣言を追加します
- OnClick イベント内にサービスを呼び出す処理を記述します

サンプルコードは以下です

```pascal
unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  FMX.Controls.Presentation, FMX.StdCtrls, System.Android.Service;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { private 宣言 }
    mService: TLocalServiceConnection;  // 追加
  public
    { public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

uses Unit2;

procedure TForm1.Button1Click(Sender: TObject);
begin
// uses に System.Android.Service を追加を忘れずに

// Service 起動
// StartService で指定するのはプロジェクト名で OK
  mService := TLocalServiceConnection.Create;
  mService.StartService('Project2');
end;

end.
```

##### 3. 保存してビルドしてエラーが無いかを確認しておきます
メイン側もコレで完了
実行するとデバイスに配置されてしまうので、実行ではなくビルドで

## メイン側のアプリにサービスを追加する
サービス側はメインのアプリと共に配布するので、メイン側にサービスを追加するという作業が必要です
これもメニューが用意されているので、容易に行うことができます
Android のマニフェストファイルへの追加も行なってくれるので便利便利

##### 1. プロジェクトマネージャ上でメインのプロジェクトのターゲットプラットフォームの中の Android プラットフォームを選択します
マウスの右ボタンをクリックして「Android サービスの追加」を選択します
![service08.png](https://qiita-image-store.s3.amazonaws.com/0/164551/ad831e88-07d1-ea87-a8ce-6646430b4eb7.png)

##### 2. 「新規 Android サービスの追加」のダイアログが表示されます
デフォルトの「プロジェクトの基底パスからファイルを自動的に検索する」を選択して「次へ」をクリックします
![service09.png](https://qiita-image-store.s3.amazonaws.com/0/164551/134efbe5-4c95-8b0d-1a56-4a0a04a5bcd3.png)

##### 3. 「Andorid サービスの選択」の画面が表示されます
サービス（ここでは Project2）のあるフォルダを選択して「次へ」をクリックします
![service11.png](https://qiita-image-store.s3.amazonaws.com/0/164551/3fbf58b2-fb1b-8d3f-b736-ef3e3410ad9e.png)

##### 4. 「変更の確認」の画面が表示されます
必要なファイルが自動検索できた場合は、この画面が表示されますので「終了」をクリックします
![service14.png](https://qiita-image-store.s3.amazonaws.com/0/164551/dd14f0eb-188c-d12d-c0ce-115982719331.png)

必要なファイルが自動検索できなかった場合は、足らない部分を喚起するダイアログが表示されます
![service12.png](https://qiita-image-store.s3.amazonaws.com/0/164551/91dfc063-714d-c917-bf7d-e055bbb7eb4a.png)

ので、足りなかった箇所を補って「終了」をクリックします
![service13.png](https://qiita-image-store.s3.amazonaws.com/0/164551/88bfe0c6-bbae-99d2-6148-a7488f3c010a.png)

**注意: メイン側とサービス側を同じフォルダに保存している場合は、サービス側のプロジェクトファイル等が設定されているかを確認します（メイン側が指定されている場合がある)**

これで、メイン側のアプリにサービスの追加が完了です

#### Android のマニフェストファイル (AndroidManifest.xml) をみてみると
service タグに、サービスのプロジェクトである com.embarcadero.services.Project2 の記述があります

```xml
<service android:name="com.embarcadero.services.Project2" android:exported="false"/>
```

#### 配置されるものを確認すると
サービス追加前
![service10.png](https://qiita-image-store.s3.amazonaws.com/0/164551/9fb9b233-321b-27ad-3f86-b4848f520031.png)

サービス追加後
![service15.png](https://qiita-image-store.s3.amazonaws.com/0/164551/010144c8-63d9-81f1-bf49-9a141edf98ca.png)

ここでも Project2 (libProject2.so)が追加されているのが分かります

## アプリを実行すると

メイン側のアプリを起動して、ボタンをタップするとサービスが起動します
![Screenshot_20180828-115233.png](https://qiita-image-store.s3.amazonaws.com/0/164551/7aa6aedd-c643-4f99-b593-9a2008a7ee9d.png)

このアプリでは 15秒経過すると通知を送って自身は停止します（実行中のサービスリストから消えます）
通知を送っているので、左上に通知のアイコンが出ています
![Screenshot_20180828-115245.png](https://qiita-image-store.s3.amazonaws.com/0/164551/1d7219a6-13b0-aadf-067e-98644e2c2355.png)

通知を確認するとサービスからの通知があります
![Screenshot_20180828-115306.png](https://qiita-image-store.s3.amazonaws.com/0/164551/84fe3605-f772-23d9-0ce0-eef4225f45c0.png)

## ドキュメント(docwiki)

- [Android サービスの作成](http://docwiki.embarcadero.com/RADStudio/Tokyo/ja/Android_%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%81%AE%E4%BD%9C%E6%88%90)
- [Android サービス](http://docwiki.embarcadero.com/RADStudio/Tokyo/ja/%EF%BC%BBAndroid_%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%EF%BC%BD)