Halaman ini menjelaskan praktik yang direkomendasikan untuk membuat widget lanjutan guna memberikan pengalaman pengguna yang lebih baik.
Pengoptimalan untuk mengupdate konten widget
Mengupdate konten widget dapat menghabiskan banyak biaya secara komputasi. Untuk menghemat konsumsi baterai, optimalkan jenis, frekuensi, dan waktu update.
Jenis update widget
Ada tiga cara untuk mengupdate widget: update lengkap, update sebagian, dan pembaruan data untuk widget koleksi. Masing-masing memiliki biaya komputasi dan konsekuensi yang berbeda.
Berikut ini penjelasan setiap jenis update dan menyediakan cuplikan kode untuk setiap jenis update.
Update penuh: panggil
AppWidgetManager.updateAppWidget(int, android.widget.RemoteViews)
untuk mengupdate widget sepenuhnya. Tindakan ini menggantikanRemoteViews
yang disediakan sebelumnya denganRemoteViews
baru. Update ini adalah update 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 mengupdate bagian widget. Ini akan menggabungkanRemoteViews
baru denganRemoteViews
yang disediakan sebelumnya. Metode ini diabaikan jika widget tidak menerima setidaknya satu update lengkap 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);
Pemuatan ulang data pengumpulan: panggil
AppWidgetManager.notifyAppWidgetViewDataChanged
untuk membatalkan data tampilan koleksi di widget Anda. Tindakan ini akan memicuRemoteViewsFactory.onDataSetChanged
. Untuk sementara, data lama akan ditampilkan di widget. Anda dapat melakukan tugas-tugas mahal secara aman secara sinkron dengan 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 dalam aplikasi, selama aplikasi tersebut memiliki
UID yang sama dengan class
AppWidgetProvider
yang sesuai.
Menentukan frekuensi update widget
Widget diupdate secara berkala bergantung pada nilai yang diberikan untuk
atribut
updatePeriodMillis
. Widget dapat diupdate sebagai respons terhadap interaksi pengguna, update
siaran, atau keduanya.
Perbarui secara berkala
Anda dapat mengontrol frekuensi update berkala dengan menentukan nilai untuk
AppWidgetProviderInfo.updatePeriodMillis
dalam XML appwidget-provider
. Setiap update akan memicu metode AppWidgetProvider.onUpdate()
, tempat Anda dapat menempatkan kode untuk mengupdate widget. Namun, pertimbangkan alternatif untuk
update penerima siaran yang dijelaskan di
bagian berikut jika widget Anda perlu memuat data secara asinkron atau memerlukan waktu lebih
dari 10 detik untuk diupdate, karena setelah 10 detik, sistem akan menganggap
BroadcastReceiver
sebagai non-responsif.
updatePeriodMillis
tidak mendukung nilai kurang dari 30 menit. Namun, jika
ingin menonaktifkan update berkala, Anda dapat menentukan 0.
Anda dapat mengizinkan pengguna menyesuaikan frekuensi update dalam konfigurasi. Misalnya, mereka mungkin ingin ticker saham diperbarui setiap 15 menit atau hanya
empat kali sehari. Dalam hal ini, tetapkan updatePeriodMillis
ke 0 dan gunakan
WorkManager
sebagai gantinya.
Pembaruan sebagai respons terhadap interaksi pengguna
Berikut ini beberapa cara yang direkomendasikan untuk mengupdate widget berdasarkan interaksi pengguna:
Dari aktivitas aplikasi: langsung panggil
AppWidgetManager.updateAppWidget
sebagai respons terhadap interaksi pengguna, seperti ketukan pengguna.Dari interaksi jarak jauh, seperti notifikasi atau widget aplikasi: buat
PendingIntent
, lalu update widget dariActivity
,Broadcast
, atauService
yang dipanggil. Anda dapat memilih prioritas Anda sendiri. Misalnya, jika memilihBroadcast
untukPendingIntent
, Anda dapat memilih siaran latar depan untuk memberikan prioritasBroadcastReceiver
.
Mengupdate sebagai respons terhadap peristiwa siaran
Contoh peristiwa siaran yang memerlukan update widget adalah saat pengguna mengambil foto. Dalam hal ini, Anda perlu mengupdate 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 tindakan ini menghabiskan resource perangkat, gunakan ini dengan hati-hati dan hanya dengarkan
siaran tertentu. Dengan diperkenalkannya batasan
siaran di Android
7.0 (API level 24) dan Android 8.0 (API level 26), aplikasi tidak dapat mendaftarkan siaran
implisit dalam manifesnya, dengan
pengecualian tertentu.
Pertimbangan saat mengupdate widget dari BroadcastReceiver
Jika widget diupdate dari BroadcastReceiver
, termasuk
AppWidgetProvider
, perhatikan pertimbangan berikut terkait
durasi dan prioritas update widget.
Durasi update
Biasanya, sistem memungkinkan penerima siaran, yang biasanya berjalan di thread utama aplikasi, berjalan hingga 10 detik sebelum dianggap tidak responsif dan memicu error Aplikasi Tidak Merespons (ANR). Jika perlu waktu lebih lama untuk mengupdate widget, pertimbangkan alternatif berikut:
Menjadwalkan tugas menggunakan
WorkManager
.Beri penerima lebih banyak waktu dengan metode
goAsync
. Hal ini memungkinkan penerima mengeksekusi selama 30 detik.
Lihat Pertimbangan keamanan dan praktik terbaik untuk mengetahui informasi selengkapnya.
Prioritas update
Secara default, siaran—termasuk yang dibuat menggunakan
AppWidgetProvider.onUpdate
—dijalankan sebagai proses latar belakang. Ini berarti
resource sistem yang kelebihan beban dapat menyebabkan penundaan dalam pemanggilan penerima
siaran. Untuk memprioritaskan siaran, jadikan sebagai proses latar depan.
Misalnya, tambahkan flag 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 skalabel dengan secara langsung menyediakan tata letak widget yang sebenarnya akan menurunkan 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 alat pilih
widget, sebaiknya pertahankan file tata letak terpisah yang ditujukan hanya untuk
pratinjau. File tata letak terpisah ini mencakup tata letak widget yang sebenarnya dan
tampilan koleksi placeholder dengan item palsu. Misalnya, Anda dapat meniru
ListView
dengan menyediakan LinearLayout
placeholder dengan beberapa item daftar
palsu.
Untuk menggambarkan 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
metadata AppWidgetProviderInfo
. Anda tetap menentukan tata letak widget yang sebenarnya
untuk atribut initialLayout
dan menggunakan tata letak widget yang 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
. Mungkin
lebih rumit untuk menyediakan item palsu jika item merupakan 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 identik. Untuk menyediakan 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 berikut 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 sebanyak mungkin gaya 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 di 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>