แอปที่เปิดตลอดเวลาและโหมดแอมเบียนท์ของระบบ

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

การทำให้แอปมองเห็นได้ตลอดเวลาจะส่งผลต่อระยะเวลาการใช้งานแบตเตอรี่อย่างมาก ดังนั้นโปรดพิจารณาผลกระทบต่อพลังงานเมื่อเพิ่มฟีเจอร์นี้

หัวข้อสำคัญ

เมื่อแอป Wear OS แสดงแบบเต็มหน้าจอ แอปจะอยู่ในสถานะพลังงาน 2 สถานะต่อไปนี้

  • โต้ตอบ: สถานะพลังงานสูงที่หน้าจอมีความสว่างเต็มที่ ทำให้ผู้ใช้โต้ตอบได้อย่างเต็มที่
  • แอมเบียนท์: สถานะพลังงานต่ำที่จอแสดงผลจะหรี่ลงเพื่อประหยัดพลังงาน ในสถานะนี้ UI ของแอปจะยังคงแสดงแบบเต็มหน้าจอ แต่ระบบอาจเปลี่ยนลักษณะที่ปรากฏโดยทำให้เบลอหรือวางซ้อนเนื้อหา เช่น เวลา สถานะนี้เรียกอีกอย่างว่าโหมดแอมเบียนท์

ระบบปฏิบัติการจะควบคุมการเปลี่ยนสถานะระหว่างสถานะเหล่านี้

แอปที่ทำงานตลอดเวลา คือแอปพลิเคชันที่แสดงเนื้อหาทั้งในสถานะ โต้ตอบและ แอมเบียนท์

เมื่อแอปที่ทำงานตลอดเวลาแสดง UI ของตัวเองต่อไปขณะที่อุปกรณ์อยู่ในสถานะ แอมเบียนท์พลังงานต่ำ ระบบจะอธิบายว่าแอปอยู่ในโหมดแอมเบีย็กทีฟ

การเปลี่ยนสถานะของระบบและลักษณะการทำงานเริ่มต้น

เมื่อแอปอยู่ในเบื้องหน้า ระบบจะจัดการการเปลี่ยนสถานะพลังงานตามการหมดเวลา 2 รายการที่ทริกเกอร์โดยการไม่มีการใช้งานของผู้ใช้

  • การหมดเวลา #1: สถานะโต้ตอบเป็นสถานะแอมเบียนท์: หลังจากไม่มีการใช้งานของผู้ใช้เป็นระยะเวลาหนึ่ง อุปกรณ์จะเข้าสู่สถานะ แอมเบียนท์
  • การหมดเวลา #2: กลับไปที่หน้าปัดนาฬิกา: หลังจากไม่มีการใช้งานเป็นระยะเวลาหนึ่ง ระบบอาจซ่อนแอปปัจจุบันและแสดงหน้าปัดนาฬิกา

ทันทีหลังจากที่ระบบเปลี่ยนสถานะครั้งแรกเป็นสถานะ แอมเบียนท์ ลักษณะการทำงานเริ่มต้นจะขึ้นอยู่กับเวอร์ชัน Wear OS และการกำหนดค่าของแอป ดังนี้

  • ใน Wear OS 5 และต่ำกว่า ระบบจะแสดงภาพหน้าจอเบลอของ แอปพลิเคชันที่หยุดชั่วคราว โดยมีเวลาวางซ้อนอยู่ด้านบน สถานะนี้แสดง ด้วยโหนด "AOD Lite" ใน โฟลว์ชาร์ตต่อไปนี้
  • ใน Wear OS 6 ขึ้นไป หากแอปกำหนดเป้าหมายเป็น SDK 36 ขึ้นไป ระบบจะถือว่าเป็นแอปที่ ทำงานตลอดเวลา จอแสดงผลจะหรี่ลง แต่แอปพลิเคชันจะทำงานต่อไปและยังคงมองเห็นได้ (การอัปเดตอาจเกิดขึ้นไม่บ่อยนัก เช่น ทุกๆ 1 นาที) สถานะนี้แสดงด้วยโหนด "Global AOD" ใน โฟลว์ชาร์ต ต่อไปนี้

ปรับแต่งลักษณะการทำงานสำหรับสถานะแอมเบียนท์

ไม่ว่าลักษณะการทำงานเริ่มต้นของระบบจะเป็นอย่างไร ใน Wear OS ทุกเวอร์ชัน คุณสามารถ ปรับแต่งลักษณะที่ปรากฏหรือลักษณะการทำงานของแอปขณะอยู่ในสถานะ แอมเบียนท์ได้โดย ใช้ AmbientLifecycleObserver เพื่อฟังการเรียกกลับเมื่อมีการเปลี่ยนสถานะ สถานะนี้แสดงด้วยโหนด "โหมดแอมเบีย็กทีฟ" ใน โฟลว์ชาร์ตต่อไปนี้

ใช้ AmbientLifecycleObserver

หากต้องการตอบสนองต่อเหตุการณ์โหมดแอมเบียนท์ ให้ใช้คลาส AmbientLifecycleObserver โดยทำดังนี้

  1. ใช้อินเทอร์เฟซ AmbientLifecycleObserver.AmbientLifecycleCallback ใช้วิธี onEnterAmbient() เพื่อปรับ UI สำหรับสถานะพลังงานต่ำ และ onExitAmbient() เพื่อคืนค่าให้แสดงแบบโต้ตอบเต็มรูปแบบ

    val ambientCallback = object : AmbientLifecycleObserver.AmbientLifecycleCallback {
        override fun onEnterAmbient(ambientDetails: AmbientLifecycleObserver.AmbientDetails) {
            // ... Called when moving from interactive mode into ambient mode.
            // Adjust UI for low-power state: dim colors, hide non-essential elements.
        }
    
        override fun onExitAmbient() {
            // ... Called when leaving ambient mode, back into interactive mode.
            // Restore full UI.
        }
    
        override fun onUpdateAmbient() {
            // ... Called by the system periodically (typically once per minute)
            // to allow the app to update its display while in ambient mode.
        }
    }

  2. สร้าง AmbientLifecycleObserver และลงทะเบียนกับวงจรการทำงานของกิจกรรมหรือ Composable

    private val ambientObserver = AmbientLifecycleObserver(activity, ambientCallback)
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        lifecycle.addObserver(ambientObserver)
    
        // ...
    }

  3. เรียก removeObserver() เพื่อนำ Observer ออกใน onDestroy()

    override fun onDestroy() {
        super.onDestroy()
        lifecycle.removeObserver(ambientObserver)
    
        // ...
    }

สำหรับนักพัฒนาแอปที่ใช้ Jetpack Compose ไลบรารี Horologist มียูทิลิตีที่เป็นประโยชน์ คือ AmbientAware Composable ซึ่งช่วยลดความซับซ้อนในการใช้รูปแบบนี้

TimeText ที่รองรับโหมดแอมเบียนท์

ใน Wear OS 6 TimeText วิดเจ็ต รองรับโหมดแอมเบียนท์ ซึ่งเป็นข้อยกเว้นที่ไม่ต้องใช้ Observer ที่กำหนดเอง โดยจะอัปเดตโดยอัตโนมัติทุกๆ 1 นาทีเมื่ออุปกรณ์อยู่ในสถานะ แอมเบียนท์โดยไม่ต้องใช้โค้ดเพิ่มเติม

โฟลว์ชาร์ตลักษณะการทำงานของโหมดแอมเบียนท์

โฟลว์ชาร์ตต่อไปนี้แสดงให้เห็นว่าระบบกำหนดลักษณะการทำงานของโหมดแอมเบียนท์อย่างไร โดยอิงตามเวอร์ชัน Wear OS ของอุปกรณ์ targetSdkVersion ของแอป และแอปใช้ AmbientLifecycleCallback หรือไม่

โฟลว์ชาร์ตแสดงตรรกะการตัดสินใจสำหรับโหมดแอมเบียนท์ของ Wear OS โดยจะแสดงวิธีที่เวอร์ชันระบบปฏิบัติการของอุปกรณ์และการกำหนดค่าของแอปกำหนดผลลัพธ์ 1 ใน 3 อย่าง ได้แก่ ภาพซ้อนทับเบลอ, AOD ทั่วโลก หรือโหมด Ambiactive ที่แอปจัดการ
รูปที่ 1 โฟลว์ชาร์ตแสดงตรรกะการตัดสินใจสำหรับโหมดแอมเบียนท์ของ Wear OS

ควบคุมระยะเวลาที่หน้าจอเปิดอยู่

ส่วนต่อไปนี้จะอธิบายวิธีจัดการระยะเวลาที่แอปจะแสดงบนหน้าจอ

ป้องกันไม่ให้กลับไปที่หน้าปัดนาฬิกาด้วยกิจกรรมต่อเนื่องหรือการอัปเดตแบบสด

หลังจากอยู่ในสถานะ แอมเบียนท์เป็นระยะเวลาหนึ่ง (การหมดเวลา #2) โดยปกติแล้วระบบจะกลับไปที่หน้าปัดนาฬิกา ผู้ใช้สามารถกำหนดระยะเวลาการหมดเวลาได้ในการตั้งค่าระบบ ในบางกรณีการใช้งาน เช่น ผู้ใช้ติดตามการออกกำลังกาย แอปอาจต้องแสดงต่อไปนานขึ้น

ใน Wear OS 5 ขึ้นไป คุณสามารถป้องกันไม่ให้เกิดเหตุการณ์นี้ได้โดยใช้กิจกรรมต่อเนื่อง หากแอปแสดงข้อมูลเกี่ยวกับงานของผู้ใช้ที่กำลังดำเนินการอยู่ เช่น เซสชันการออกกำลังกาย คุณสามารถใช้ Ongoing Activity API เพื่อให้ แอปแสดงต่อไปจนกว่างานจะสิ้นสุด หากผู้ใช้กลับไปที่หน้าปัดนาฬิกาด้วยตนเอง ตัวบ่งชี้กิจกรรมต่อเนื่องจะช่วยให้ผู้ใช้กลับไปที่แอปได้ด้วยการแตะเพียงครั้งเดียว

หรือใน Wear OS 7 ขึ้นไป คุณสามารถใช้การอัปเดตแบบสดแทน กิจกรรมต่อเนื่องได้ โปรดรองรับกิจกรรมต่อเนื่องในอุปกรณ์ที่ใช้ Wear OS 6 หรือต่ำกว่าต่อไปเพื่อความเข้ากันได้แบบย้อนกลับ

หากต้องการใช้ฟีเจอร์นี้ Touch Intent ของการแจ้งเตือนต่อเนื่องต้องชี้ไปที่กิจกรรมที่ทำงานตลอดเวลา ดังที่แสดงในข้อมูลโค้ดต่อไปนี้

val activityIntent =
    Intent(this, AlwaysOnActivity::class.java).apply {
        flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
    }

val pendingIntent =
    PendingIntent.getActivity(
        this,
        0,
        activityIntent,
        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
    )

val notificationBuilder =
    NotificationCompat.Builder(this, CHANNEL_ID)
        // ...
        // ...
        .setOngoing(true)

// ...

val ongoingActivity =
    OngoingActivity.Builder(applicationContext, NOTIFICATION_ID, notificationBuilder)
        // ...
        // ...
        .setTouchIntent(pendingIntent)
        .build()

ongoingActivity.apply(applicationContext)

val notification = notificationBuilder.build()

ให้หน้าจอเปิดอยู่เสมอและป้องกันไม่ให้อุปกรณ์เข้าสู่สถานะแอมเบียนท์

ในบางกรณีที่พบไม่บ่อยนัก คุณอาจต้องป้องกันไม่ให้อุปกรณ์เข้าสู่สถานะ แอมเบียนท์โดยสมบูรณ์ นั่นคือเพื่อหลีกเลี่ยงการหมดเวลา #1 คุณสามารถทำได้โดยใช้แฟล็กหน้าต่าง FLAG_KEEP_SCREEN_ON ซึ่งจะทำหน้าที่เป็น Wake Lock ทำให้อุปกรณ์อยู่ในสถานะ โต้ตอบ โปรดใช้ฟีเจอร์นี้ด้วยความระมัดระวังอย่างยิ่งเนื่องจากจะส่งผลต่อระยะเวลาการใช้งานแบตเตอรี่อย่างมาก

คำแนะนำสำหรับโหมดแอมเบียนท์

โปรดปฏิบัติตามหลักเกณฑ์การออกแบบต่อไปนี้เพื่อมอบประสบการณ์การใช้งานที่ดีที่สุดแก่ผู้ใช้และประหยัดพลังงานในโหมด แอมเบียนท์ คำแนะนำเหล่านี้ให้ความสำคัญกับประสบการณ์การใช้งานที่ชัดเจน โดยป้องกันข้อมูลที่ทำให้เข้าใจผิดและลดความซับซ้อนของภาพ พร้อมทั้งเพิ่มประสิทธิภาพพลังงานของจอแสดงผล

  • ลดความซับซ้อนของภาพและพลังงานของจอแสดงผล UI ที่สะอาดและเรียบง่ายจะส่งสัญญาณให้ผู้ใช้ทราบว่าแอปอยู่ในสถานะพลังงานต่ำ และช่วยประหยัดแบตเตอรี่ได้มากโดยจำกัดพิกเซลสว่าง
    • ให้หน้าจอเป็นสีดำอย่างน้อย 85%
    • แสดงเฉพาะข้อมูลที่สำคัญที่สุด โดยย้ายรายละเอียดรองไปที่จอแสดงผลแบบโต้ตอบ
    • ใช้เส้นขอบสำหรับไอคอนหรือปุ่มขนาดใหญ่แทนการเติมสีทึบ
    • หลีกเลี่ยงบล็อกสีทึบขนาดใหญ่ รวมถึงการสร้างแบรนด์หรือรูปภาพพื้นหลังที่ไม่มีฟังก์ชันการทำงาน
  • จัดการข้อมูลแบบไดนามิกที่ล้าสมัย
    • ระบบจะเรียกใช้การเรียกกลับ onUpdateAmbient() เป็นระยะๆ เท่านั้น ซึ่งโดยปกติจะเรียกใช้ทุกๆ 1 นาที เพื่อประหยัดพลังงาน ข้อจำกัดนี้ทำให้ข้อมูลที่เปลี่ยนแปลงบ่อยๆ เช่น นาฬิกาจับเวลา อัตราการเต้นของหัวใจ หรือระยะทางการออกกำลังกาย ล้าสมัยระหว่างการอัปเดต หากต้องการหลีกเลี่ยงการแสดงข้อมูลที่ทำให้เข้าใจผิดและไม่ถูกต้อง ให้ฟังการเรียกกลับ onEnterAmbient และแทนที่ค่าแบบสดเหล่านี้ด้วยเนื้อหาตัวยึดตำแหน่งแบบคงที่ เช่น --
  • รักษาเลย์เอาต์ที่สอดคล้องกัน
    • เก็บองค์ประกอบไว้ในตำแหน่งเดียวกันในโหมด โต้ตอบและ แอมเบียนท์เพื่อสร้างการเปลี่ยนสถานะที่ราบรื่น
    • แสดงเวลาเสมอ
  • คำนึงถึงบริบท
    • หากผู้ใช้อยู่ในหน้าจอการตั้งค่าหรือการกำหนดค่าเมื่ออุปกรณ์เข้าสู่โหมดแอมเบียนท์ ให้พิจารณาแสดงหน้าจอที่เกี่ยวข้องมากขึ้นจากแอปแทนมุมมองการตั้งค่า
  • จัดการข้อกำหนดเฉพาะของอุปกรณ์
    • ในออบเจ็กต์ AmbientDetails ที่ส่งไปยัง onEnterAmbient() ให้ทำดังนี้
      • หาก deviceHasLowBitAmbient เป็น true ให้ปิดใช้การป้องกันการเกิดรอยหยักหากเป็นไปได้
      • หาก burnInProtectionRequired เป็น true ให้เลื่อนองค์ประกอบ UI เล็กน้อยเป็นระยะๆ และหลีกเลี่ยงพื้นที่สีขาวทึบเพื่อป้องกันไม่ให้หน้าจอเกิดอาการเบิร์นอิน

การแก้ไขข้อบกพร่องและการทดสอบ

คำสั่ง adb ต่อไปนี้อาจมีประโยชน์เมื่อพัฒนาหรือทดสอบลักษณะการทำงานของแอป เมื่ออุปกรณ์อยู่ในโหมดแอมเบียนท์

# put device in ambient mode if the always on display is enabled in settings
# (and not disabled by other settings, such as theatre mode)
$ adb shell input keyevent KEYCODE_SLEEP

# put device in interactive mode
$ adb shell input keyevent KEYCODE_WAKEUP

ตัวอย่าง: แอปออกกำลังกาย

ลองพิจารณาแอปออกกำลังกายที่ต้องแสดงเมตริกแก่ผู้ใช้ตลอดระยะเวลาเซสชันการออกกำลังกาย แอปต้องแสดงต่อไปตลอดการเปลี่ยนสถานะ แอมเบียนท์และไม่ถูกแทนที่ด้วยหน้าปัดนาฬิกา

นักพัฒนาแอปควรทำดังนี้เพื่อให้บรรลุเป้าหมาย

  1. ใช้ AmbientLifecycleObserver เพื่อจัดการการเปลี่ยนแปลง UI ระหว่างสถานะ โต้ตอบและ แอมเบียนท์ เช่น การหรี่หน้าจอและการนำข้อมูลที่ไม่จำเป็นออก
  2. สร้างเลย์เอาต์พลังงานต่ำใหม่สำหรับสถานะแอมเบียนท์ที่ปฏิบัติตาม แนวทางปฏิบัติแนะนำ
  3. ใช้ Ongoing Activity API (หรือ การอัปเดตแบบสด ใน Wear OS 7 และ ขึ้นไป) ตลอดระยะเวลาการออกกำลังกายเพื่อป้องกันไม่ให้ระบบ กลับไปที่หน้าปัดนาฬิกา

ดูการใช้งานที่สมบูรณ์ได้ที่ตัวอย่างการออกกำลังกายที่อิงตาม Compose ในGitHub ตัวอย่างนี้ยังแสดงให้เห็นถึงการใช้ AmbientAware Composable จากไลบรารี Horologist เพื่อลดความซับซ้อนในการจัดการโหมดแอมเบียนท์ ใน Compose