Jetpack Glance 安定版の登場
Android 開発において、アプリウィジェット(AppWidget)の構築方法が刷新されました。Jetpack Glance の安定版(Stable)がリリースされ、従来の XML ベースな RemoteViews に代わり、Jetpack Compose と類似した宣言型 UI API でウィジェットを作成できるようになりました。これにより、モダンな Android 開発フローの中でウィジェット開発の生産性が大幅に向上します。
環境構築と依存関係
プロジェクトに Glance を導入するには、build.gradle ファイルに依存関係を追加し、Compose 機能を有効にする必要があります。
dependencies {
implementation "androidx.glance:glance:1.0.0"
implementation "androidx.glance:glance-appwidget:1.0.0"
}
android {
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.3"
}
}
ウィジェットの定義と設定
Glance を使用したウィジェットは、GlanceAppWidgetReceiver を継承した BroadcastReceiver として manifest に登録します。また、ウィジェットのメタデータ定義ファイルも必要です。
AndroidManifest.xml
<receiver
android:name=".widget.NewsWidgetReceiver"
android:exported="false">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/news_widget_info" />
</receiver>
ウィジェットプロバイダ設定 (res/xml/news_widget_info.xml)
ウィジェットの初期サイズや更新頻度などを定義します。Android 12 以降ではリサイズ可能な属性も設定可能です。
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/widget_desc"
android:initialLayout="@layout/glance_default_loading_layout"
android:minWidth="110dp"
android:minHeight="69dp"
android:minResizeWidth="110dp"
android:minResizeHeight="69dp"
android:resizeMode="horizontal|vertical"
android:updatePeriodMillis="86400000"
android:widgetCategory="home_screen" />
コアコンポーネントの構造
Glance ウィジェットは主に 2 つのクラスで構成されます。
GlanceAppWidgetReceiver
システムからのブロードキャストを受信するエントリーポイントです。抽象プロパティ glanceAppWidget を実装し、実際のウィジェットロジックを担うクラスを返します。
class NewsWidgetReceiver : GlanceAppWidgetReceiver() {
override val glanceAppWidget: GlanceAppWidget = NewsFeedGlanceWidget()
}
GlanceAppWidget
ウィジェットの UI とデータロジックを定義する抽象クラスです。最も重要なメソッドは provideGlance で、これはサスペンド関数として定義されているため、内部で非同期データ取得が可能です。
class NewsFeedGlanceWidget : GlanceAppWidget() {
override suspend fun provideGlance(context: Context, id: GlanceId) {
// データ取得などの処理
val newsItems = fetchNewsData()
provideContent {
GlanceTheme {
Column(
modifier = GlanceModifier.fillMaxSize()
.padding(16.dp)
) {
Text(
text = "最新ニュース",
modifier = GlanceModifier.padding(bottom = 8.dp)
)
LazyColumn {
items(newsItems) { item ->
NewsItemRow(item)
}
}
}
}
}
}
}
サイズモードとレスポンシブ対応
SizeMode を使用することで、ウィジェットのサイズ変化に対応した UI を提供できます。
- Single: 最小サイズのみで UI を提供します。
- Exact: 利用可能なすべてのサイズに対して UI を生成します。
- Responsive: 定義したサイズセットに基づき、システムが最適なビューを選択します(Android 12 以降推奨)。
override val sizeMode: SizeMode = SizeMode.Responsive(
setOf(
DpSize(110.dp, 69.dp),
DpSize(180.dp, 120.dp)
)
)
主要機能と実装ポイント
非同期処理のサポート
provideGlance がサスペンド関数であるため、ネットワーク請求やデータベース読み込みなどを直接記述できます。これにより、従来の WidgetProvider で必要だった複雑な非同期処理のボイラープレートが削減されます。
WorkManager による更新管理
ウィジェットの更新タイミングを制御するために、Glance は内部で WorkManager を利用しています。独自の更新ロジックを実装する場合は、以下のようにワーカーを enqueue します。
class WidgetRefreshWorker(
context: Context,
params: WorkerParameters
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
// 更新処理
return Result.success()
}
companion object {
fun scheduleUpdate(context: Context, glanceId: GlanceId) {
val request = OneTimeWorkRequestBuilder<WidgetRefreshWorker>()
.addTag(glanceId.toString())
.build()
WorkManager.getInstance(context).enqueue(request)
}
}
}
ウィジェットが削除された際には、関連するワークをキャンセルする処理を onDelete メソッドに実装する必要があります。
インタラクションと Action
Compose の onClick に相当する機能として、GlanceModifier.clickable や actionStartActivity が用意されています。
Text(
text = "詳細を見る",
modifier = GlanceModifier.clickable(
actionStartActivity<DetailActivity>()
)
)
ただし、アニメーション効果などは RemoteViews の制約によりサポートされていない点に注意が必要です。
実装上の注意点
Compose と API が類似しているため、インポート文の混同に注意が必要です。レイアウト要素や Modifier は androidx.glance パッケージからインポートする必要があります。
import androidx.glance.layout.Column
import androidx.glance.layout.PaddingValues
import androidx.glance.GlanceModifier
// androidx.compose.foundation.layout などは使用不可
画像表示については、Compose の Painter ではなく ImageProvider を使用します。また、文字列リソースの取得には LocalContext.current を利用して getString を呼び出す必要があります。
@Composable
fun getStringResource(@StringRes id: Int): String {
return LocalContext.current.getString(id)
}
Image(
provider = ResourceImageProvider(R.drawable.ic_news),
contentDescription = "News Icon",
modifier = GlanceModifier.size(32.dp)
)
これらの差異を理解し、Glance 専用の API を正しく利用することで、安定したウィジェット動作を実現できます。