고급 위젯 만들기

이 페이지에서는 더 나은 사용자 환경을 위해 고급 위젯을 만드는 권장사항을 설명합니다.

위젯 콘텐츠 업데이트를 위한 최적화

위젯 콘텐츠를 업데이트하는 데는 계산 비용이 많이 들 수 있습니다. 배터리 절약 업데이트 유형, 빈도, 타이밍을 최적화할 수 있습니다.

위젯 업데이트 유형

위젯을 업데이트하는 방법에는 전체 업데이트, 부분 업데이트, 컬렉션 위젯의 경우 데이터 새로고침 등 세 가지가 있습니다. 각 방법에는 계산 비용과 결과가 다릅니다.

다음은 각 업데이트 유형에 대한 설명과 각각에 대한 코드 스니펫을 제공합니다.

  • 전체 업데이트: 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초 이상 걸리는 경우 다음 섹션에 설명된 broadcast receiver 업데이트의 대안을 고려하세요. 10초 후에는 시스템에서 BroadcastReceiver가 응답하지 않는 것으로 간주하기 때문입니다.

updatePeriodMillis는 30분 미만의 값을 지원하지 않습니다. 그러나 주기적인 업데이트를 사용 중지하려면 0으로 지정하면 됩니다.

사용자가 구성에서 업데이트 빈도를 조정하도록 허용할 수 있습니다. 대상 예를 들어 주식 티커가 15분마다 업데이트되도록 하거나 확인할 수 있습니다 이 경우 updatePeriodMillis를 0으로 설정하고 대신 WorkManager를 사용하세요.

사용자 상호작용에 대한 업데이트

다음은 사용자 상호작용에 따라 위젯을 업데이트하는 데 권장되는 방법입니다.

  • 앱 활동에서: 사용자 상호작용(예: 사용자 탭)에 대한 응답으로 AppWidgetManager.updateAppWidget를 직접 호출합니다.

  • 알림 또는 앱 위젯과 같은 원격 상호작용: PendingIntent를 생성한 다음 호출된 Activity, Broadcast 또는 Service에서 위젯을 업데이트합니다. 우선순위를 직접 선택할 수 있습니다. 예를 들어 PendingIntentBroadcast를 선택하면 포그라운드 브로드캐스트를 선택하여 BroadcastReceiver에 우선순위를 줄 수 있습니다.

방송 이벤트에 대한 응답으로 업데이트

업데이트가 필요한 브로드캐스트 이벤트의 예로는 사용자가 사진을 찍습니다. 이 경우에는 새 사진이 표시될 때 위젯을 업데이트하는 것이 좋습니다. 감지됩니다

JobScheduler로 작업을 예약하고 JobInfo.Builder.addTriggerContentUri 메서드를 사용하여 브로드캐스트를 트리거로 지정할 수 있습니다.

브로드캐스트의 BroadcastReceiver를 등록할 수도 있습니다. 예를 들면 다음과 같습니다. 듣는 중 ACTION_LOCALE_CHANGED 하지만 이렇게 하면 기기 리소스가 소모되므로 주의해서 사용하고 특정 브로드캐스트만 리슨하세요. 방송국의 도입으로 제한사항 7.0 (API 수준 24) 및 Android 8.0 (API 수준 26)의 경우 앱은 암시적으로 등록할 수 없습니다. 특정 브로드캐스트를 예외입니다.

BroadcastReceiver에서 위젯을 업데이트할 때의 고려사항

위젯이 다음을 포함하여 BroadcastReceiver에서 업데이트되는 경우 AppWidgetProvider님, 위젯 업데이트의 기간 및 우선순위입니다.

업데이트 기간

일반적으로 시스템은 일반적으로 앱의 기본 스레드에서 실행되는 broadcast receiver가 최대 10초 동안 실행되도록 허용한 후 응답하지 않는 것으로 간주하고 애플리케이션 응답 없음(ANR) 오류를 트리거합니다. 시간이 더 오래 걸리는 경우 다음과 같은 대안을 고려하세요.

  • WorkManager를 사용하여 작업을 예약합니다.

  • goAsync 메서드를 사용하여 수신자에게 더 많은 시간을 줍니다. 이렇게 하면 수신기가 30초 동안 실행됩니다.

자세한 내용은 보안 고려사항 및 권장사항 권장사항을 확인할 수 있습니다

업데이트 우선순위

기본적으로 AppWidgetProvider.onUpdate: 백그라운드 프로세스로 실행됩니다. 즉, 과부하된 시스템 리소스로 인해 broadcast receiver 호출이 지연될 수 있습니다. 브로드캐스트의 우선순위를 높이려면 포그라운드 프로세스로 만듭니다.

예를 들어 Intent.FLAG_RECEIVER_FOREGROUND 사용자가 사용자가 메시지를 보낼 때 PendingIntent.getBroadcast에 전달되는 Intent에 대한 플래그 탭하는 동작입니다.

동적 항목이 포함된 정확한 미리보기 빌드

그림 1: 목록 항목이 표시되지 않는 위젯 미리보기

이 섹션에서는 컬렉션 뷰가 있는 위젯(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>

다음 항목의 previewLayout 속성을 제공할 때 미리보기 레이아웃 파일을 지정합니다. AppWidgetProviderInfo 메타데이터 실제 위젯 레이아웃 지정 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>

가짜 목록 항목을 제공하려면 레이아웃을 여러 번 포함할 수 있지만 이렇게 하면 각 목록 항목이 동일해집니다. 고유한 목록 항목을 제공하려면 다음 단계를 따르세요.

  1. 텍스트 값의 속성 집합을 만듭니다.

    <resources>
        <attr name="widgetTitle" format="string" />
        <attr name="widgetContent" format="string" />
    </resources>
    
  2. 다음 속성을 사용하여 텍스트를 설정합니다.

    <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>
    
  3. 미리보기에 필요한 만큼 스타일을 만듭니다. 각 스타일의 값을 다시 정의합니다.

    <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>
    
  4. 미리보기 레이아웃에서 모조 항목에 스타일을 적용합니다.

    <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>