Palm Programmer's Laboratory
Palm OS Programmer's Companion Volume I/11-2
11-2 プリファレンス
プリファレンスマネージャは、システム全体のプリファレンスとアプリケーション固有のプリファレンスの両方を処理します。プリファレンスマネージャは2つの異なるデータベースに格納されたプリファレンスを管理します。
- “保存される(saved)”プリファレンスデータベースは、HotSync操作によってバックアップされるプリファレンスを格納します。“保存される”プリファレンスデータベースは1つだけ存在し、全てのアプリケーションがそれを利用します。このデータベースにはアプリケーション固有のプリファレンス同様、システム全体のプリファレンスを格納します。
- “保存されない(unsaved)”プリファレンスデータベースは、HotSync 操作によってバックアップされないアプリケーション固有のプリファレンスを格納します。“保存されない”プリファレンスデータベースは1つだけ存在し、全てのアプリケーションがそれを利用します。
このセクションでは、これらのプリファレンスデータベースのそれぞれについて値の取得および設定を行なう方法について説明します。以下のトピックがあります。
- システムプリファレンスへのアクセス
- システムプリファレンスの設定
- アプリケーション固有プリファレンスの設定
システムプリファレンスへのアクセス
システムプリファレンスには、ユーザーがPalmハンドヘルドにどのように振舞って欲しいかが設定されています。例えば、システムプリファレンスは日付と時刻がどのように表示されるかを指定したり、アラーム発生時にサウンドを鳴らすかどうかを指定します。これらの値は通常組込みの Preferences アプリケーションや Security アプリケーションで設定します。原則として、アプリケーションはシステムプリファレンスに保存されている設定に従う必要があります。
システムプリファレンスの値を取得するには、PrefGetPreference 関数をコールして SystemPreferencesChoice 列挙値の1つを渡します。例えば、アプリケーションがユーザーインタフェイスにおいて現在日時を表示する場合、以下のようにしてユーザーが望む日時の表示書式を取得します。
TimeFormatType timeFormat = (TimeFormatType)PrefGetPreference(prefTimeFormat); DateFormatType dateFormat = (DateFormatType)PrefGetPreference(prefDateFormat);
- WARNING!
- PrefGetPreference を PrefGetPreferences と混同しないようにして下さい。後者は古いもので、バージョン 1.0 のシステムプリファレンス構造体を取得します。
PrefGetPreference 関数はデフォルトでは UInt32 型の値を返却します。この復帰値は取得したプリファレンスの種類によって適切な型にキャストする必要があります。
また、システムプリファレンス構造体は頻繁に更新され、構造体内部のバージョン情報も変わることに注意して下さい。システムプリファレンス構造体を変更する Palm OS のリリースでは、構造体の最後に新たな値が追加され、表 11.1 に示すように構造体のバージョン番号がインクリメントされます。
表 11.1 システムプリファレンスのバージョン番号
Palm OS バージョン | システムプリファレンスのバージョン |
---|---|
2.0 | 2 |
3.0 | 3 |
3.0 | 4 |
3.1 | 5 |
3.2 | 6 |
3.3 | 7 |
3.5 | 8 |
4.0 | 9 |
Palm OS Garnet version 5.0 | 10 |
Palm OS Garnet version 5.1 | 11 |
Palm OS Garnet version 5.3SC | 12 |
どのバージョンでどのプリファレンスが追加されたかや、各プリファレンスの復帰値型を知るには、Palm OS Programmer's API Reference の 900 ページにある表 48.1 を参照して下さい。
旧バージョンとの互換性を維持するために、Palm OS 2.0 以降で追加されたプリファレンスの値を取得する場合は事前にプリファレンスのバージョン番号をチェックして下さい。例えば、Palm OS 4.0 ではハンドヘルドのロケール情報(国や言語)に LmLocaleType 構造体のかたちでアクセスできるようなプリファレンスが追加されました。このプリファレンスにアクセスする前に、リスト 11.2 に示すようにプリファレンスバージョンをチェックする必要があります。
リスト 11.2 システムプリファレンスバージョンのチェック
LmLocaleType currentLocale; CountryType currentCountry; if (PrefGetPreference(prefVersion) >= preferenceDataVer9) { currentLocale = (LmLocaleType)PrefGetPreference(prefLocale); } else { /* make do with the country */ currentCountry = (CountryType)PrefGetPreference(prefCountry); }
場合によっては、新しいプリファレンスが既存のプリファレンスを置き換える場合があります。例えば、prefAutoOffDuration はプリファレンス構造体のバージョン 8 で prefAutoOffDurationSecs に置き換えられました。古いプリファレンスは自動的な電源断までの時間を分単位で格納していましたが、新しい方では秒単位になっています。Palm OS 4.0 で prefAutoOffDuration を使用すると、システムは自動電源断までの時間を分単位で返します。ただし、この値を取得するために、システムは秒単位の設定値を分単位に変換し、必要に応じて結果を丸めます。prefAutoOffDurationSecs を使用すればより正確な値を得ることができます。
システムプリファレンスの設定
時として、アプリケーションがシステムレベルのプリファレンスを設定する必要があります。システムプリファレンスを上書きする場合は、ユーザーの入力なしには行なわないことを強く推奨します。
例えば、組込みのアドレス帳を置き換えるアプリケーションを作成しているとします。プリファレンスアプリケーションは、アドレス帳のハードボタンを押した時に起動するアプリケーションを変更できるパネルを備えています。しかし、ユーザーがアドレス帳ボタンの挙動をもっと簡単に変更できるようにしたいので、初回起動時にユーザーに対してボタンの設定を変更するかどうかを確認するアラートを表示します。ユーザーが Yes をタップすれば、アプリケーションから PrefSetPreference をコールして新しい値を設定します。コードは以下のようになるでしょう。
リスト 11.3 システムプリファレンスの設定
if (PrefGetPreference(prefHard2CharAppCreator != myAppCreatorId)) { if (FrmAlert(MakeMeTheDefaultAlert) == 0) { /* user pressed Yes */ PrefSetPreference(prefHard2CharAppCreator, myAppCreatorId); } }
- WARNING!
- PrefSetPreference と PrefSetPreferences を混同しないで下さい。後者は古いもので、バージョン 1.0 のシステムプリファレンス構造体全体を設定します。
アプリケーション固有プリファレンスの設定
プリファレンスマネージャを使用して、アプリケーションに固有のプリファレンスを設定あるいは取得することができます。これは“saved”プリファレンスデータベースか“unsaved”プリファレンスデータベースのうちいずれかに保存することで実現します。
アプリケーションプリファレンスを保存するには、PrefSetAppPreferences を使用します。それを読み込むには、PrefGetAppPreferences を使用します。通常、プリファレンスの書込みは制御が他のアプリケーションに渡される際の appStopEvent に対する応答で行ないます。プリファレンスの読み込みは通常起動に対する応答で行ないます。
PrefSetAppPreferences と PrefGetAppPreferences は概ね同じパラメータをとります。アプリケーションのクリエータ ID、プリファレンスリソースを一意に識別するプリファレンス ID、プリファレンスの値を保持する構造体のポインタ、その構造体のサイズ、そして“saved”と“unsaved”のどちらのプリファレンスデータベースを使用するかを指示する Boolean 値です。PrefSetAppPreferences ではプリファレンス構造体のバージョン番号も指定します。この値は PrefGetAppPreferences の復帰値となります。
以下のセクションでは、アプリケーション固有プリファレンスの使用に関する話題を説明します。
- アプリケーションプリファレンスを使用すべき場合
- プリファレンスの保存方法
- どちらのプリファレンスデータベースを使うべきか
- 新しいリリースでのプリファレンスのアップデート
アプリケーションプリファレンスを使用すべき場合
アプリケーションプリファレンスは、アプリケーションの起動を跨いで存続する必要のある、アプリケーション固有の状態を格納するために使用します。例えば、組込みのアプリケーションは他のアプリケーションに制御が切り替る前に最後に表示していたフォームやレコード(あるいはレコードの集合)に関する情報を保存します。これにより、ユーザーがそのアプリケーションに戻ってきた時に同じビューを復元することができます。
プリファレンスを他の目的で使用することもできます。ユーザーがアプリケーションの振舞いをカスタマイズできるようにし、その情報をプリファレンスデータベースに保存することもできますし、他のアプリケーションと情報を共有する方法としてプリファレンスデータベースを使用することもできます。
プリファレンスに格納する値の選択はできるだけ簡潔にして下さい。例えばゲームでは、スクリーンの現在状態をビットマップで保存したくなるかもしれません。このようなビットマップはカラーデバイスでは 25KB を越えるサイズになるため、避けるべきです。そのかわり、現在の状態を再現できるような項目を保存するのが良いでしょう。例えばピクセル値でのプレイヤーの位置と現在のレベルなどです。
アプリケーションの重要な値を保存する方法は他にもあります。例えば、フィーチャマネージャを使用して単一の値をフィーチャとして保存することができます。プリファレンスとして保存することとフィーチャとして保存することの違いは、
- プリファレンス(“unsaved”データベースも含む)はストレージヒープにあるため、ソフトリセットしても存続します。フィーチャはソフトリセットにより削除されます。このため、プリファレンスはフィーチャよりもアプリケーションの状態を保存するのに適しています。
- アプリケーションプリファレンスはデータベースのレコードです。そのため、64KB のサイズ制限があります。複数のアプリケーションプリファレンスの作成も可能です。全てのバージョンの Palm OS でサポートされるフィーチャの最大サイズは 4KB です。Palm OS 3.1 以降では、フィーチャメモリを使用することで 4KB を越えるフィーチャを作成できます。Palm OS 3.5 未満のフィーチャメモリのサイズは最大で 64KB です。Palm OS 3.5 以降では、64KB を越えるフィーチャメモリのチャンクをアロケートできます。ただし、フィーチャメモリはアプリケーションプリファレンスとは異なる状況を想定しています。詳細は“フィーチャメモリ”を参照して下さい。
アプリケーションの状態をプリファレンスやフィーチャに保存するかわりに、アプリケーション自身が作成・管理するデータベースを使用することもできます。アプリケーションプリファレンスの保存にこの方法を選択する場合、独自の関数を作成してデータベースへの書込みとデータベースからの読込みを行なわなければなりません。プリファレンスのバックアップが必要であれば、backup ビットもセットする必要があります。しかし、独自のデータベースを使用した方が良い場合もあります。“どちらのプリファレンスデータベースを使うべきか”を参照して下さい。
プリファレンスの保存方法
ほとんどのアプリケーションは単一のプリファレンスリソースIDを使って単一のプリファレンス構造体を保存します。アプリケーションは appStopEvent を受信すると、PrefSetAppPreferences を使って構造体全体をリソースに書込みます。sysAppLaunchCmdNormalLaunch を受信した場合、アプリケーションは PrefGetAppPreferences を使用して構造体を読み込みます。
単一のプリファレンス構造体をデータベースに保存するのは多くのアプリケーションが従っている慣習です。これは、一度に全てのプリファレンスにアクセスできて便利だからです。アプリケーションはプリファレンスマネージャを使って複数のプリファレンスリソースを保存することもできます。これには PrefSetAppPreferences や PrefGetAppPreferences を複数回コールする必要がありますが、可変長のプリファレンスを使用するような場合にはこちらの方が便利だと思うかもしれません。
どちらのプリファレンスデータベースを使うべきか
PrefGetAppPreferences と PrefSetAppPreferences はともに、“saved”と“unsaved”のどちらのデータベースを使って値を読み書きするかを指定する Boolean 値をとります。“saved”プリファレンスを使用する場合は true を、“unsaved”プリファレンスを使用する場合は false をそれぞれ使用します。
2つのデータベースの違いは、デフォルトではユーザーが HotSync 操作を実行した時に“saved”プリファレンスデータベースがバックアップされ、“unsaved”データベースはバックアップされない、ということだけです。( ユーザーはサードパーティ製のツールを使って“unsaved”プリファレンスデータベースに backup ビットをセットし、バックアップが実行されるようにすることができます。) “saved”および“unsaved”プリファレンスはともにストレージヒープに存在するため、その内容はソフトリセットによっても存続します。プリファレンスが失われるのはハードリセットが実行された場合だけです。
ハードリセット後にレストアする必要のあるデータにのみ“saved”プリファレンスを使用し、アプリケーションの現在状態には“unsaved”プリファレンスを使用して下さい。例えば、アプリケーションがレジストコードを持っている場合、ハードリセット後にユーザーがレジストコードを探し出して再入力しなくても済むよう、“saved”プリファレンスデータベースに保存しておく方が良いでしょう。しかし、表示中のフォームや表示中のデータベースレコードは失われるかもしれませんから、それらは“unsaved”プリファレンスに書込みます。ゲームでは、ハイスコア情報は“saved”プリファレンスデータベースに保存し、実行中のゲーム関する情報は“unsaved”プリファレンスに保存することになるでしょう。
“saved”プリファレンスデータベースの使用を倹約することは重要です。いつ、どのアプリケーションが“saved”プリファレンスデータベースを更新したとしても、次の HotSync 操作ではデータベース全体がバックアップされることになります。多くのアプリケーションを使用するユーザーにとっては、HotSync 操作の実行にかかる時間に深刻な影響を与えることになります。
リスト 11.4 に HardBall アプリケーションのプリファレンス構造体と StopApplication 関数を示します。“saved”プリファレンスデータベースに保存される HardBallPreferenceType にはハイスコア情報と累積時間程度しか保存されていません。その他の全てのプリファレンスは“unsaved”プリファレンスデータベースに書き込まれる GameStatusType に格納されています。
リスト 11.4 アプリケーション固有プリファレンスの保存
typedef struct { SavedScore highScore[highScoreMax]; UInt8 lastHighScore; UInt8 startLevel; UInt32 accumulatedTime; } HardBallPreferenceType; typedef struct { enum gameProgress status; UInt8 periodLength; UInt32 nextPeriodTime; UInt32 periodsToWait; Boolean paused; UInt32 pausedTime; BrickType brick[rowsOfBricks][columnsOfBricks]; UInt8 bricksRemaining; UInt8 level; WorldState last; WorldState next; RemovedBrick brokenBricks[brokenBricksMax]; Int16 brokenBricksCount; UInt8 ballsRemaining; Boolean movePaddleLeft; Boolean movePaddleRight; SoundType soundToMake; Int8 soundPeriodsRemaining; Int32 scoreToAwardBonusBall; Boolean lowestHighScorePassed; Boolean highestHighScorePassed; Boolean gameSpedUp; Boolean cheatMode; UInt32 startTime; } GameStatusType; HardBallPreferenceType Prefs; static GameStatusType GameStatus; static void StopApplication (void) { ... // Update the time accounting. Prefs.accumulatedTime += (TimGetTicks() - GameStatus.startTime); // If we are saving a game resuming (it hasn't started // playing yet) then preserve the game status. if (GameStatus.status == gameResuming) { GameStatus.status = SavedGameStatus; } // Save state/prefs. PrefSetAppPreferences (appFileCreator, appPrefID, appPrefVersion, &Prefs, sizeof (Prefs), true); PrefSetAppPreferences (appFileCreator, appSavedGameID, appSavedGameVersion, &GameStatus, sizeof (GameStatus), false); // Close all the open forms. FrmCloseAllForms (); }
大量のプリファレンスデータがあって HotSync 操作によってバックアップされる必要があり、しかもそれが頻繁に変更されるのであれば、パフォーマンスの最適化として、“saved”プリファレンスよりもアプリケーションで独自に作成・管理するデータベースに保存する方法があります。これにより、HotSync 操作のたびに“saved”プリファレンスデータベース全体をバックアップしなくても済むことになります。この方法でアプリケーションプリファレンスを保存することのデメリットは、データベースを管理したりデータを取得するコードを全て自分で書かなければならないということです。
新しいリリースでのプリファレンスのアップデート
アプリケーションをアップデートする場合、プリファレンスデータベースに新しい項目を追加したくなるかもしれません。データベースの別レコードに新しいプリファレンスを書き込むという選択肢もありますが、現在のプリファレンス構造体を更新した方が良いでしょう。
PrefSetAppPreferences および PrefGetAppPreferences 関数は、プリファレンス構造体の更新に使用できるバージョン情報を管理しています。これを使用するには、PrefSetAppPreferences に渡したバージョン番号をトラッキングします。プリファレンス構造体の末尾に新しいプリファレンスを追加したら、このバージョン番号をインクリメントします。これには以下のようなマクロが使用できるでしょう。
#define CurrentPrefsVersion 2
新しいバージョンのアプリケーションが起動すると、PrefSetAppPreferences よりも先に PrefGetAppPreferences がコールされます。PrefGetAppPreferences 関数はデータベースから取得した構造体のバージョンを返します。例えば新しいバージョンが 2 であれば、そのバージョンが最後に実行される際 PrefGetAppPreferences は 1 を返します。返されたバージョンが現在のバージョンと一致しないので、バージョン 2 で導入された新しいプリファレンスは値が保存されていないことがわかります。これにより、これらのプリファレンスにデフォルト値を設定するべきであることがわかります。
どのバージョンであれ、そのアプリケーションが初めて実行される際には PrefGetAppPreferences は noPreferenceFound を返します。これはアプリケーションのプリファレンスがまったく保存されておらず、アプリケーションがプリファレンス構造体全体に対してデフォルト値を設定する必要があることを意味しています。Datebook アプリケーションがどのようにして PrefGetAppPreferences からバージョン番号の取得を処理しているかをリスト 11.5 に示します。
リスト 11.5 プリファレンスのバージョン番号のチェック
#define datebookPrefsVersionNum 4 Int16 DatebookLoadPrefs (DatebookPreferenceType* prefsP) { UInt16 prefsSize; Int16 prefsVersion = noPreferenceFound; Boolean haveDefaultFont = false; UInt32 defaultFont; ErrNonFatalDisplayIf(!prefsP, "null prefP arg"); // Read the preferences / saved-state information. Fix-up if no prefs or // older/newer version prefsSize = sizeof (DatebookPreferenceType); prefsVersion = PrefGetAppPreferences (sysFileCDatebook, datebookPrefID, prefsP, &prefsSize, true); // If the preferences version is from a future release (as can happen when // going back and syncing to an older version of the device), treat it the // same as "not found" because it could be significantly different if ( prefsVersion > datebookPrefsVersionNum ) prefsVersion = noPreferenceFound; if ( prefsVersion == noPreferenceFound ) { // Version 1 and 2 preferences prefsP->dayStartHour = defaultDayStartHour; prefsP->dayEndHour = defaultDayEndHour; prefsP->alarmPreset.advance = defaultAlarmPresetAdvance; prefsP->alarmPreset.advanceUnit = defaultAlarmPresetUnit; prefsP->saveBackup = defaultSaveBackup; prefsP->showTimeBars = defaultShowTimeBars; prefsP->compressDayView = defaultCompressDayView; prefsP->showTimedAppts = defaultShowTimedAppts; prefsP->showUntimedAppts = defaultShowUntimedAppts; prefsP->showDailyRepeatingAppts = defaultShowDailyRepeatingAppts; // We need to set up the note font with a default value for the system. FtrGet(sysFtrCreator, sysFtrDefaultFont, &defaultFont); haveDefaultFont = true; prefsP->v20NoteFont = (FontID)defaultFont; } if ((prefsVersion == noPreferenceFound) || (prefsVersion < datebookPrefsVersionNum)) { // Version 3 preferences prefsP->alarmSoundRepeatCount = defaultAlarmSoundRepeatCount; prefsP->alarmSoundRepeatInterval = defaultAlarmSoundRepeatInterval; prefsP->alarmSoundUniqueRecID = defaultAlarmSoundUniqueRecID; prefsP->noteFont = prefsP->v20NoteFont; // Fix up the note font if we copied from older preferences. if ((prefsVersion != noPreferenceFound) && (prefsP->noteFont == largeFont)) prefsP->noteFont = largeBoldFont; if (!haveDefaultFont) FtrGet(sysFtrCreator, sysFtrDefaultFont, &defaultFont); prefsP->apptDescFont = (FontID)defaultFont; } if ((prefsVersion == noPreferenceFound) || (prefsVersion < datebookPrefsVersionNum)) { // Version 4 preferences prefsP->alarmSnooze = defaultAlarmSnooze; } return prefsVersion; }