Android Glance 安定版リリース:Compose で構築するウィジェット開発

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.clickableactionStartActivity が用意されています。

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 を正しく利用することで、安定したウィジェット動作を実現できます。

タグ: Android Jetpack-Glance AppWidget Kotlin Jetpack-Compose

6月8日 23:43 投稿