Pepper SDK Plugin導入から始まり、簡単なロボアプリの開発を行ってきました。今回は、QiSDKの基本となるRobot focus と Robot lifecycleについて説明します。
#1.フォーカスを理解する
まずはロボットフォーカスについての簡単な説明です。 Activityがロボットのアクションを実行するためには、ロボットフォーカスが必要です。ロボットフォーカスはフォアグラウンドのActivityだけが保持することができ、ロボットフォーカスオーナーと呼ばれます。ロボットフォーカスはQiSDKに管理されており、Activityはいつでもロボットフォーカスを獲得したり喪失したりする可能性があります。
#2.ロボットライフサイクル
Pepper SDK入門(2) はじめてのロボアプリ開発①で登場したロボットライフサイクルについてです。 QiSDKがそれぞれのActivity用のロボットフォーカスを渡すために、RobotLifecycleCallbacksインタフェースを実装したオブジェクトが必要になります。以下はActivityにRobotLifecycleCallbacksインタフェースを実装した例です。
public class MyActivity extends RobotActivity implements RobotLifecycleCallbacks
また、RobotLifecycleCallbacksオブジェクトにコールバックさせるにはonCreateメソッドでActivityとRobotLifecycleCallbacksオブジェクトをQiSDKに登録する必要があります。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
QiSDK.register(this, this);
}
その上で、onDestroyメソッドでの登録解除も必要です。
@Override
protected void onDestroy() {
QiSDK.unregister(this, this);
super.onDestroy();
}
#3.ロボットフォーカスの獲得
ロボットフォーカスの獲得に関して詳述します。 ロボットフォーカスはActivityがフォアグラウンドに遷移したタイミングでActivityに与えられ、QiSDKからRobotLifecycleCallbacksオブジェクトのonRobotFocusGainedメソッドがコールバックされます。
void onRobotFocusGained(QiContext qiContext);
警告
このコールバックはバックグラウンドスレッドで実行されているため、UIコンポーネントが内部で直接操作できません。UI thread上でコードを実行するには、下記の7.UI threadでの処理を参照してください。
以下が許可されているQiContextです。
- アクションの作成
Say say = SayBuilder.with(qiContext)
.withText("こんにちは")
.build();
- アクション用リソースの作成
Animation animation = AnimationBuilder.with(qiContext)
.withResources(R.raw.elephant_a001)
.build();
- ロボットサービスの取り扱い
HumanAwareness humanAwareness = qiContext.getHumanAwareness();
このコールバックで、アクションを実行することが出来ます。
- アクションの実行
say.run();
say.async().run();
#4.ロボットフォーカスの喪失
ロボットフォーカス喪失について詳述します。 ロボットフォーカスを喪失したActivityと一緒に登録されたRobotLifecycleCallbacksオブジェクトのonRobotFocusLostメソッドがコールバックされます。
void onRobotFocusLost();
警告
このコールバックはバックグラウンドスレッドで実行されているため、UIコンポーネントを内部で直接操作できません。UI thread上でコードを実行するには、下記の7.UI threadでの処理を参照してください。
アクションの影響
ロボットフォーカスを喪失した場合、このActivityはロボットフォーカスを再獲得するまでPepperのアクションを実行できません。
//ロボットフォーカスを喪失している場合、実行されません。
say.run();
また、アクションの実行中にonRobotFocusLostが呼ばれた場合、実行中のアクションは停止し例外がスローされます。
//例外がスローされます。
say.run();
say.async().run().thenConsume(future -> {
if (future.isSuccess()) {
//コールされません。
} else if (future.hasError()) {
//コールされます。
}
});
ロボットフォーカスなしでコールバックされるリスナーは、onRobotFocusLostメソッドで出来るだけ解除しておきましょう。
if (lookAt != null) {
lookAt.removeAllOnPolicyChangedListeners();
}
ロボットサービスの影響
ロボットサービスはロボットフォーカスの喪失に影響されず実行を継続します。
Future<List<Human>> humansAroundFuture = humanAwareness.async().getHumansAround();
humanAwareness.addOnHumansAroundChangedListener(listener);
ロボットフォーカスなしでコールバックされるリスナーは、onRobotFocusLostメソッドで出来るだけ解除しておきましょう。
if (humanAwareness != null) {
humanAwareness.removeAllOnHumansAroundChangedListeners();
}
ロボットサービスに設定したリスナーを解除し忘れた場合には、アプリが終了するまで実行されることになります。
#5. ロボットフォーカス拒否
ロボットフォーカス拒否について詳述します。 ロボットが不安定な時などは、Activityがフォアグラウンドに遷移したにもかかわらずロボットフォーカスが与えられない場合があります。その場合、このActivityと一緒に登録したRobotLifecycleCallbacksオブジェクトのonRobotFocusRefusedメソッドがコールバックされます。
void onRobotFocusRefused(String reason);
ロボットフォーカスが与えられなかった原因は、onRobotFocusRefusedメソッドの引数で通知されます。
#6. 制約事項
制約事項についてです。 onRobotFocusGained、あるいはonRobotFocusRefusedのコールバックは保証されていません。onRobotFocusGainedやonRobotFocusRefusedを待ち続けることのないよう、必ずタイムアウトを実装しましょう。
#7.UIスレッドでの処理
UIスレッドでの処理に関して説明します。 Robot lifecycleの各メソッドで画面更新を行いたい場合には、2つの方法があります。
Androidベースの解決策
ActivityクラスのrunOnUiThreadメソッドにRunnableオブジェクトを渡すと、UIスレッドでの処理の実行が可能になります。
@Override
public void onRobotFocusGained(QiContext qiContext) {
Say say = SayBuilder.with(qiContext)
.withText("こんにちは")
.build();
say.run();
runOnUiThread(() -> textView.setText("完了"));
}
QiSDKベースの解決策
QiSDKクラスのonUiThreadメソッドにConsumerオブジェクトを渡すと、UIスレッドで処理を実行できるようになります。
@Override
public void onRobotFocusGained(QiContext qiContext) {
Say say = SayBuilder.with(qiContext)
.withText("こんにちは")
.build();
Future<void> sayFuture = say.async().run();
sayFuture.andThenConsume(Qi.onUiThread((Consumer<void>) ignore -> {
textView.setText("完了");
}));
}
#8.Activityライフサイクルとのリンク
Activityライフサイクルとロボットライフサイクルのリンクに関して説明します。
ロボットライフサイクルは完全にActivityライフサイクルとリンクしているわけではありません。しかしAとBの2つのActivityインスタンスがある時には、次のような条件でActivity Aがロボットフォーカスを喪失します。
- フォアグラウンドのActivityがAからBに遷移したとき
- アプリがバックグラウンドに遷移したとき
- アプリが終了させられたとき
重要
Activityがバックグラウンドに遷移した場合は、必ずロボットフォーカスを失います。