이 페이지에서는 더 나은 사용자 환경을 위해 고급 위젯을 만들기 위한 권장사항을 설명합니다.
위젯 콘텐츠 업데이트를 위한 최적화
위젯 콘텐츠를 업데이트하는 데는 계산 비용이 많이 들 수 있습니다. 배터리 소모를 절약하려면 업데이트 유형, 빈도, 타이밍을 최적화하세요.
위젯 업데이트 유형
위젯을 업데이트하는 방법에는 전체 업데이트, 부분 업데이트, 그리고 컬렉션 위젯의 경우 데이터 새로고침의 세 가지 방법이 있습니다. 계산 비용과 효과는 각각 다릅니다.
다음은 각 업데이트 유형에 대한 설명과 각각에 대한 코드 스니펫을 제공합니다.
전체 업데이트:
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
와 병합됩니다. 위젯이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초 넘게 걸리는 경우 다음 섹션에 설명된 broadcast receiver 업데이트 대안을 고려하세요. 10초가 지나면 시스템에서 BroadcastReceiver
를 무응답으로 간주하기 때문입니다.
updatePeriodMillis
는 30분 미만의 값을 지원하지 않습니다. 그러나 주기적 업데이트를 사용 중지하려면 0으로 지정하면 됩니다.
사용자가 구성의 업데이트 빈도를 조정하도록 허용할 수 있습니다. 예를 들어 주식 티커를 15분마다 또는 하루에 4번만 업데이트하고 싶을 수 있습니다. 이 경우 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
에서 업데이트되는 경우 위젯 업데이트의 기간 및 우선순위에 관한 다음 고려사항에 유의하세요.
업데이트 기간
일반적으로 시스템은 앱의 기본 스레드에서 실행되는 broadcast receiver를 최대 10초 동안 실행한 후 무응답으로 간주하고 애플리케이션 응답 없음 (ANR) 오류를 트리거합니다. 위젯을 업데이트하는 데 시간이 더 오래 걸리면 다음 대안을 고려하세요.
WorkManager
를 사용하여 작업을 예약합니다.goAsync
메서드를 사용하여 수신자에게 더 많은 시간을 제공합니다. 이렇게 하면 수신기가 30초 동안 실행될 수 있습니다.
자세한 내용은 보안 고려사항 및 권장사항을 참조하세요.
업데이트 우선순위
기본적으로 AppWidgetProvider.onUpdate
를 사용하여 만든 브로드캐스트를 포함하여 브로드캐스트는 백그라운드 프로세스로 실행됩니다. 즉, 과부하된 시스템 리소스로 인해 broadcast receiver 호출이 지연될 수 있습니다. 브로드캐스트의 우선순위를 지정하려면 포그라운드 프로세스로 설정하세요.
예를 들어 사용자가 위젯의 특정 부분을 탭할 때 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>