สร้างวิดเจ็ตขั้นสูง

ลองใช้วิธีแบบ Compose
Jetpack Compose เป็นชุดเครื่องมือ UI ที่แนะนำสำหรับ Android ดูวิธีสร้างวิดเจ็ตโดยใช้ API รูปแบบ Compose

หน้านี้อธิบายแนวทางปฏิบัติที่แนะนำสำหรับการสร้างวิดเจ็ตขั้นสูงเพื่อประสบการณ์ของผู้ใช้ที่ดียิ่งขึ้น

การเพิ่มประสิทธิภาพสำหรับการอัปเดตเนื้อหาวิดเจ็ต

การอัปเดตเนื้อหาวิดเจ็ตอาจใช้ทรัพยากรในการประมวลผลสูง เพื่อประหยัดการใช้แบตเตอรี่ ให้เพิ่มประสิทธิภาพประเภท ความถี่ และเวลาในการอัปเดต

ประเภทการอัปเดตวิดเจ็ต

การอัปเดตวิดเจ็ตทำได้ 3 วิธี ได้แก่ การอัปเดตทั้งหมด การอัปเดตบางส่วน และการรีเฟรชข้อมูลในกรณีที่เป็นวิดเจ็ตคอลเล็กชัน แต่ละวิธีมีต้นทุนและผลกระทบด้านการประมวลผลที่แตกต่างกัน

ข้อมูลต่อไปนี้จะอธิบายการอัปเดตแต่ละประเภทและให้ข้อมูลโค้ดสำหรับการอัปเดตแต่ละรายการ

  • การอัปเดตแบบเต็ม: เรียกใช้ AppWidgetManager.updateAppWidget(int, android.widget.RemoteViews) เพื่ออัปเดตวิดเจ็ตอย่างเต็มรูปแบบ ซึ่งจะแทนที่ RemoteViews ที่ระบุไว้ก่อนหน้านี้ด้วย RemoteViews ใหม่ การอัปเดตนี้ใช้ทรัพยากรในการประมวลผลมากที่สุด

val appWidgetManager = AppWidgetManager.getInstance(context)
val remoteViews = RemoteViews(context.packageName, R.layout.widgetlayout).apply {
    setTextViewText(R.id.textview_widget_layout1, "Updated text1")
    setTextViewText(R.id.textview_widget_layout2, "Updated text2")
}
appWidgetManager.updateAppWidget(appWidgetId, remoteViews)

  • การอัปเดตบางส่วน: เรียกใช้ AppWidgetManager.partiallyUpdateAppWidget เพื่ออัปเดตบางส่วนของวิดเจ็ต ซึ่งจะรวม RemoteViews ใหม่เข้ากับ RemoteViews ที่ระบุไว้ก่อนหน้านี้ ระบบจะไม่สนใจเมธอดนี้หากวิดเจ็ตไม่ได้รับการอัปเดตแบบเต็มอย่างน้อย 1 ครั้งผ่าน updateAppWidget(int[], RemoteViews)

val appWidgetManager = AppWidgetManager.getInstance(context)
val remoteViews = RemoteViews(context.packageName, R.layout.widgetlayout).apply {
    setTextViewText(R.id.textview_widget_layout, "Updated text")
}
appWidgetManager.partiallyUpdateAppWidget(appWidgetId, remoteViews)

  • การรีเฟรชข้อมูลคอลเล็กชัน: เรียกใช้ AppWidgetManager.notifyAppWidgetViewDataChanged เพื่อทำให้ข้อมูลของมุมมองคอลเล็กชันในวิดเจ็ตใช้งานไม่ได้ ซึ่งจะทริกเกอร์ RemoteViewsFactory.onDataSetChanged ในระหว่างนี้ ข้อมูลเก่าจะแสดงในวิดเจ็ต คุณสามารถทำงานที่มีค่าใช้จ่ายสูงพร้อมกันได้อย่างปลอดภัยด้วยวิธีนี้

val appWidgetManager = AppWidgetManager.getInstance(context)
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_listview)

คุณเรียกใช้เมธอดเหล่านี้ได้จากทุกที่ในแอป ตราบใดที่แอปมี UID เดียวกันกับคลาส AppWidgetProvider ที่เกี่ยวข้อง

กำหนดความถี่ในการอัปเดตวิดเจ็ต

วิดเจ็ตจะอัปเดตเป็นระยะๆ โดยขึ้นอยู่กับค่าที่ระบุสำหรับแอตทริบิวต์ updatePeriodMillis วิดเจ็ตสามารถอัปเดตเพื่อตอบสนองต่อการโต้ตอบของผู้ใช้ การอัปเดตการออกอากาศ หรือทั้ง 2 อย่าง

อัปเดตเป็นระยะๆ

คุณควบคุมความถี่ของการอัปเดตเป็นระยะได้โดยระบุค่าสำหรับ AppWidgetProviderInfo.updatePeriodMillis ใน XML ของ appwidget-provider การอัปเดตแต่ละครั้งจะทริกเกอร์เมธอด AppWidgetProvider.onUpdate() ซึ่งเป็นตำแหน่งที่คุณสามารถวางโค้ดเพื่ออัปเดตวิดเจ็ตได้ อย่างไรก็ตาม ให้พิจารณาทางเลือกสำหรับการอัปเดต Broadcast Receiver ที่อธิบายไว้ในส่วนต่อไปนี้หากวิดเจ็ตต้องโหลดข้อมูลแบบไม่พร้อมกันหรือใช้เวลาอัปเดตนานกว่า 10 วินาที เนื่องจากหลังจากผ่านไป 10 วินาที ระบบจะถือว่า BroadcastReceiver ไม่ตอบสนอง

updatePeriodMillis ไม่รองรับค่าที่น้อยกว่า 30 นาที แต่หากต้องการปิดใช้การอัปเดตเป็นระยะ ให้ระบุ 0

คุณอนุญาตให้ผู้ใช้ปรับความถี่ของการอัปเดตในการกำหนดค่าได้ ตัวอย่างเช่น ผู้ใช้อาจต้องการให้แถบแสดงราคาหุ้นอัปเดตทุก 15 นาที หรือเพียง 4 ครั้งต่อวัน ในกรณีนี้ ให้ตั้งค่า updatePeriodMillis เป็น 0 และใช้ WorkManager แทน

อัปเดตเพื่อตอบสนองต่อการโต้ตอบของผู้ใช้

วิธีที่แนะนำในการอัปเดตวิดเจ็ตตามการโต้ตอบของผู้ใช้มีดังนี้

  • จากกิจกรรมของแอป: เรียกใช้ AppWidgetManager.updateAppWidget โดยตรงเพื่อตอบสนองต่อการโต้ตอบของผู้ใช้ เช่น การแตะของผู้ใช้

  • จากการโต้ตอบระยะไกล เช่น การแจ้งเตือนหรือวิดเจ็ตแอป ให้สร้าง PendingIntent แล้วอัปเดตวิดเจ็ตจาก Activity, Broadcast หรือ Service ที่เรียกใช้ คุณเลือกลำดับความสำคัญเองได้ เช่น หากคุณเลือก Broadcast สำหรับ PendingIntent คุณสามารถเลือกการออกอากาศในเบื้องหน้าเพื่อให้ความสำคัญกับ BroadcastReceiver ได้

อัปเดตเพื่อตอบสนองต่อเหตุการณ์การออกอากาศ

ตัวอย่างเหตุการณ์การประกาศที่ต้องอัปเดตวิดเจ็ตคือเมื่อผู้ใช้ถ่ายรูป ในกรณีนี้ คุณต้องการอัปเดตวิดเจ็ตเมื่อตรวจพบรูปภาพใหม่

คุณสามารถกำหนดเวลางานด้วย WorkManager และระบุทริกเกอร์ URI เนื้อหาโดยใช้เมธอด Constraints.Builder.addContentUriTrigger

นอกจากนี้ คุณยังลงทะเบียน BroadcastReceiver สำหรับการออกอากาศได้ด้วย เช่น การฟัง ACTION_LOCALE_CHANGED อย่างไรก็ตาม เนื่องจากวิธีนี้ใช้ทรัพยากรของอุปกรณ์ โปรดใช้อย่างระมัดระวังและฟังเฉพาะการออกอากาศที่เฉพาะเจาะจงเท่านั้น เมื่อมีการเปิดตัวข้อจำกัดการบรอดแคสต์ใน Android 7.0 (ระดับ API 24) และ Android 8.0 (ระดับ API 26) แอปจะไม่สามารถลงทะเบียนการกระจายข้อมูลแบบไม่เจาะจงปลายทางในไฟล์ Manifest ได้ โดยมีข้อยกเว้นบางประการ

ข้อควรพิจารณาเมื่ออัปเดตวิดเจ็ตจาก BroadcastReceiver

หากวิดเจ็ตได้รับการอัปเดตจาก BroadcastReceiver รวมถึง AppWidgetProvider โปรดคำนึงถึงข้อควรพิจารณาต่อไปนี้เกี่ยวกับระยะเวลาและลำดับความสำคัญของการอัปเดตวิดเจ็ต

ระยะเวลาของการอัปเดต

โดยปกติแล้ว ระบบจะอนุญาตให้ Broadcast Receiver ซึ่งมักจะทำงานในเธรดหลักของแอปทำงานได้นานถึง 10 วินาทีก่อนที่จะพิจารณาว่าไม่ตอบสนองและทริกเกอร์ข้อผิดพลาดแอปพลิเคชันไม่ตอบสนอง (ANR) หากต้องการหลีกเลี่ยงการบล็อกเทรดหลักขณะจัดการการออกอากาศ ให้ใช้เมธอด goAsync หากใช้เวลานานกว่านั้นในการอัปเดตวิดเจ็ต ให้พิจารณากำหนดเวลางานโดยใช้ WorkManager

Caution: Any work you do here blocks further broadcasts until it completes,
so it can slow the receiving of later events.

ดูข้อมูลเพิ่มเติมได้ที่ข้อควรพิจารณาด้านความปลอดภัยและแนวทางปฏิบัติแนะนำ

ลำดับความสำคัญของการอัปเดต

โดยค่าเริ่มต้น การออกอากาศ ซึ่งรวมถึงการออกอากาศที่ทำโดยใช้ AppWidgetProvider.onUpdate จะทำงานเป็นกระบวนการเบื้องหลัง ซึ่งหมายความว่าทรัพยากรระบบที่ทำงานหนักเกินไปอาจทำให้การเรียกใช้ Broadcast Receiver ล่าช้า หากต้องการจัดลำดับความสำคัญของการออกอากาศ ให้กำหนดให้เป็นกระบวนการที่ทำงานอยู่เบื้องหน้า

เช่น เพิ่มแฟล็ก Intent.FLAG_RECEIVER_FOREGROUND ลงใน Intent ที่ส่งไปยัง PendingIntent.getBroadcast เมื่อผู้ใช้แตะส่วนใดส่วนหนึ่งของวิดเจ็ต