Halaman ini menjelaskan praktik yang direkomendasikan untuk membuat widget yang lebih canggih demi pengalaman pengguna yang lebih baik.
Pengoptimalan untuk memperbarui konten widget
Memperbarui konten widget dapat memerlukan biaya komputasi yang tinggi. Untuk menghemat konsumsi baterai, optimalkan jenis, frekuensi, dan waktu pembaruan.
Jenis update widget
Ada tiga cara untuk memperbarui widget: pembaruan penuh, pembaruan sebagian, dan, dalam kasus widget koleksi, refresh data. Setiap metode memiliki biaya komputasi dan konsekuensi yang berbeda.
Berikut ini menjelaskan setiap jenis pembaruan dan memberikan cuplikan kode untuk setiap jenis.
Update penuh: panggil
AppWidgetManager.updateAppWidget(int, android.widget.RemoteViews)
untuk mengupdate widget sepenuhnya. Tindakan ini menggantikanRemoteViews
yang sebelumnya diberikan denganRemoteViews
baru. Ini adalah pembaruan yang paling mahal secara komputasi.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);
Update sebagian: panggil
AppWidgetManager.partiallyUpdateAppWidget
untuk memperbarui bagian widget. Tindakan ini menggabungkanRemoteViews
baru denganRemoteViews
yang sebelumnya diberikan. Metode ini diabaikan jika widget tidak menerima setidaknya satu update penuh melaluiupdateAppWidget(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);
Pembaruan data pengumpulan: panggil
AppWidgetManager.notifyAppWidgetViewDataChanged
untuk membatalkan validasi data tampilan koleksi di widget Anda. Tindakan ini akan memicuRemoteViewsFactory.onDataSetChanged
. Sementara itu, data lama ditampilkan di widget. Anda dapat melakukan tugas berat secara sinkron dengan aman menggunakan metode ini.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);
Anda dapat memanggil metode ini dari mana saja di aplikasi, asalkan aplikasi memiliki
UID yang sama dengan class
AppWidgetProvider
yang sesuai.
Menentukan seberapa sering widget diperbarui
Widget diperbarui secara berkala, bergantung pada nilai yang diberikan untuk atribut
updatePeriodMillis
. Widget dapat diperbarui sebagai respons terhadap interaksi pengguna, menyiarkan pembaruan, atau keduanya.
Update secara berkala
Anda dapat mengontrol frekuensi update berkala dengan menentukan nilai untuk
AppWidgetProviderInfo.updatePeriodMillis
dalam XML appwidget-provider
. Setiap
pembaruan memicu metode AppWidgetProvider.onUpdate()
, tempat Anda
dapat menempatkan kode untuk memperbarui widget. Namun, pertimbangkan alternatif untuk
update penerima siaran yang dijelaskan di
bagian berikutnya jika widget Anda perlu memuat data secara asinkron atau memerlukan waktu lebih
dari 10 detik untuk diupdate, karena setelah 10 detik, sistem menganggap
BroadcastReceiver
tidak responsif.
updatePeriodMillis
tidak mendukung nilai kurang dari 30 menit. Namun, jika Anda ingin menonaktifkan update berkala, Anda dapat menentukan 0.
Anda dapat mengizinkan pengguna menyesuaikan frekuensi update dalam konfigurasi. Misalnya, mereka mungkin ingin mengupdate simbol saham setiap 15 menit atau hanya empat kali dalam satu hari. Dalam hal ini, tetapkan updatePeriodMillis
ke 0 dan gunakan
WorkManager
sebagai gantinya.
Memperbarui sebagai respons terhadap interaksi pengguna
Berikut beberapa cara yang direkomendasikan untuk memperbarui widget berdasarkan interaksi pengguna:
Dari aktivitas aplikasi: panggil
AppWidgetManager.updateAppWidget
secara langsung sebagai respons terhadap interaksi pengguna, seperti ketukan pengguna.Dari interaksi jarak jauh, seperti notifikasi atau widget aplikasi: buat
PendingIntent
, lalu perbarui widget dariActivity
,Broadcast
, atauService
yang dipanggil. Anda dapat memilih prioritas Anda sendiri. Misalnya, jika Anda memilihBroadcast
untukPendingIntent
, Anda dapat memilih siaran latar depan untuk memberikan prioritasBroadcastReceiver
.
Memperbarui sebagai respons terhadap peristiwa siaran
Contoh peristiwa siaran yang memerlukan update widget adalah saat pengguna mengambil foto. Dalam hal ini, Anda ingin memperbarui widget saat foto baru terdeteksi.
Anda dapat menjadwalkan tugas dengan JobScheduler
dan menentukan siaran sebagai
pemicu menggunakan
metode
JobInfo.Builder.addTriggerContentUri
.
Anda juga dapat mendaftarkan BroadcastReceiver
untuk siaran—misalnya,
memproses
ACTION_LOCALE_CHANGED
.
Namun, karena hal ini menggunakan resource perangkat, gunakan dengan hati-hati dan dengarkan
hanya siaran tertentu. Dengan diperkenalkannya batasan siaran di Android 7.0 (level API 24) dan Android 8.0 (level API 26), aplikasi tidak dapat mendaftarkan siaran implisit dalam manifesnya, dengan pengecualian tertentu.
Pertimbangan saat memperbarui widget dari BroadcastReceiver
Jika widget diupdate dari BroadcastReceiver
, termasuk
AppWidgetProvider
, perhatikan pertimbangan berikut terkait
durasi dan prioritas update widget.
Durasi update
Sebagai aturan, sistem memungkinkan penerima siaran, yang biasanya berjalan di
thread utama aplikasi, berjalan hingga 10 detik sebelum menganggapnya tidak responsif dan
memicu error Aplikasi Tidak
Merespons (ANR). Untuk menghindari pemblokiran
thread utama saat menangani siaran, gunakan metode
goAsync
. Jika widget memerlukan waktu lebih lama untuk diperbarui, pertimbangkan untuk menjadwalkan tugas menggunakan WorkManager
.
Caution: Any work you do here blocks further broadcasts until it completes,
so it can slow the receiving of later events.
Lihat Pertimbangan dan praktik terbaik keamanan untuk mengetahui informasi selengkapnya.
Prioritas update
Secara default, siaran—termasuk yang dibuat menggunakan
AppWidgetProvider.onUpdate
—berjalan sebagai proses latar belakang. Artinya, resource sistem yang kelebihan beban dapat menyebabkan penundaan dalam pemanggilan penerima siaran. Untuk memprioritaskan siaran, jadikan siaran sebagai proses latar depan.
Misalnya, tambahkan tanda
Intent.FLAG_RECEIVER_FOREGROUND
ke Intent
yang diteruskan ke PendingIntent.getBroadcast
saat pengguna
mengetuk bagian tertentu dari widget.
Membuat pratinjau akurat yang menyertakan item dinamis

Bagian ini menjelaskan pendekatan yang direkomendasikan untuk menampilkan beberapa item dalam pratinjau widget untuk widget dengan tampilan
koleksi—yaitu, widget yang menggunakan
ListView
, GridView
, atau StackView
.
Jika widget Anda menggunakan salah satu tampilan ini, membuat pratinjau yang dapat diskalakan dengan memberikan tata letak widget yang sebenarnya secara langsung akan menurunkan kualitas pengalaman saat pratinjau widget tidak menampilkan item. Hal ini terjadi karena data tampilan koleksi ditetapkan secara dinamis saat runtime, dan terlihat mirip dengan gambar yang ditampilkan pada gambar 1.
Agar pratinjau widget dengan tampilan koleksi ditampilkan dengan benar di pemilih widget, sebaiknya pertahankan file tata letak terpisah yang ditetapkan hanya untuk pratinjau. File tata letak terpisah ini mencakup tata letak widget sebenarnya dan tampilan kumpulan placeholder dengan item palsu. Misalnya, Anda dapat meniru
ListView
dengan menyediakan placeholder LinearLayout
dengan beberapa item
daftar palsu.
Untuk mengilustrasikan contoh ListView
, mulailah dengan file tata letak terpisah:
// 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>
Tentukan file tata letak pratinjau saat memberikan atribut previewLayout
dari
metadata AppWidgetProviderInfo
. Anda tetap menentukan tata letak widget sebenarnya
untuk atribut initialLayout
dan menggunakan tata letak widget sebenarnya saat
membuat RemoteViews
saat runtime.
<appwidget-provider
previewLayout="@layout/widget_previe"
initialLayout="@layout/widget_view" />
Item daftar kompleks
Contoh di bagian sebelumnya menyediakan item daftar palsu, karena item daftar adalah objek TextView
. Penyediaan item palsu bisa
lebih rumit jika itemnya memiliki tata letak yang kompleks.
Pertimbangkan item daftar yang ditentukan dalam widget_list_item.xml
dan terdiri dari
dua objek 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>
Untuk menyediakan item daftar palsu, Anda dapat menyertakan tata letak beberapa kali, tetapi hal ini menyebabkan setiap item daftar menjadi identik. Untuk memberikan item daftar unik, ikuti langkah-langkah berikut:
Buat serangkaian atribut untuk nilai teks:
<resources> <attr name="widgetTitle" format="string" /> <attr name="widgetContent" format="string" /> </resources>
Gunakan atribut ini untuk menyetel teks:
<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>
Buat gaya sebanyak yang diperlukan untuk pratinjau. Tentukan ulang nilai di setiap gaya:
<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>
Terapkan gaya pada item palsu dalam tata letak pratinjau:
<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>