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

「[Android の StartService のアプリを Delphi で作ってみた](https://qiita.com/CYonezawa/items/0e8bd216d53668511cb8)」を書いたので、もうひとつの BindService も書かないと

BindService は StartService と同様にアプリによって開始されます
BindService でサービスに接続して、UnBindService でサービスとの接続を解除します
StartService が自身で停止するのと違い、BindService の場合は、他から使われている限り実行し続け、誰からも使われなくなったら、サービスを破棄します
参考: [Android Developer - Service](https://developer.android.com/guide/components/services?hl=ja)

## どんなアプリを作るの？

[Android の StartService のアプリを Delphi で作ってみた](https://qiita.com/CYonezawa/items/0e8bd216d53668511cb8) で作ったアプリ・サービスを修正・拡張します
修正・拡張するので、メインのアプリにサービスを登録する作業を再度行なう必要はありません
修正・拡張の内容ですが

- メイン側のアプリは BindService のボタンを押すとサービスを開始し、BindService でそのサービスに接続します
- メイン側のアプリは UnBindService のボタンを押すと、サービスとの接続を解除します
- サービス側は、BindService の機能を追加しますが、やることは同じで通知を行ないます
- サービス側では解除＝サービス停止とします

## メイン側の画面の拡張
ボタンを１個、新しく配置するだけの簡単なお仕事
分かりやすいように、「BindService」「UnBindService」と Text プロパティを変更
![BindService01.png](https://qiita-image-store.s3.amazonaws.com/0/164551/2e4dcf35-a121-b27c-fb03-11802c9a77d9.png)

## メイン側の動作の拡張
「BindService」のボタンのクリックイベントを次のように修正します

```pascal
procedure TForm1.Button1Click(Sender: TObject);
begin
  // Service 起動
  if mService = nil then begin
    mService := TLocalServiceConnection.Create;
    mService.StartService('Project2');
  end;

  // Service に Bind
  mService.BindService('Project2', 0);
end;
```

「UnBindService」のボタンのクリックイベントを次のように記述します

```pascal
procedure TForm1.Button2Click(Sender: TObject);
begin
  if mService <> nil then begin
    mService.UnbindService;  // Service との接続解除
    mService := nil;
  end;
end;
```

## サービス側の拡張
DM(TDM) に新たに OnBind イベントと、OnUnBind イベントを追加
![BindService02.png](https://qiita-image-store.s3.amazonaws.com/0/164551/db54b327-8723-ed4c-48ba-27b96624e97c.png)

OnStartCommand イベントのコードを次のように修正します 

```pascal
function TDM.AndroidServiceStartCommand(const Sender: TObject; const Intent: JIntent; Flags, StartId: Integer): Integer;
begin
  // サービスの開始
  // StartService は result を返す
  Result := TJService.JavaClass.START_STICKY;
end;
```

OnBind イベントのコードを次のように記述します

```pascal
function TDM.AndroidServiceBind(const Sender: TObject; const AnIntent: JIntent): JIBinder;
var
  mNotice: TNotification;
begin

  // BindService として通知の実行
  mNotice := NotificationCenter1.CreateNotification;
  mNotice.Title := 'BindService通知';
  mNotice.AlertBody := 'メッシャアーッ';
  NotificationCenter1.PresentNotification(mNotice);
  mNotice.DisposeOf;

  // IBinder オブジェクトを戻り値として返す
  Result := GetBinder;
end;

```

OnUnBind イベントのコードを次のように記述します

```pascal
function TDM.AndroidServiceUnBind(const Sender: TObject; const AnIntent: JIntent): Boolean;
begin
// このサンプルでは解除＝停止
// サービスの停止
  JavaService.stopService(AnIntent);
  Result := False;
end;
```

## 動かしてみる
まずはアプリ起動（この画面を撮っていなかったのに後から気付いたため、この画面ショットだけ時系列がズレていますw）
![Screenshot_20180904-112723.png](https://qiita-image-store.s3.amazonaws.com/0/164551/99853a1d-53d6-cc04-9982-4ba568d1f439.png)

BindService をタップするとサービスが起動し、そのサービスに接続して「通知」が行なわれます
![Screenshot_20180904-111717.png](https://qiita-image-store.s3.amazonaws.com/0/164551/3ad7493c-9f18-4bd9-0498-67b4477b17e5.png)

通知内容
![Screenshot_20180904-111725.png](https://qiita-image-store.s3.amazonaws.com/0/164551/b55b0a87-afd8-5c1b-a181-18faa47b9afd.png)

UnBindService をタップするとサービスとの接続を解除して、サービスを停止します
![Screenshot_20180904-111804.png](https://qiita-image-store.s3.amazonaws.com/0/164551/08cc03b7-97da-752a-957a-e050fc23e289.png)

BindService のボタンを続けて２回タップしても通知は１つだけです
これは、まだ Bind が解除されていないので OnBind イベントが発生しないのです
UnBindService のボタンをタップしてから、BindService のボタンをタップすると、OnBind イベントが発生し通知が行なわれます

## 参考
BindService のライフサイクル
[Android Developer](https://developer.android.com/guide/components/bound-services?hl=ja) より

![alt](https://developer.android.com/images/fundamentals/service_binding_tree_lifecycle.png?hl=ja)

## あとがき

Delphi で Android サービスを作る際に 4 つの選択

- ローカルサービス
- インテントローカルサービス
- リモートサービス
- インテントリモートサービス

が出てきます

「ローカル」と「リモート」の違いは、
ローカル - そのアプリだけで使用できる private のサービス
リモート - public であり、他のアプリからアクセスすることができる
です

「インテントxxxxサービス」は、サービスで [IntentService](http://developer.android.com/reference/android/app/IntentService.html) を使用したい場合に選択します
IntentService はバッググラウンドで特定のタスクを実行し、ワーカスレッドでタスクキューを受け取り順に処理します
サービスの停止はキュー内の作業が全て終わったときに自動的に行なわれるので、明示的に停止する必要はありません