本頁面說明建立進階小工具的最佳做法,以提供更優質的使用者體驗。
更新小工具內容的最佳化調整
更新小工具內容的運算費用可能非常高。省電模式 最佳化更新類型、頻率和時間
小工具更新類型
更新小工具的方法有三種:完整更新、部分更新,以及在收藏夾小工具的情況下,資料重新整理。每種方法的運算成本和影響各不相同。
以下說明各項更新類型,並提供各項更新類型的程式碼片段。
完整的更新:請呼叫
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);
只要應用程式與對應的 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 更新小工具的注意事項
如果小工具是從 BroadcastReceiver
更新,包括
AppWidgetProvider
,請注意下列關於
更新間隔的時間和優先順序
更新時間長度
一般來說,系統會讓廣播接收器 (通常在應用程式的主要執行緒中執行) 執行最多 10 秒,然後才視為無回應,並觸發「應用程式無回應」(ANR) 錯誤。如果處理時間 更新小工具,請考慮以下替代方案:
使用
WorkManager
安排工作。使用
goAsync
方法,讓接收端有更多時間。這可讓接收端執行 30 秒。
詳情請參閱「安全性考量事項和最佳做法」。
更新的優先順序
根據預設,廣播內容,包括使用
AppWidgetProvider.onUpdate
:以背景程序的形式執行。這表示系統資源過載可能會導致廣播接收器的叫用延遲。如要將廣播訊息設為優先,請將其設為前景程序。
舉例來說,當使用者輕觸小工具的特定部分時,您可以將 Intent.FLAG_RECEIVER_FOREGROUND
標記新增至傳遞至 PendingIntent.getBroadcast
的 Intent
。
建立包含動態項目的準確預覽畫面
本節說明在小工具預覽畫面中顯示多個項目的建議做法,適用於含有集合檢視畫面的小工具,也就是使用 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>
如果想提供假的清單項目,可以多次加入版面配置,但這 會導致每個清單項目相同。如要提供不重複的清單項目,請按照下列步驟操作:
為文字值建立一組屬性:
<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>