更新スケジュールの実装
前述したように、AppWidgetProviderInfoクラス内の設定値はイミュータブルです。updateTimeMillisの値はこのクラス内にあるため、この値セットを持つAppWidgetProviderInfoを使って作成されたApp Widgetインスタンスは、指定の頻度で更新されますが、その頻度を変更することはできません。今回のアプリケーションでは、それぞれのApp Widgetインスタンス内で画像が表示される頻度をユーザーに設定させるため、この機能を独自に実装する必要があります。
AlarmManagerクラスは、この目的に使用できる非常に論理的な更新メカニズムで、繰り返し通知をサポートしています。この通知は単純なPendingIntentオブジェクトのトリガーによって実現されます。
では、具体的にどのようにしたらよいのでしょうか。まず思いつくのはこんな方法でしょう。まず単純にAppWidgetManager.ACTION_APPWIDGET_UPDATEというaction値を持つIntentオブジェクトを作成し、extras値を特定のApp Widget IDに設定し、その後、AlarmManagerのsetRepeating()メソッドを呼び出して、更新を繰り返し行うようスケジュール設定するという方法です。しかし残念ながら、それではうまくいきません。Androidシステムはaction値とscheme値の両方が一致するIntentを再利用する仕組みになっており、「extras」の値は比較されません。これはつまり、それぞれのApp Widget IDに対応するIntentは実際には同じIntentであるという意味です。幸い、この問題は簡単に解決できます。App Widgetのscheme値を定義し、これを使用して一意のIntentインスタンスを定義すればよいのです。これを実現するコード例を次に示します。
Intent widgetUpdate = new Intent();
widgetUpdate.setAction(
AppWidgetManager.ACTION_APPWIDGET_UPDATE);
widgetUpdate.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS,
new int[] { appWidgetId });
// make this pending intent unique
widgetUpdate.setData(
Uri.withAppendedPath(Uri.parse(
ImagesWidgetProvider.URI_SCHEME + "://widget/id/"),
String.valueOf(appWidgetId)));
PendingIntent newPending = PendingIntent.getBroadcast(
getApplicationContext(), 0, widgetUpdate,
PendingIntent.FLAG_UPDATE_CURRENT);
// now schedule it
AlarmManager alarms = (AlarmManager) getApplicationContext().
getSystemService(Context.ALARM_SERVICE);
alarms.setRepeating(AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime(), updateRateSeconds * 1000,
newPending);
この記述はImagesWidgetConfiguration Activityの実装コードに含まれています。マニフェストファイルには、この特定のscheme値を扱うための<receiver>ブロックが示されています。これに加えて、AppWidgetProviderインターフェースのonDeleted()メソッド内で繰り返し通知を停止する必要があるでしょう。また、携帯電話を再起動したときに更新スケジュールも同様に再開する必要があります。
これに対する簡単なソリューションは、AppWidgetProviderのonReceive()メソッド内で、あるロジックを直接利用することです。まず、更新アクションかどうかをチェックします。更新の場合は、scheme値が含まれていないことを確認します。含まれていない場合は、AlarmManagerにスケジュール設定されているPendingIntentは、このアクションを起動しません。その場合は、各App Widget IDに対してプリファレンス値が設定されているかどうかを調べます。プリファレンス値が設定されていない場合は、この更新アクションはApp Widgetが設定される前に受信されたことが分かります。一方、プリファレンス値が設定されている場合は、PendingIntentをスケジュール設定できることが分かります。繰り返しになりますが、このロジック全体はonReceive()メソッド内で実装されています。
図2に表すように、このスキーマを使用すると、同じ種類のApp Widgetsを複数同時に表示できます。更新頻度はさまざまに設定でき、それぞれに表示される画像は、用意された一連の画像ファイルの中からランダムに選ばれます。
この記事では、Androidのプラットフォーム上で基本的なApp Widgetを作成する方法について解説してきました。ホーム画面に表示されるこのApp Widgetの各インスタンスは、それぞれユーザーが設定したスケジュールで個別に動作しますが、この機能はApp Widgetフレームワークから直接利用できるものではありません。今後の記事では、App Widgetの機能を拡張し、インターネットからダウンロードした画像を使用できるようにする方法や、画面上のインターフェースを使用して画像表示を直接制御できるようにする方法について解説していきたいと思います。

