이 페이지에서는 더 나은 사용자 환경을 위해 고급 위젯을 만드는 권장사항을 설명합니다.
위젯 콘텐츠 업데이트 최적화
위젯 콘텐츠를 업데이트하는 데는 컴퓨팅 비용이 많이 들 수 있습니다. 배터리 소모를 줄이려면 업데이트 유형, 빈도, 타이밍을 최적화하세요.
위젯 업데이트 유형
위젯을 업데이트하는 방법에는 전체 업데이트, 부분 업데이트, 컬렉션 위젯의 경우 데이터 새로고침의 세 가지가 있습니다. 각각의 계산 비용과 결과가 다릅니다.
다음은 각 업데이트 유형을 설명하고 각 유형의 코드 스니펫을 제공합니다.
전체 업데이트: 위젯을 완전히 업데이트하려면
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)
자바
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
와 병합됩니다. 이 메서드는 위젯이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)
자바
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)
자바
AppWidgetManager 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분마다 업데이트하고 싶은 사람도 있고 하루에 네 번만 업데이트하고 싶은 사람도 있습니다. 이 경우 updatePeriodMillis
을 0으로 설정하고 대신 WorkManager
를 사용합니다.
사용자 상호작용에 대한 응답으로 업데이트
다음은 사용자 상호작용에 따라 위젯을 업데이트하는 권장 방법입니다.
앱의 활동에서: 사용자의 탭과 같은 사용자 상호작용에 대한 응답으로
AppWidgetManager.updateAppWidget
을 직접 호출합니다.알림이나 앱 위젯과 같은 원격 상호작용에서:
PendingIntent
를 구성한 다음 호출된Activity
,Broadcast
또는Service
에서 위젯을 업데이트합니다. 우선순위를 직접 선택할 수 있습니다. 예를 들어PendingIntent
에Broadcast
를 선택하면BroadcastReceiver
에 우선순위를 부여하는 포그라운드 브로드캐스트를 선택할 수 있습니다.
브로드캐스트 이벤트에 대한 응답으로 업데이트
위젯을 업데이트해야 하는 브로드캐스트 이벤트의 예는 사용자가 사진을 찍는 경우입니다. 이 경우 새 사진이 감지되면 위젯을 업데이트해야 합니다.
JobScheduler
로 작업을 예약하고 JobInfo.Builder.addTriggerContentUri
메서드를 사용하여 브로드캐스트를 트리거로 지정할 수 있습니다.
브로드캐스트를 위해 BroadcastReceiver
를 등록할 수도 있습니다(예: ACTION_LOCALE_CHANGED
수신 대기).
하지만 이 기능은 기기 리소스를 사용하므로 주의해서 사용하고 특정 브로드캐스트만 수신하세요. Android 7.0 (API 수준 24) 및 Android 8.0 (API 수준 26)에서 브로드캐스트 제한이 도입됨에 따라 앱은 특정 예외를 제외하고 매니페스트에서 암시적 브로드캐스트를 등록할 수 없습니다.
BroadcastReceiver에서 위젯을 업데이트할 때 고려사항
AppWidgetProvider
을 비롯한 BroadcastReceiver
에서 위젯을 업데이트하는 경우 위젯 업데이트의 기간 및 우선순위에 관한 다음 고려사항을 참고하세요.
업데이트 기간
일반적으로 시스템은 앱의 기본 스레드에서 실행되는 브로드캐스트 리시버가 응답하지 않는 것으로 간주하고 애플리케이션 응답 없음 (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
를 사용하여 만든 브로드캐스트를 비롯한 브로드캐스트는 백그라운드 프로세스로 실행됩니다. 즉, 과부하된 시스템 리소스로 인해 브로드캐스트 리시버 호출이 지연될 수 있습니다. 브로드캐스트의 우선순위를 지정하려면 포그라운드 프로세스로 만드세요.
예를 들어 사용자가 위젯의 특정 부분을 탭할 때 PendingIntent.getBroadcast
에 전달된 Intent
에 Intent.FLAG_RECEIVER_FOREGROUND
플래그를 추가합니다.
동적 항목이 포함된 정확한 미리보기 만들기

이 섹션에서는 컬렉션 뷰가 있는 위젯, 즉 ListView
, GridView
또는 StackView
를 사용하는 위젯의 위젯 미리보기에 여러 항목을 표시하는 권장 방법을 설명합니다.
위젯이 이러한 뷰 중 하나를 사용하는 경우 실제 위젯 레이아웃을 직접 제공하여 확장 가능한 미리보기를 만들면 위젯 미리보기에 항목이 표시되지 않을 때 환경이 저하됩니다. 이는 컬렉션 뷰 데이터가 런타임에 동적으로 설정되기 때문이며 그림 1에 표시된 이미지와 유사합니다.
컬렉션 뷰가 있는 위젯의 미리보기가 위젯 선택기에 올바르게 표시되도록 하려면 미리보기 전용으로 지정된 별도의 레이아웃 파일을 유지하는 것이 좋습니다. 이 별도의 레이아웃 파일에는 실제 위젯 레이아웃과 가짜 항목이 있는 자리표시자 컬렉션 뷰가 포함됩니다. 예를 들어 가짜 목록 항목이 여러 개 있는 자리표시자 LinearLayout
를 제공하여 ListView
를 모방할 수 있습니다.
ListView
의 예를 설명하려면 별도의 레이아웃 파일로 시작하세요.
// res/layout/widget_preview.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/widget_background"
android:orientation="vertical">
// Include the actual widget layout that contains ListView.
<include
layout="@layout/widget_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
// The number of fake items you include depends on the values you provide
// for minHeight or targetCellHeight in the AppWidgetProviderInfo
// definition.
<TextView android:text="@string/fake_item1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="?attr/appWidgetInternalPadding" />
<TextView android:text="@string/fake_item2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="?attr/appWidgetInternalPadding" />
</LinearLayout>
AppWidgetProviderInfo
메타데이터의 previewLayout
속성을 제공할 때 미리보기 레이아웃 파일을 지정합니다. initialLayout
속성의 실제 위젯 레이아웃을 지정하고 런타임에 RemoteViews
를 구성할 때 실제 위젯 레이아웃을 사용합니다.
<appwidget-provider
previewLayout="@layout/widget_previe"
initialLayout="@layout/widget_view" />
복잡한 목록 항목
이전 섹션의 예시에서는 목록 항목이 TextView
객체이므로 가짜 목록 항목을 제공합니다. 항목이 복잡한 레이아웃인 경우 가짜 항목을 제공하는 것이 더 복잡할 수 있습니다.
widget_list_item.xml
에 정의되고 두 개의 TextView
객체로 구성된 목록 항목을 고려해 보세요.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView android:id="@id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/fake_title" />
<TextView android:id="@id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/fake_content" />
</LinearLayout>
가짜 목록 항목을 제공하려면 레이아웃을 여러 번 포함하면 되지만 이렇게 하면 각 목록 항목이 동일해집니다. 고유한 목록 항목을 제공하려면 다음 단계를 따르세요.
텍스트 값의 속성 집합을 만듭니다.
<resources> <attr name="widgetTitle" format="string" /> <attr name="widgetContent" format="string" /> </resources>
다음 속성을 사용하여 텍스트를 설정합니다.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@id/title" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="?widgetTitle" /> <TextView android:id="@id/content" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="?widgetContent" /> </LinearLayout>
미리보기에 필요한 만큼 스타일을 만듭니다. 각 스타일의 값을 재정의합니다.
<resources> <style name="Theme.Widget.ListItem"> <item name="widgetTitle"></item> <item name="widgetContent"></item> </style> <style name="Theme.Widget.ListItem.Preview1"> <item name="widgetTitle">Fake Title 1</item> <item name="widgetContent">Fake content 1</item> </style> <style name="Theme.Widget.ListItem.Preview2"> <item name="widgetTitle">Fake title 2</item> <item name="widgetContent">Fake content 2</item> </style> </resources>
미리보기 레이아웃의 가짜 항목에 스타일을 적용합니다.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" ...> <include layout="@layout/widget_view" ... /> <include layout="@layout/widget_list_item" android:theme="@style/Theme.Widget.ListItem.Preview1" /> <include layout="@layout/widget_list_item" android:theme="@style/Theme.Widget.ListItem.Preview2" /> </LinearLayout>