高度なウィジェットを作成する

Compose をお試しください
Jetpack Compose は、Android で推奨される UI ツールキットです。Compose スタイルの API を使用してウィジェットを構築する方法を学びます。

このページでは、ユーザー エクスペリエンスを向上させる、より高度なウィジェットを作成するための推奨プラクティスについて説明します。

ウィジェット コンテンツの更新の最適化

ウィジェットのコンテンツを更新すると、計算費用が高額になることがあります。バッテリーの消費を抑えるには、更新の種類、頻度、タイミングを最適化します。

ウィジェットの更新の種類

ウィジェットを更新する方法には、完全更新、部分更新、コレクション ウィジェットの場合はデータ更新の 3 つがあります。それぞれに異なる計算コストと影響があります。

以下では、各更新タイプについて説明し、それぞれのコード スニペットを示します。

val appWidgetManager = AppWidgetManager.getInstance(context)
val remoteViews = RemoteViews(context.packageName, R.layout.widgetlayout).apply {
    setTextViewText(R.id.textview_widget_layout1, "Updated text1")
    setTextViewText(R.id.textview_widget_layout2, "Updated text2")
}
appWidgetManager.updateAppWidget(appWidgetId, remoteViews)

  • 部分更新: AppWidgetManager.partiallyUpdateAppWidget を呼び出して、ウィジェットの一部を更新します。これにより、新しい RemoteViews が以前に指定された RemoteViews と統合されます。ウィジェットが updateAppWidget(int[], RemoteViews) を介して完全な更新を 1 回以上受信していない場合、このメソッドは無視されます。

val appWidgetManager = AppWidgetManager.getInstance(context)
val remoteViews = RemoteViews(context.packageName, R.layout.widgetlayout).apply {
    setTextViewText(R.id.textview_widget_layout, "Updated text")
}
appWidgetManager.partiallyUpdateAppWidget(appWidgetId, remoteViews)

  • コレクション データの更新: AppWidgetManager.notifyAppWidgetViewDataChanged を呼び出して、ウィジェット内のコレクション ビューのデータを無効にします。これにより、RemoteViewsFactory.onDataSetChanged がトリガーされます。その間、ウィジェットには古いデータが表示されます。このメソッドを使用すると、負荷の高いタスクを安全に同期的に実行できます。

val appWidgetManager = AppWidgetManager.getInstance(context)
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_listview)

これらのメソッドは、アプリが対応する AppWidgetProvider クラスと同じ UID を持っている限り、アプリ内のどこからでも呼び出すことができます。

ウィジェットの更新頻度を決定する

ウィジェットは、updatePeriodMillis 属性に指定された値に応じて定期的に更新されます。ウィジェットは、ユーザー操作、ブロードキャスト アップデート、またはその両方に応じて更新できます。

定期的に更新する

定期的な更新の頻度は、appwidget-provider XML で AppWidgetProviderInfo.updatePeriodMillis の値を指定することで制御できます。更新のたびに AppWidgetProvider.onUpdate() メソッドがトリガーされます。このメソッドに、ウィジェットを更新するコードを配置できます。ただし、ウィジェットでデータを非同期的に読み込む必要がある場合や、更新に 10 秒以上かかる場合は、後述のブロードキャスト レシーバの更新の代替手段を検討してください。10 秒を過ぎると、システムは BroadcastReceiver が応答していないと見なします。

updatePeriodMillis では、30 分未満の値はサポートされていません。ただし、定期的な更新を無効にする場合は、0 を指定できます。

ユーザーが設定で更新の頻度を調整できるようにすることができます。たとえば、株価ティッカーを 15 分ごとに更新したり、1 日に 4 回だけ更新したりできます。この場合は、updatePeriodMillis を 0 に設定し、代わりに WorkManager を使用します。

ユーザー操作に応じて更新する

ユーザー操作に基づいてウィジェットを更新するおすすめの方法をいくつか紹介します。

  • アプリのアクティビティから: ユーザーのタップなどのユーザー操作に応じて、AppWidgetManager.updateAppWidget を直接呼び出します。

  • 通知やアプリ ウィジェットなどのリモート操作から: PendingIntent を作成し、呼び出された Activity、Broadcast、Service からウィジェットを更新します。優先度は任意で選択できます。たとえば、PendingIntent に Broadcast を選択した場合は、フォアグラウンド ブロードキャストを選択して BroadcastReceiver に優先度を付与できます。

ブロードキャスト イベントに応じて更新する

ウィジェットの更新が必要なブロードキャスト イベントの例としては、ユーザーが写真を撮影した場合が挙げられます。この場合、新しい写真が検出されたときにウィジェットを更新します。

WorkManager を使用して処理をスケジュールし、Constraints.Builder.addContentUriTrigger メソッドを使用してコンテンツ URI トリガーを指定できます。

ブロードキャストの BroadcastReceiver を登録することもできます(ACTION_LOCALE_CHANGED をリッスンするなど)。ただし、この方法ではデバイスのリソースが消費されるため、注意して使用し、特定のブロードキャストのみをリッスンするようにしてください。Android 7.0(API レベル 24)と Android 8.0(API レベル 26)でブロードキャストの制限が導入されたため、アプリはマニフェストに暗黙的ブロードキャストを登録できなくなりました。ただし、特定の例外があります。

BroadcastReceiver からウィジェットを更新する際の考慮事項

ウィジェットが BroadcastReceiver(AppWidgetProvider を含む)から更新される場合は、ウィジェットの更新の期間と優先度に関する次の考慮事項に注意してください。

更新の所要時間

通常、システムは、ブロードキャスト レシーバ(通常はアプリのメインスレッドで実行される)を最大 10 秒間実行してから、応答なしと見なし、アプリケーション応答なし(ANR)エラーをトリガーします。ブロードキャストの処理中にメインスレッドがブロックされないようにするには、goAsync メソッドを使用します。ウィジェットの更新に時間がかかる場合は、WorkManager を使用してタスクをスケジュールすることを検討してください。

Caution: Any work you do here blocks further broadcasts until it completes,
so it can slow the receiving of later events.

詳細については、セキュリティに関する考慮事項とベスト プラクティスをご覧ください。

アップデートの優先度

デフォルトでは、AppWidgetProvider.onUpdate を使用して行われたブロードキャストを含むブロードキャストは、バックグラウンド プロセスとして実行されます。つまり、システム リソースが過負荷になると、ブロードキャスト レシーバの呼び出しが遅れる可能性があります。ブロードキャストを優先するには、フォアグラウンド プロセスにします。

たとえば、ユーザーがウィジェットの特定の部分をタップしたときに、PendingIntent.getBroadcast に渡される Intent に Intent.FLAG_RECEIVER_FOREGROUND フラグを追加します。