Skip to content

Instantly share code, notes, and snippets.

@lvctr
Last active April 30, 2022 10:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lvctr/f16cf6575270776b48697dc00cc5746e to your computer and use it in GitHub Desktop.
Save lvctr/f16cf6575270776b48697dc00cc5746e to your computer and use it in GitHub Desktop.
pepper-tickle-issues-ja.md

pepper-tickleの移植にあたって直面した問題

Pepperくすぐりアプリを昔のPythonベースからAndroid/Javaへ移植しようとしてた時、非同期的にSayアクションの処理の実装にあたって複数の問題に直面しました。

Futureに関する混乱

Futureとは、ある処理を非同期的に包むオブジェクトであり、利用すると非同期的処理を手続き型に表せるようになります。

Pepper APIの公式ドキュメンテーションだと、複数のFutureを一つのメソッドに利用すればアクションをチェーンすることができるという例はたくさんありますが、pepper-tickleの場合だと、同じFutureを複数のメソッドに利用せざるをえなくて、Atomic変数を使用することにしました。

Atomic変数に関する混乱

先ほど書いた通り、普段だと一つのFutureを複数のメソッドで呼び出すことは不可能です。この場合、並行的な問題を処理してくれるAtomic変数を利用するのが一番です。

Pepper APIだとAtomic変数の使用に関して一切ドキュメントされていませんが、幸いに、Future変数をAtomicレファレンスにキャストすればFuture変数を複数の非同期メソッドで共有することが可能になります。

Pepper APIにあたってFutureとAtomic変数の処理

1. Future変数をAtomicレファレンスにをキャストする

慣習的に、あるアクションのためのFutureの定義はこのようになります:

Future<Void> sayFuture;

ですが、このやり方ですとそのFutureを非同期メソッドに呼ぼうとしたらエラーが返されます。使用可能にするには、このようにAtomicレファレンスにキャストします:

AtomicReference<Future<Void>> sayFuture;

2. チェーンとキャンセルを処理するヘルパーメソッドの実装

pepper-tickleのユースケースだと、一つのSay関数を使用して色んなフレーズを言わせることが必要だったため、実行する度にキャンセルすることができるようにするのが必要でした。

そのため、まずフレーズを引数としてこの関数を書きました。新しいスレッドにSayアクションが実行されるようになっています。

public Future<Void> speakNow(QiContext qiContext, Phrase phrase) {
    Future<Say> say = SayBuilder.with(qiContext).withPhrase(phrase).buildAsync();
    Future<Void> sayFuture = null;

    try {
        sayFuture = say.get().async().run();
    } catch (ExecutionException | InterruptedException e) {
        e.printStackTrace();
    }

    return sayFuture;
}

次に、この関数を実際のコードベースに実装します。頭のタッチセンサーと連携します。

headTouchSensor.addOnStateChangedListener(touchState -> {
    Log.i("TAG", "Sensor " + (touchState.getTouched() ? "touched" : "released") + " at " + touchState.getTime());
    if (touchState.getTouched()) {
        String randomHand = whichHand();
        gameRunning.set(true);
        sayFuture.get().cancel(true);
        sayFuture.set(speakNow(qiContext, phraseStartGame));
        Log.i("TAG", "Game is now running, correct hand is " + randomHand);
    }
});

ここでは、また一つAtomic変数AtomicReference<Boolean> gameRunningを使ってくすぐりゲームが実行中かどうかを確認します。sayFuture.get().cancel(true);を実行してまず実行中のSayアクションをキャンセルし、sayFuture.set(speakNow(qiContext, phraseStartGame));を実行してSayアクションをFutureにパスして非同期で実行します。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment