Pepper SDK入門(6) アクション

ロボットフォーカスとロボットライフサイクルに関しては、前回学習しました。今回はPepperのアクションについて掘り下げます。

#Pepperでのアクション実行
Pepperは、話す・聞く・動く・アニメーションを再生する・人間を見つめる・チャットを行う・視線を動かす・特定の地点に留まる・写真を撮るといった動作が可能です。アプリを開発する際、これらのアクションがメインの構成要素になります。

**前提条件**
このページで説明しているアクションの利用方法を理解するためには、Javaの非同期やスレッドに関する知識が必要です。

##1.アクションの作成
ビルダーを用いて、アクションを同期もしくは非同期で作成します。以下が各アクションをビルドする手順です。

1.アクションのビルダーにQiContextを渡す
2.ビルダーへアクションのパラメータを渡す
3.buildメソッドかbuildAsyncメソッドを呼び、アクションを作成する

**同期**
アクションを同期で作成するため、ビルダーのbuildメソッドを呼びます。

```java

Say say = SayBuilder.with(qiContext) 
                    .withText("こんにちは") 
                    .build();
```

**警告**
アクションを同期で作成した際はPepperとタブレットが通信するため、UI上で実行するとNetworkOnMainThreadExceptionがスローされる可能性があります。

**非同期**
アクションを非同期で作成するため、ビルダーのbuildAsyncメソッドを呼びます。

```java

Future<say>sayActionFuture = SayBuilder.with(qiContext) 
                                        .withText("Hello!")
                                        .buildAsync();
```

アクションを非同期で作成すると、非同期処理と連携するためのFutureインスタンスが取得できます。

##2.アクションの実行
1で作成したアクションを実行してみましょう。

**同期**
アクションを同期で実行するには、runメソッドを使用します。

```java

say.run();
```

**警告**
アクションを同期で作成した際はPepperとタブレットが通信するため、UI上で実行するとNetworkOnMainThreadExceptionがスローされる可能性があります。

**非同期**
アクションを非同期で実行することもできます。

```java

Say.Async sayAsync = say.async();
sayAsync.run();
```

asyncメソッドでAsyncオブジェクト取得し、Asyncオブジェクトのrunメソッドを呼ぶことで非同期で実行されます。一行で書くと以下のようになります。

```java

say.async().run();
```

**非同期の結果(処理の説明)** 非同期と同期で、以下の違いがあります。

- 非同期でアクションを実行すると、スレッドはブロックされない。
- 非同期でアクションを実行すると、runを呼んでからすぐには終わらない。

#アクションの実行管理
上記で各アクションを同期と非同期で実行したことにより、次のことが可能になりました。

- アクション結果の獲得
- エラーハンドリング
- アクションのキャンセル

##1.アクション結果の獲得
アクションの実行方法が分かったところで、アクションが成功した場合について見ていきます。

**同期**
アクションを同期で実行し、結果を受け取りたい場合にはrunメソッドを使用します。

```java

Void ignore = say.run();
Log.i(TAG, "Say action finished with success.");
```

ここではSayが戻り値を持たないためVoidクラスですが、アクションによっては値を返す場合もあります。

**非同期**
アクションを非同期で実行する際にはrunメソッドがFutureを返すため、Futureを保持しておく必要があります。

```java

Future<void>sayFuture = say.async().run();
```

FutureクラスのandThenConsumeメソッドにより、非同期で実行したアクションの結果を受け取ることが出来ます。

```java

Future<void>sayFuture = say.async().run();
sayFuture.andThenConsume(ignore -> {
    Log.i(TAG, "Say action finished with success.");
});
```

asyncメソッドを使用すればボタンをクリックした時にPepperに発話させるなど、UIスレッド上から簡単にバックグラウンドで非同期アクションを実行させることが可能です。Qi.onUiThreadメソッドを使用すると、アクション終了時にUIスレッド上で操作することができます。

```java

sayFuture.andThenConsume(Qi.onUiThread((Consumer<void>) ignore -> {
    Toast.makeText(MainActivity.this, "Say action finished with success.", Toast.LENGTH_SHORT).show();
}));
```

##2.エラー処理
アクションを実行する際には常にエラー発生の可能性があり、その対処法はアクションの実行方法に依存します。

**同期**
アクションを同期で実行した場合、RuntimeExceptionがスローされるかもしれません。onRobotFocusGainedメソッドでこれが発生した場合、runメソッドを呼んだところで実行は停止してしまうため、try-catchブロックでエラーを管理しましょう。

```java

try {
    say.run();
    Log.i(TAG, "Success");
} catch (Exception exception) {
    Log.e(TAG, "Error", exception);
}
```

**非同期**
アクションを非同期で実行した場合、アクションの中のエラーを取り扱うにはthenConsumeメソッドを使用しましょう。Futureクラスにはアクションが非同期実行中にエラーがないかチェックするhasErrorメソッドがあります。

```java

Future<void>sayFuture = say.async().run();
sayFuture.thenConsume(future -> {
    if (future.isSuccess()) {
        Log.i(TAG, "Success");
    } else if (future.hasError()) {
        Log.e(TAG, "Error", future.getError());
    }
});
```

##3.アクションのキャンセル
アクションをキャンセルしたい場合の操作についてです。 該当するFutureクラスのrequestCancellationメソッドを呼ぶことでアクションをキャンセルします。

```java

Future<void>sayFuture = say.async().run();
sayFuture.requestCancellation();
```

FutureクラスのthenConsumeメソッドを使用している場合には、isCancelledメソッドでアクションがキャンセルされているかどうか確認できます。

```java

Future<void>sayFuture = say.async().run();
sayFuture.thenConsume(future -> {
    if (future.isSuccess()) {
        Log.i(TAG, "Success");
    } else if (future.isCancelled()) {
        Log.i(TAG, "Cancelled");
    }
});
sayFuture.requestCancellation();
```

#複数のアクションを使用する
以下で複数のアクションを使用する場合について確認していきます。

##1.複数のアクションを連続実行する
ここでも同期、非同期に分けてそれぞれの方法を解説します。

**同期**
アクションを同期で連続実行してみましょう。

```java

Say sayHello = SayBuilder.with(qiContext)
                         .withText("こんにちは")
                         .build();
Say sayPepper = SayBuilder.with(qiContext)
                          .withText("私はPepperです")
                          .build();
sayHello.run();
sayPepper.run();
```

最初のアクションが成功すると、2つ目のアクションが実行されるはずです。

**非同期**
FutureクラスのthenComposeとandThenComposeで、複数のアクションを非同期で連続実行できます。

```java

Say sayHello = SayBuilder.with(qiContext)
                         .withText("こんにちは")
                         .build();
Future<void> sayHelloFuture = sayHello.async().run();
sayHelloFuture.andThenCompose(ignore -> {
    Say sayPepper = SayBuilder.with(qiContext)
                              .withText("私はPepperです")
                              .build();
    return sayPepper.async().run();
});
```

##2.アクションを同時に実行する
Pepperに複数のアクションを同時に実行させたい場合、アクションを非同期で続けて実行してください。ただし複数の発話や、発話と聞き取りなどは同時に実行することが出来ません。

```java

Say say = SayBuilder.with(qiContext)
                    .withText("こんにちは")
                    .build();
Animate animate = AnimateBuilder.with(qiContext)
                                .withAnimation(animation)
                                .build();
say.async().run();
animate.async().run();
```