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

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

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

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

ウィジェット コンテンツの更新は、計算コストが高くなる可能性があります。バッテリー消費を抑えるには、更新タイプ、頻度、タイミングを最適化します。

ウィジェットの更新タイプ

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

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

  • 完全更新: AppWidgetManager.updateAppWidget(int, android.widget.RemoteViews) を呼び出して、ウィジェットを完全に更新します。これにより、以前に提供された RemoteViewsが新しい RemoteViewsに置き換えられます。これは、最も計算コストの高い更新です。

    Kotlin

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

    Java

    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
    RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widgetlayout);
    remoteViews.setTextViewText(R.id.textview_widget_layout1, "Updated text1");
    remoteViews.setTextViewText(R.id.textview_widget_layout2, "Updated text2");
    appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
  • 部分更新: AppWidgetManager.partiallyUpdateAppWidget を呼び出して、ウィジェットの一部を更新します。これにより、新しい RemoteViews が以前に提供された RemoteViews とマージされます。ウィジェットが 少なくとも 1 回の完全更新を受信しない場合、このメソッドはupdateAppWidget(int[], RemoteViews)を介して無視されます。

    Kotlin

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

    Java

    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
    RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widgetlayout);
    remoteViews.setTextViewText(R.id.textview_widget_layout, "Updated text");
    appWidgetManager.partiallyUpdateAppWidget(appWidgetId, remoteViews);
  • コレクション データの更新: AppWidgetManager.notifyAppWidgetViewDataChanged を呼び出して、ウィジェット内のコレクション ビューのデータを無効にします。これにより、 RemoteViewsFactory.onDataSetChanged がトリガーされます。 それまでの間、古いデータがウィジェットに表示されます。このメソッドを使用すると、コストのかかるタスクを安全に同期的に実行できます。

    Kotlin

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

    Java

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

アプリが対応する 同じ UID を持っている限り、アプリ内の任意の場所からこれらのメソッドを呼び出すことができます。AppWidgetProvider

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

ウィジェットは、 updatePeriodMillis 属性に指定された値に応じて定期的に更新されます。ウィジェットは、ユーザー インタラクション、ブロードキャストの更新、またはその両方に応じて更新できます。

定期的に更新する

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

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

ユーザーが設定で更新頻度を調整できるようにすることもできます。たとえば、株価情報を 15 分おきに更新する、1 日 4 回のみ更新する、といった設定ができます。この場合は、updatePeriodMillis を 0 に設定し、代わりに WorkManager を使用します。

ユーザー インタラクションに応じて更新する

ユーザー インタラクションに基づいてウィジェットを更新する推奨の方法をいくつか紹介します。

  • アプリの Activity から: ユーザーのタップなどのユーザー インタラクションに応じて、AppWidgetManager.updateAppWidget を直接呼び出します。

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

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

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

JobScheduler でジョブをスケジュールし、ブロードキャストを トリガーとして指定するには、 JobInfo.Builder.addTriggerContentUri メソッドを使用します。

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

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

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

更新の所要時間

原則として、システムは、通常アプリの メインスレッドで実行されるブロードキャスト レシーバが応答していないと見なして Application Not Responding(ANR)エラーをトリガーするまでに、最大 10 秒間実行できるようにします。ブロードキャストの処理中に メインスレッドがブロックされないようにするには、 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 を使用して行われたブロードキャストを含むブロードキャストは、バックグラウンド プロセスとして実行されます。つまり、システムリソースが過負荷になると、ブロードキャスト レシーバの呼び出しが遅延する可能性があります。ブロードキャストに優先順位を付けるには、フォアグラウンド プロセスにします。

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