AppMessage を使用する前に、Pebble C アプリは、受信ボックスと送信ボックスに使用されるバッファを設定する必要があります。これらは、まだ処理されていない受信メッセージと、まだ送信されていない送信メッセージを保存するために使用されます。さらに、メッセージの処理時に発生する成功または失敗イベントにアプリが応答できるように、コールバックを登録できます。これらすべてについては、このガイドで説明します。
AppMessage API を使用して送受信されるすべてのメッセージは、DictionaryIterator 構造に保存されます。これは、本質的に Tuple オブジェクトのリストです。各 Tuple には、そのキーに関連付けられた値に「ラベル」を付けるために使用されるキーが含まれています。
メッセージが送信されると、DictionaryIterator には、送信データの各項目の Tuple が格納されます。逆に、メッセージが受信されると、コールバックによって提供される DictionaryIterator が各キーの存在について調べられます。キーが存在する場合、それに関連付けられた値を読み取ることができます。
Tuple.value 共用体を使用すると、複数のデータ型を受信した各メッセージに保存したり、メッセージから読み取ったりできます。これらの詳細は以下のとおりです:
| Name | Type | Size in Bytes | Signed? |
|---|---|---|---|
| uint8 | uint8_t |
1 | No |
| uint16 | uint16_t |
2 | No |
| uint32 | uint32_t |
4 | No |
| int8 | int8_t |
1 | Yes |
| int16 | int16_t |
2 | Yes |
| int32 | int32_t |
4 | Yes |
| cstring | char[] |
可変長配列 | N/A |
| data | uint8_t[] |
可変長配列 | N/A |
送信ボックスと受信ボックスの各バッファのサイズは、アプリが送受信する最大のメッセージが収まるように選択する必要があります。受信ボックスバッファのサイズを超える受信メッセージ、および送信ボックスバッファのサイズを超える送信メッセージは削除されます。
これらのサイズは、AppMessage システムが「開かれ」、通信が発生できるようになったときに指定されます:
// Largest expected inbox and outbox message sizes
const uint32_t inbox_size = 64;
const uint32_t outbox_size = 256;
// Open AppMessage
app_message_open(inbox_size, outbox_size);
これらの各バッファはこの時点で割り当てられ、アプリのメモリ予算から差し引かれるため、受信ボックスと送信ボックスのサイズは控えめにする必要があります。アプリが処理する最大のメッセージ内のすべてのキーと値のサイズを合計して、必要なバッファのサイズを計算してください。たとえば、3 つの整数キーと値を含むメッセージは、32 バイトのバッファサイズで機能します。
送受信される各メッセージについて、内容は Tuple のキーと値のペアを使用してアクセス可能です。これにより、メッセージ内の各データをそのキーを使用して一意に識別でき、また単一のメッセージ内に多くの異なるデータ型を保存できます。
送信される可能性のある各データには、一意のキー値を割り当てる必要があります。これは、受信したメッセージ内で見つかったときに関連する値を読み取るために使用されます。天気アプリの例を以下に示します:
これらの値は、pebble.h をインクルードする任意のファイルで MESSAGE_KEY_ プレフィックス付きで利用可能になります(MESSAGE_KEY_Temperature や MESSAGE_KEY_WindSpeed など)。
これらのキー値が電話側アプリでどのように使用されるかの例は、PebbleKit JS、PebbleKit iOS (Discontinued)、および PebbleKit Android に示されています。
Pebble C API の他の多くの側面と同様に、AppMessage システムは、開発者定義のコールバックを使用して、正常に送受信されたメッセージや発生する可能性のあるエラーなど、発生する可能性のあるすべてのイベントをアプリが適切に処理できるようにします。
これらのコールバックタイプについては以下で説明します。各コールバックは、まずコールバックタイプのシグネチャと一致する関数を作成し、次にそれを AppMessage システムに登録して、そのイベントタイプが発生したときに呼び出されるようにすることで使用されます。エラーが発生したときにユーザーを修正に導くためにコールバックを適切に使用してアプリの UI を駆動すると、ユーザーエクスペリエンスが向上します。
AppMessageInboxReceived コールバックは、接続された電話から新しいメッセージが受信されたときに呼び出されます。これは、提供された DictionaryIterator を使用してメッセージを読み取り、内容を読み取ってアプリが次に何をするかを決定する瞬間です。例は、Reading an Incoming Message の下に示されています:
static void inbox_received_callback(DictionaryIterator *iter, void *context) {
// A new message has been successfully received
}
このコールバックを登録して、適切なタイミングで呼び出されるようにします:
// Register to be notified about inbox received events
app_message_register_inbox_received(inbox_received_callback);
AppMessageInboxDropped コールバックは、メッセージが受信されたが削除されたときに呼び出されます。これの一般的な原因は、メッセージが受信ボックスに対して大きすぎたことです。失敗の理由は、コールバックによって提供される AppMessageResult を使用して判断できます:
static void inbox_dropped_callback(AppMessageResult reason, void *context) {
// A message was received, but had to be dropped
APP_LOG(APP_LOG_LEVEL_ERROR, "Message dropped. Reason: %d", (int)reason);
}
このコールバックを登録して、適切なタイミングで呼び出されるようにします:
// Register to be notified about inbox dropped events
app_message_register_inbox_dropped(inbox_dropped_callback);
AppMessageOutboxSent コールバックは、Pebble から送信されたメッセージが接続された電話に正常に配信されたときに呼び出されます。提供された DictionaryIterator は、送信されたばかりのメッセージの内容を検査するためにオプションで使用できます。
短時間に複数のメッセージを送信する場合、次のメッセージを送信する前に前のメッセージが送信されるまで待つために、このコールバックを使用することが強く推奨されます。
static void outbox_sent_callback(DictionaryIterator *iter, void *context) {
// The message just sent has been successfully delivered
}
このコールバックを登録して、適切なタイミングで呼び出されるようにします:
// Register to be notified about outbox sent events
app_message_register_outbox_sent(outbox_sent_callback);
AppMessageOutboxFailed コールバックは、送信されたばかりのメッセージが接続された電話に正常に配信されなかったときに呼び出されます。理由は、提供された AppMessageResult の値を読み取ることで判断でき、失敗したメッセージの内容は提供された DictionaryIterator で検査できます。
このコールバックの使用は強く推奨されます。これにより、アプリは失敗したメッセージを検出して、その送信を再試行するか、ユーザーに失敗を通知して、アクションを再試行できるようにすることができます。
static void outbox_failed_callback(DictionaryIterator *iter,
AppMessageResult reason, void *context) {
// The message just sent failed to be delivered
APP_LOG(APP_LOG_LEVEL_ERROR, "Message send failed. Reason: %d", (int)reason);
}
このコールバックを登録して、適切なタイミングで呼び出されるようにします:
// Register to be notified about outbox failed events
app_message_register_outbox_failed(outbox_failed_callback);
メッセージは、DictionaryIterator オブジェクトと Dictionary API を使用して、AppMessage 経由で C アプリから構築および送信されます。メッセージを送受信する前に、app_message_open() が呼び出されていることを確認してください。
最初のステップは、構築中の辞書の状態を追跡するために使用される DictionaryIterator ポインタを準備して、送信メッセージを開始することです:
// Declare the dictionary's iterator
DictionaryIterator *out_iter;
// Prepare the outbox buffer for this message
AppMessageResult result = app_message_outbox_begin(&out_iter);
AppMessageResult をチェックして、送信ボックスが正常に準備されたことを確認する必要があります:
if(result == APP_MSG_OK) {
// Construct the message
} else {
// The outbox cannot be used right now
APP_LOG(APP_LOG_LEVEL_ERROR, "Error preparing the outbox: %d", (int)result);
}
結果が APP_MSG_OK の場合、メッセージの構築を続行できます。データは、Dictionary API を使用してデータ型に応じて辞書に書き込まれます。仮想的な天気アプリの例を以下に示します:
if(result == APP_MSG_OK) {
// A dummy value
int value = 0;
// Add an item to ask for weather data
dict_write_int(out_iter, MESSAGE_KEY_RequestData, &value, sizeof(int), true);
}
すべての必要なデータが辞書に書き込まれたら、メッセージを送信できます:
// Send this message
result = app_message_outbox_send();
// Check the result
if(result != APP_MSG_OK) {
APP_LOG(APP_LOG_LEVEL_ERROR, "Error sending the outbox: %d", (int)result);
}
重要
PebbleKit JS 経由でウォッチから電話にデータを送信したいアプリは、ready イベントが発生するまで待つ必要があります。これは、電話がアプリの JavaScript をロードし、データを受信する準備ができたことを示します。詳細については、Advanced Communication を参照してください。
メッセージ送信操作が完了すると、成功または失敗の結果に応じて、AppMessageOutboxSent または AppMessageOutboxFailed コールバック(登録されている場合)のいずれかが呼び出されます。
送信メッセージの組み立ての完全な例を以下に示します:
// Declare the dictionary's iterator
DictionaryIterator *out_iter;
// Prepare the outbox buffer for this message
AppMessageResult result = app_message_outbox_begin(&out_iter);
if(result == APP_MSG_OK) {
// Add an item to ask for weather data
int value = 0;
dict_write_int(out_iter, MESSAGE_KEY_RequestData, &value, sizeof(int), true);
// Send this message
result = app_message_outbox_send();
if(result != APP_MSG_OK) {
APP_LOG(APP_LOG_LEVEL_ERROR, "Error sending the outbox: %d", (int)result);
}
} else {
// The outbox cannot be used right now
APP_LOG(APP_LOG_LEVEL_ERROR, "Error preparing the outbox: %d", (int)result);
}
接続された電話からメッセージが受信されると、AppMessageInboxReceived コールバックが呼び出され、提供された DictionaryIterator を使用してメッセージの内容を読み取ることができます。これは、各期待される Tuple キー値の存在を調べ、必要に応じて関連する値を使用することによって行う必要があります。
ほとんどのアプリは、整数値または文字列を処理して、それぞれ信号または人間が読める情報を渡します。これらの一般的なユースケースについては、以下で説明します。
From JS
var dict = {
Temperature: 29,
};
In C
static void inbox_received_callback(DictionaryIterator *iter, void *context) {
// A new message has been successfully received
// Does this message contain a temperature value?
Tuple *temperature_tuple = dict_find(iter, MESSAGE_KEY_Temperature);
if(temperature_tuple) {
// This value was stored as JS Number, which is stored here as int32_t
int32_t temperature = temperature_tuple->value->int32;
}
}
送信された文字列の一般的な使用法は、TextLayer に表示することです。表示されるテキストは長期間存在する必要があるため、データが受信されたときに static char バッファを使用できます:
From JS
var dict = {
LocationName: "London, UK",
};
In C
static void inbox_received_callback(DictionaryIterator *iter, void *context) {
// Is the location name inside this message?
Tuple *location_tuple = dict_find(iter, MESSAGE_KEY_LocationName);
if(location_tuple) {
// This value was stored as JS String, which is stored here as a char string
char *location_name = location_tuple->value->cstring;
// Use a static buffer to store the string for display
static char s_buffer[MAX_LENGTH];
snprintf(s_buffer, sizeof(s_buffer), "Location: %s", location_name);
// Display in the TextLayer
text_layer_set_text(s_text_layer, s_buffer);
}
}
パックされたバイナリデータを扱うアプリは、このデータを送信し、必要に応じて両側でパック/アンパックできます:
From JS
var dict = {
Data: [1, 2, 4, 8, 16, 32, 64],
};
In C
static void inbox_received_callback(DictionaryIterator *iter, void *context) {
// Expected length of the binary data
const int length = 32;
// Does this message contain the data tuple?
Tuple *data_tuple = dict_find(iter, MESSAGE_KEY_Data);
if(data_tuple) {
// Read the binary data value
uint8_t *data = data_tuple->value->data;
// Inspect the first byte, for example
uint8_t byte_zero = data[0];
// Store into an app-defined buffer
memcpy(s_buffer, data, length);
}