Palm Programmer's Laboratory

トップ 差分 一覧 ソース 検索 ヘルプ RSS ログイン

Palm OS Programmer's Companion Volume I/2-6

← 5 節に戻る ↑2 章トップへ 7 節に進む →


2-6 ノティフィケーション

 
ノティフィケーションフィーチャセットがサポートされているシステムであれば、アプリケーションはシステムレベルまたはアプリケーションレベルのイベントが発生したときにノティフィケーションを受け取ることで起動することができます。ノティフィケーションはアプリケーションの起動コードと同等ですが、起動コードとは2つの重要な点において異なります。

  • ノティフィケーションはシェアードライブラリやシステム拡張(例えば HackMaster によってインストールされたハックなど)のようなコードリソースにも送ることができます。起動コードはアプリケーションにしか送ることができません。ノティフィケーションを受信するように登録されたコードリソースはノティフィケーションクライアントと呼ばれます。
  • ノティフィケーションはそれを受信するように特別に登録されたアプリケーションやコードリソースにのみ送られます。これは起動コードよりも効率的です。多くの起動コードはそれぞれに応答するチャンスを与えるために、インストールされている全てのアプリケーションに対して送られます。

Palm OS システムとビルトインのアプリケーションは、特定のイベントが発生したときにノティフィケーションを発行しています。ノティフィケーションの一覧については、「 2-10 ノティフィケーションの要約 」 を参照して下さい。

アプリケーションで独自のノティフィケーションを作成してブロードキャストすることも可能です。しかし、そうすることは稀でしょう。それよりも既存のノティフィケーションを受信するように登録したり、sysNotifyHelperEvent( 「 2-7 ヘルパー・ノティフィケーション 」参照 )をブロードキャストすることの方が多いはずです。

ノティフィケーションの使用においては、一般的に3種類のイベントフローがあります。

シングルコンシューマー

イベントの発生を受けた各クライアントが、それぞれの方法で処理を行ないます。この際、パラメータブロック内の情報には変更を加えません。

コラボレーティブ

ノティフィケーションのパラメータブロックには handled フラグが含まれています。クライアントはこのフラグをセットすることで、他のクライアントが受信可能な状態を保ったままイベントが処理済みであることを伝えることができます。この例が Palm VII シリーズのハンドヘルドにおける sysNotifyAntennaRaisedEvent です。クライアントはアンテナキーダウンイベントを処理する場合があり、その場合はフラグをセットして他のクライアントにイベントが処理されたことを伝えます。

コレクティブ

各クライアントはノティフィケーションのパラメータブロックに情報を追加することができます。この情報は全てのクライアントのために蓄積されます。この種のノティフィケーションは、例えば各クライアントにメニューテキストを追加させることでダイナミックにメニューを作成するような使い方ができます。sysNotifyMenuCmdBarOpenEvent はこの方式に似ています。

 

ノティフィケーションの受信登録

イベント発生時にノティフィケーションを受信するためには、SysNotifyRegister 関数を使用して登録をしなければなりません。一度登録をしてしまえば、システムがリセットされるか SysNotifyUnregister を使用して明示的に解除するまで受信登録は有効です。

アプリケーションが HotSync の通知を受けるための登録は、リスト 2.4 のような関数呼び出しになります。

リスト 2.4 ノティフィケーションへのアプリケーションの登録

SysNotifyRegister(myCardNo, appDBID, sysNotifySyncStartEvent,
                  NULL, sysNotifyNormalPriority, myDataP);

アプリケーションではなくシェアードライブラリを書いていて HotSync イベントの通知を受けたければ、SysNotifyRegister の呼び出しが少し異なります。リスト 2.5 を参照して下さい。

リスト 2.5 ノティフィケーションへのシェアードライブラリの登録

SysNotifyRegister(myCardNo, shlibDBID,
                  sysNotifySyncStartEvent, SyncNotifyHandler,
                  sysNotifyNormalPriority, myDataP);

SysNotifyRegister 関数に渡すパラメータは、以下の要領で指定します。

  • 最初の2つのパラメータは prc ファイルのカード番号とデータベース ID です。アプリケーションがアクセスしているレコードデータベースのローカルIDを渡さないように注意して下さい。アプリケーションのローカルIDよりもレコードデータベースのローカルIDの方が頻繁に使用されるため、このミスは良く起こりがちです。
  • HotSync の開始時に通知を受けたければ sysNotifySyncStartEvent を指定します。HotSync 終了を通知するものとしては、sysNotifySyncFinishEvent があります。
  • 次のパラメータは、ノティフィケーションをどのように受信するかを指定します。リスト 2.4 とリスト 2.5 の違いはここです。アプリケーションはこのパラメータに NULL を指定することで、アプリケーション起動コード sysAppLaunchCmdNotify 経由で通知を受信するように指示します。他の全ての起動コード同様、システムはアプリケーションの PilotMain 関数にこの起動コードを渡します。シェアードライブラリは PilotMain 関数を持たないため、起動コードを受けとることができません。そのため、このパラメータにコールバック関数のポインタを渡します。コードに PilotMain 関数がない場合のみコールバック関数を使用して下さい。また、コールバック関数を指定する場合でも prc ファイルのカード番号とデータベース ID を常に指定する必要があることに注意して下さい。
  • sysNotifyNormalPriority は、ノティフィケーションを受けとる時に特別な扱いを受けたくないという意味です。ノティフィケーションはプライオリティ順に同時にブロードキャストされます。このパラメータに小さな値を指定すると、早く通知を受けとることになります。事実上常に、sysNotifyNormalPriority を使うべきでしょう。もし、どうしても特定の順番( 全体の中で最初の方または最後の方 )で通知を受けとる必要があるのであれば、システムや他のアプリケーションの処理と衝突しないよう、プライオリティ値にある程度のスペースを開けておかなければなりません。指定可能範囲の最大値や最小値は絶対に使用しないで下さい。一般的に、Palm は最少桁のビットがゼロであるような値( 32, 64, 96 など )を推奨しています。
  • myDataP はノティフィケーションハンドラ関数でアクセスする必要のあるデータのポインタです。ほとんどの起動コード同様、sysAppLaunchCmdNotify の処理ではグローバル変数にアクセスできませんから、必要なデータはこのポインタを使って自分で渡す必要があります。

リスト 2.4 とリスト 2.5 の呼び出しの後であれば、システムが HotSync を開始する際、両方のクライアントに sysNotifySyncStartEvent が通知されます。

アプリケーションは起動コード sysAppLaunchCmdNotify を経由して通知を受け取ります。この起動コードのパラメータブロックは SysNotifyParamType 構造体で、ノティフィケーション名、ブロードキャスト元、および登録時に指定したポインタ( 上の例における myDataP )が含まれています。ノティフィケーションによっては、この構造体の notifyDetailsP フィールドに追加の情報を格納しています。HotSync に関する通知では、notifyDetailsP フィールドは使用されません。

シェアードライブラリは SyncNotifyHandler 関数の呼び出しによって通知を受け取ります。この関数にも起動コードの場合と同じ SysNotifyParamType 構造体が渡されます。

IMPORTANT
コールバック関数のポインタは関数をダイレクトに呼び出すのに使用されるため、ポインタは SysNotifyRegister 呼び出し時点からノティフィケーションがブロードキャストされるまで有効でなければなりません。関数がシェアードライブラリ内にある場合、ライブラリをオープンしておく必要があります。関数が個別にロードされるコードリソース内にある場合、ノティフィケーションの登録中はリソースをロックしておく必要があります。ライブラリをクローズしたりリソースをアンロックする場合、先にノティフィケーションの登録を解除して下さい。登録を解除しないと、ノティフィケーションがブロードキャストされた時にシステムがクラッシュしてしまいます。

 

ノティフィケーションハンドラの作成

アプリケーションの sysAppLaunchCmdNotify への応答とシェアードライブラリのコールバック関数は、ノティフィケーションハンドラと呼ばれます。ノティフィケーションハンドラはユーザーインターフェースの表示や他のノティフィケーションのブロードキャストを含め、必要な処理を行ないます。

ユーザーインターフェースを表示する場合、他のアプリケーションがノティフィケーションを受信するのをブロックする可能性を考慮して下さい。この理由により、モーダルフォームを表示したりユーザーの応答を待つ必要のあるようなことをするのは通常良い考えではありません。また、多くのノティフィケーションは SysHandleEvent の処理中にブロードキャストされます。このことは、自分でユーザーインターフェースを表示できるか、あるいはスタックがオーバーフローするまでイベントループが先に進まないことを意味します。

もしノティフィケーションハンドラで長々と処理をしたいのであれば、他のイベントをブロックしないようにする方法の1つは、自分で繰り越しのノティフィケーションを発行することです。例として、リスト 2.6 に sysNotifyTimeChangeEvent ノティフィケーションに対するハンドラを示します。このハンドラでは、繰り越しのノティフィケーション(myDeferredNotifyEvent)を登録してブロードキャストをスケジュールすること以外は何もしません。アプリケーションが myDeferredNotifyEvent を受けとると、時刻の変更を処理する実際の関数である MyNotifyHandler を呼び出します。

リスト 2.6 ハンドラ内でのノティフィケーションの繰り越し

case sysAppLaunchCmdNotify :
    if (cmdPBP->notify->notifyType == sysNotifyTimeChangeEvent) {
        SysNotifyParamType notifyParm;
        MyGlobalsToAccess myData;

        /* initialize myData here */

        /* Create the notification block. */
        notifyParam.notifyType = myDeferredNotifyEvent;
        notifyParam.broadcaster = myCreatorID;
        notifyParam.notifyDetailsP= NULL;
        notifyParam.handled = false;

        /* Register for my notification */
        SysNotifyRegister(myCardNo, appDBID, myDeferredNotifyEvent, NULL,
                          sysNotifyNormalPriority, &myData);

        /* Broadcast the notification */
        SysNotifyBroadcastDeferred(&notifyParam, NULL);

    } else if (cmdPBP->notify->notifyType == myDeferredNotifyEvent)
        MyNotifyHandler(cmdPBP->notify);
break;

SysNotifyBroadcastDeferred 関数は、指定されたノティフィケーションを全ての関連する宛先にブロードキャストします。ただし、その前に現在のイベント処理が完了するまで待たなくてはなりません。そのため、繰り越しノティフィケーションを使う場合、他の全てのクライアントが最初のノティフィケーションに応答する機会を得ることが保証されます。

ノティフィケーションをブロードキャストする関数はいくつかあります。ノティフィケーションハンドラはノティフィケーションスタックがオーバーフローする可能性を避けるために SysNotifyBroadcastDeferred を使用するべきです。

ノティフィケーションハンドラにおける長時間処理の特殊なケースとして、システムがスリープ状態に入る場合があります。以下の“Sleep and Wake Notifications”を参照して下さい。

 

スリープとウェイクのノティフィケーション

いくつかのノティフィケーションは、システムがスリープ状態に入る、あるいはウェイクアップする様々な段階でブロードキャストされます。これらのノティフィケーションには以下のものがあります。

  • sysNotifySleepRequestEvent
  • sysNotifySleepNotifyEvent
  • sysNotifyEarlyWakeupEvent
  • sysNotifyLateWakeupEvent

これらのノティフィケーションは、ブロードキャストされることが保証されていません。例えば、ユーザーがバッテリを抜いたことによってスリープに入る場合、スリープノティフィケーションは発行されません。そのためこれらのノティフィケーションは、システムがスリープに入る前に節約のために外部ハードウェアの電源を切るようなアプリケーションには適しません。

前もってクリーンアップ処理を行なう必要から、システムがスリープに入るのを検出したければ、sysNotifySleepNotifyEvent を登録します。

スリープのノティフィケーションに対する応答では、ユーザーに確認を求めるアラートを表示するような時間のかかる処理をしないことをおすすめします。さもないと、アラートはオートオフイベントがトリガーされるまで表示され続け、他のスリープノティフィケーションハンドラに害を及ぼすかもしれません。

場合によっては、システムがスリープするのを妨げる必要があるかもしれません。例えば、コードが長時間かかる処理の最中であったり、ネットワークへの接続を試みている場合などです。その場合、かわりに sysNotifySleepRequestEvent を登録します。このノティフィケーションは、システムがスリープに入る前に全てのクライアントに通知します。必要なら、ハンドラ内で以下のようにすることでスリープのリクエストを遅らせることができます。

((SleepEventParamType *)(notify->notifyDetailsP))->deferSleep++;

システムはノティフィケーションハンドラを呼出すたびに deferSleep 値をチェックします。もしそれが非ゼロであれば、スリープイベントをキャンセルします。

スリープを遅らせれば実行中だった処理を中断する必要はありません。処理が終わったら、システムにスリープイベントの続行を許可する必要があります。そのためには、keyDownEvent を作成して resumeSleepChr と (仮想キーコードであることを示す)command key bit をセットし、イベントキューに追加します。システムがこのイベントを受信すると、再び全てのクライアントに sysNotifySleepRequestEvent がブロードキャストされます。全てのクライアントが応答した後で deferSleep が 0 であれば、システムはスリープしても安全と判断し、sysNotifySleepNotifyEvent を各クライアントにブロードキャストします。

システムが実際にスリープに入るまでに、sysNotifySleepRequestEvent を何度も受信しうることに注意してください。一方、sysNotifySleepNotifyEvent を受信するのは(スリープ1回あたり)1度だけです。

ウェイクアップイベントでは、前述の残り2つのノティフィケーションがブロードキャストされます。sysNotifyEarlyWakeupEvent はウェイクアップ処理のごく初期 ── 通常画面が表示されるよりも前 ── の段階でブロードキャストされます。この段階では、システムが完全にウェイクアップするかどうかは保証されません。アラームやバッテリ充電に関するイベントを処理した後で再びスリープする可能性もあります。ウェイクアップイベントの通知を必要とするアプリケーションのほとんどは、sysNotifyLateWakeupEvent をかわりに登録すると良いでしょう。この段階では、画面が表示され、システムが完全にウェイクアップしたことが保証されます。

ハンドヘルドが sysNotifyLateWakeupEvent ノティフィケーションを受信した場合、ロックによってユーザーのパスワード入力を待つかもしれません。この場合、ユーザーインターフェースを表示するにはユーザーがロックを解除するまで待つ必要があります。そのため、ハンドヘルドのウェイクアップ時にユーザーインターフェースを表示したければ、デバイスがロックされていないことを確認する必要があります。ハンドヘルドがロックされている場合、sysNotifyDeviceUnlocked ノティフィケーションを登録し、受信時にユーザーインターフェースを表示します。リスト 2.7 を参照して下さい。

リスト 2.7 Late Wakeup ノティフィケーションへの応答

case sysNotifyLateWakeupEvent:
    if ((Boolean)PrefGetPreference(prefDeviceLocked)) {
        SysNotifyRegister(myCardNo, myDbID,
                          sysNotifyDeviceUnlocked, NULL,
                          sysNotifyNormalPriority, NULL);
    } else {
        HandleDeviceWakeup();
    }
case sysNotifyDeviceUnlocked:
    HandleDeviceWakeup();

 


← 5 節に戻る ↑2 章トップへ 7 節に進む →