APLの新機能を使って遅延読み込みリストを作成する方法

Toshimi Hatanaka May 19, 2020
Share:
Build Node.js Optimize Multimodal Intermediate
Blog_Header_Post_Img

※このブログは、Joe Muoio ブログToshimi Hatanakaが日本向けに翻訳し加筆修正したものです。

遅延読み込み(Lazy Loading)とは、はじめにユーザーに表示するのに必要なデータだけを読み込みつつ、このあと表示される可能性のあるデータを、ユーザーがUIを操作している間にバックグラウンドで読み込んでおくという方法です。

これは、ウェブサイトやモバイルアプリでよく使われる手法です。最新のAlexa Presentation Language(APL)バージョン1.3では、遅延読み込みリストのエクスペリエンスを作成することができます。Echo ShowからFacebook Portal(USのみ)まで、APLをサポートするすべてのAlexaマルチモーダルデバイスでは、遅延読み込みリストを作成する機能を含め、バージョン1.3の新機能をサポートしています。

遅延読み込みリストの機能は、データソースの変更(新しく追加されたデータソースタイプ dynamicListIndex)、新しいリクエストタイプ、およびより多くのダイナミックリストを作成するためのディレクティブを使用することでスキルに実装することができます。これらの新機能により、「無限に」スクロールしたり、デバイス上で動的にリスト項目の値を更新(項目の追加と削除を含む)するような、項目数の多いリストをスムーズに表示するエクスペリエンスをユーザーに提供することができます。

このブログ記事では、APL 1.3でのデータソースの変更点を解説し、遅延読み込みリストを作成するための新しいディレクティブの使い方を紹介します。コードを見るのが好きな方は、GitHub.comのAlexa cookcook APL example, skill-demo-lazy-load-lists をチェックしてみてください。このデモでは、カラーを選択するAlexaスキルで遅延読み込みリストの機能を使用しています。

 

Color picker gif
APL 1.3 データソースの変更点

遅延読み込みリストの作成に入る前に、APLデータソースの変更点について説明します。

APLでは、データソースによって、デバイス上のAPLドキュメントの一部にデータをバインドすることができます。古いバージョンのAPLでは、objectデータソースか、型付けされていないデータソースしかありませんでした。Objectデータソースは、ssmlToSpeechトランスフォーマーやtextToHintトランスフォーマーなどのトランスフォーマーで使用します。型付けされていないデータソースは、APLドキュメントにバインドしたいすべてのデータ用に使用され、payload という名前のパラメーターを通してAPLドキュメントへ渡されます。

以前のバージョンのAPLでは、レスポンスのAPLデータソースオブジェクト全体がこのpayloadパラメータにバインドされており、データをpayload.[YOUROBJECTKEYS_HERE] のような書式で参照していました。既に作成済みのAPLドキュメントは後方互換性のため、まだ機能しますが、APL 1.3以降では、データソースのトップレベル変数の名前をAPLドキュメントに渡すよう変更するのが良いでしょう。

例えば、以前のバージョンのAPLドキュメントでは以下のように記述していました。

Copied to clipboard
{
  "type": "APL",
  "version": "1.2",
  "mainTemplate": {
    "parameters": [
        "payload"
    ],
    "item": {
      "type": "Text",
      "text": "${payload.displayText.helloText}"
    }
  }
}

データソース

Copied to clipboard
{
    "displayText": {
        "helloText": "hello world!"
    }
}

APL 1.3 では同じデータソースに対してAPLドキュメントは以下のように記述することができます。

Copied to clipboard
{
  "type": "APL",
  "version": "1.3",
  "mainTemplate": {
    "parameters": [
        "displayText"
    ],
    "item": {
      "type": "Text",
      "text": "${displayText.helloText}"
    }
  }
}

APLのバージョン番号の変更以外に、2つの変更点があることに注目してください。1つ目は、mainTemplateのパラメータ名です。トップレベルのオブジェクトを参照し、これをAPLドキュメントに渡しています。2つ目は、その参照方法です。APLはデフォルトでデータソース全体をpayloadパラメータにバインドしなくなったので、その参照をすべて削除することができます。以前のAPLドキュメントは、APLのすべてのバージョンでまだ動作しますが、新しいデータソースのdynamicIndexListをpayloadパラメータと組み合わせて使用することはできません。

パラメータ名にpayloadを使用すると、ドキュメント内で参照するデータソース全体がpayloadパラメーターにバインドされ、新しく追加されたデータソース型で提供される新機能は無視されます。payload以外のパラメータ名を使用した場合、APLはAPL RenderDocumentディレクティブのdatasourcesセクションでそれらのパラメータを探します。今後、新しいAPLドキュメントを作成する場合は、名前付きのデータソースを使用してください。

DynamicIndexList を使ってみる

APL 1.3では、新しいタイプのデータソースdynamicIndexListが追加されました。これにより、動的なデータソースと遅延読み込みリストを作成するために必要なツールが提供されます。

以下は、dynamicListIndex データソースの例です。

 

Copied to clipboard
"datasources": {
    "colorDynamicSource": {
        "type": "dynamicIndexList",
        "listId": "DYNAMIC_INDEX_LIST_ID",
        "startIndex": 0,
        "minimumInclusiveIndex": 0,
        "maximumExclusiveIndex": 130,
        "items": [
           {
               "index": 0,
               "name": "Alice Blue",
               "value": "#F0F8FF"
           },
            ...,
            {
               "index": 9,
               "name": "Brown",
               "value": "#A52A2A"
           }
        ]
    }
}

この例では、colorDynamicSource という名前のdynamicIndexListデータソースを表していて、インデックス(index)が 0 から始まる 130 色のリストの最初の10色を定義しています。 これは、前方にスクロールするリストを作成し、minimumInclusiveIndex と maximumExclusiveIndex にバインドされます。もし、後方にスクロールするリストを作成したい場合は、開始インデックスよりも小さい値を設定します。例えば、インデックスにリストの真ん中あたりの数(80など)を設定し、同じバウンディングインデックスを使用すると、 前方にも後方にもスクロールするリストが作成されます。

一度バウンディングインデックスがデバイスから要求されると、デバイスはそれ以上のデータを要求しなくなり、ユーザはそれ以上スクロールすることはできなくなります。これらの各フィールドの詳細については、 dynamicIndexList の技術文書を参照してください。

遅延読み込みリストを作成する方法

遅延読み込みリストは dynamicIndexList データソースが必要です。さらに、遅延読み込みリストを作成するには、ページャーやシーケンスの dataプロパティに dynamicIndexList をバインドする必要があります(コンテナはdataプロパティを持ってはいますが、遅延読み込みリストはサポートされておらず、他の動的データ・ソース機能でサポートされています)。必要に応じて、Alexa 対応のマルチモーダルデバイスでは、loadIndexListDataRequest を使用して新しいデータのリクエストを送信します。

以下に例を示します。

Copied to clipboard
{
  "request": {
    "type": "Alexa.Presentation.APL.LoadIndexListData",
    "requestId": "amzn1.echo-api.request.1",
    "timestamp": "2020-03-26T19:47:02Z",
    "locale": "en-US",
    "token": "YOUR_RENDER_DOCUMENT_TOKEN",
    "correlationToken": "SYSTEM_GENERATED_TOKEN",
    "listId": "DYNAMIC_INDEX_LIST_ID",
    "startIndex": 20,
    "count": 10
  }
}

ここで、いくつか注意点があります。

  • デバイスは、開始インデックスと、表示に必要な新しいアイテムの数を要求してきます。これは、ユーザーがスクロールしようとしたがデータがまだ読み込まれていないという状況と、リストのアイテムをスクリーンに表示する際に、ビューポートのどれくらいのスペースを必要とするかに基づいて算出されます。これらの要求はすべて、リストのインデックスの最小値と最大値にバインドされます。デバイスは、データソースのインデックスの範囲外のアイテムは要求しません。
  • リクエストと送信元のAPLドキュメントを一致させるため token という名前のトークンを記入します。
  • データソースを指定するため listId という名前のトークンを定義します。ここには先の dynamicIndexList データソース内でリストを定義する際に記述した識別子と同じものを記入します。
  • さらに correlation token というトークンもあります。これは、デバイスがレスポンスとリクエストを関連づけるために使用します。


    トークンがたくさんあるということは、ドキュメントごとに複数のリストを持つことができ、適切に応答するハンドラーを書くことができるということです。ハンドラーでは、新しいディレクティブ SendIndexListData を使って応答を返します。

    レスポンスのJSONは以下のようになります。

Copied to clipboard
{
    "directives": [
        {
            "type": "Alexa.Presentation.APL.SendIndexListData",
            "token": "YOUR_RENDER_DOCUMENT_TOKEN",
            "correlationToken": "SYSTEM_GENERATED_TOKEN",
            "listId": "DYNAMIC_INDEX_LIST_ID",
            "startIndex": 20,
            "minimumInclusiveIndex": 0,
            "maximumExclusiveIndex": 130,
            "items": [
                {
                    "index": 70,
                    "name": "Lime Green",
                    "value": "#32CD32"
                },
                ...,
                {
                    "index": 78,
                    "name": "Medium Spring Green",
                    "value": "#00FA9A"
                },
                {
                    "index": 79,
                    "name": "Medium Turquoise",
                    "value": "#48D1CC"
                }
            ]
        }
    ]
}

startIndex の項目はリクエストの数と一致させ、デバイスがリクエストした数と同じ数のレスポンスを返す必要があります。

correlationToken はリクエストと一致している必要があります。minimumInclusiveIndex  maximumExclusiveIndex のインデックスは、以前のレスポンスで送信されている場合は不要です。この遅延読み込みリストのディレクティブと動的データソースの UpdateIndexListData ディレクティブの両方を組み合わせることで、複雑なマルチモーダルエクスペリエンスを作成することができます。

サンプルコード「カラーピッカー」を試す

GithubのAlexa-cookbookにサンプルコードがあります。ここではそのデモを紹介します。

タッチスクロールで400色以上ある色のリストをブラウズし、タッチまたは音声で好みの色を選択します。すると、選択した色がリストの一番上に表示され、ヘッダーの色が選択した色に更新されます。また、ヘッダー部分で sequence か pager のどちらかをタッチして選択すると、APLドキュメントのメインコンポーネントが、シーケンスコンポーネントまたはページャーコンポーネントに切り替わります。 この時、一方のドキュメントから別のドキュメントに新しいRenderDocumentが送信されています。

このサンプルでは色の情報は colors.json というファイルから読み込んでいますが、外部のAPIサービスからデータを読み込むシナリオも考えられます。その場合、スキルが一度にすべてのデータをリクエストするよりも、遅延読み込みリストを利用するメリットが多くなります。特にページごとに分割してデータを取得できるAPIサービスが利用できる場合には便利です。 外部のAPIサービスを使用する場合、ある程度の待ち時間が発生します。データの一部を取得するのに一度だけ呼び出しを行い、あとはデバイスからそれ以降のデータを複数回に分けてAPIコールさせることで、待ち時間を短縮し、ユーザーエクスペリエンスを向上させることができます。

サンプルコードでは、新機能の APL エラーリクエストもデモしています。Node.js のコードの中に APLRuntimeErrorHandlerというハンドラーがあります。これは、Alexa Skills Kit (ASK) SDK を使用してリクエストハンドラを書くのと全く同じように書かれていて、何らかのエラーが発生した場合には、そのエラーログを残し空のレスポンスを返しています。状況によっては、エラーに基づいてここから行き先を修正するレスポンスを送信することも可能です。すべてのエラーケースのリストはこちらの技術文書に掲載されています。

GitHub.com の README.md にサンプルスキルをデプロイしてテストするための手順が記載されています。ご自身のAlexa開発者アカウントでサンプルスキルをデプロイし動作を確認してみてください。サンプルスキルを改造して、あなたならではのマルチモーダルスキルのプロトタイプを作ってみてはいかがでしょうか?

関連ドキュメント

Subscribe