จัดการการเปลี่ยนแปลงการกำหนดค่า (มุมมอง)

แนวคิดและการใช้งาน Jetpack Compose

การกำหนดค่าอุปกรณ์บางอย่างอาจเปลี่ยนแปลงได้ขณะที่แอปทำงานอยู่ ซึ่งรวมถึงแต่ไม่จำกัดเพียง

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

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

โดยปกติแล้ว พารามิเตอร์เหล่านี้ต้องมีการเปลี่ยนแปลง UI ของแอปพลิเคชันมากพอ ที่แพลตฟอร์ม Android จะมีกลไกที่สร้างขึ้นโดยเฉพาะเมื่อมีการเปลี่ยนแปลง กลไกนี้คือActivityการสร้างใหม่

การสร้างกิจกรรมใหม่

ระบบจะสร้าง Activity ขึ้นใหม่เมื่อมีการเปลี่ยนแปลงการกำหนดค่า หากต้องการทำเช่นนี้ ระบบจะเรียกใช้ onDestroy และทำลายอินสแตนซ์ Activity ที่มีอยู่ จากนั้นจะสร้างอินสแตนซ์ใหม่โดยใช้ onCreate และระบบจะเริ่มต้นอินสแตนซ์ Activity ใหม่นี้ด้วยการกำหนดค่าใหม่ที่อัปเดตแล้ว ซึ่ง ยังหมายความว่าระบบจะสร้าง UI ใหม่ด้วยการกำหนดค่าใหม่ด้วย

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

ตัวอย่างการพักผ่อนหย่อนใจ

พิจารณาTextViewที่แสดงชื่อแบบคงที่โดยใช้ android:text="@string/title" ตามที่กำหนดไว้ในไฟล์ XML ของเลย์เอาต์ เมื่อสร้างมุมมอง ระบบจะตั้งค่าข้อความเพียงครั้งเดียวตามภาษาปัจจุบัน หาก ภาษาเปลี่ยนแปลง ระบบจะสร้างกิจกรรมขึ้นใหม่ ด้วยเหตุนี้ ระบบจึงสร้างมุมมองขึ้นมาใหม่และเริ่มต้นมุมมองด้วยค่าที่ถูกต้องตามภาษาใหม่ด้วย

การสร้างใหม่ยังล้างสถานะที่เก็บไว้เป็นฟิลด์ใน Activity หรือใน Fragment, View หรือออบเจ็กต์อื่นๆ ที่มีอยู่ เนื่องจากการสร้าง Activity ใหม่จะสร้างอินสแตนซ์ใหม่ทั้งหมดของ Activity และ UI นอกจากนี้ Activity เก่าจะไม่ปรากฏหรือใช้งานได้อีกต่อไป ดังนั้นการอ้างอิงที่เหลืออยู่หรือออบเจ็กต์ที่อยู่ในนั้นจึงล้าสมัย ซึ่งอาจทำให้เกิด ข้อบกพร่อง หน่วยความจำรั่ว และข้อขัดข้อง

เพื่อดูข้อมูลเพิ่มเติม

ความคาดหวังของผู้ใช้

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

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

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

คุณใช้แนวทางหลักๆ 3 แบบเพื่อรักษาสถานะที่เกี่ยวข้องได้ ผ่านการสร้างActivityใหม่ โดยการเลือกใช้จะขึ้นอยู่กับประเภทสถานะที่คุณ ต้องการเก็บไว้

  • การคงอยู่ของข้อมูลในเครื่องเพื่อจัดการการสิ้นสุดการประมวลผลสำหรับข้อมูลที่ซับซ้อนหรือมีขนาดใหญ่ พื้นที่เก็บข้อมูลในเครื่องแบบถาวรรวมถึงฐานข้อมูลหรือ DataStore
  • ออบเจ็กต์ที่เก็บไว้ เช่น อินสแตนซ์ ViewModel เพื่อจัดการสถานะที่เกี่ยวข้องกับ UI ในหน่วยความจำขณะที่ผู้ใช้ใช้งานแอปอยู่
  • สถานะอินสแตนซ์ที่บันทึกไว้เพื่อจัดการการสิ้นสุดการประมวลผลที่ระบบเริ่มต้นและเก็บสถานะชั่วคราวที่ขึ้นอยู่กับข้อมูลจากผู้ใช้หรือการนำทาง

หากต้องการอ่านเกี่ยวกับ API สำหรับแต่ละรายการเหล่านี้โดยละเอียด และเมื่อใดที่ควรใช้แต่ละรายการ โปรดดูบันทึกสถานะ UI

ตอบสนองต่อการเปลี่ยนแปลงการกำหนดค่าในระบบมุมมอง

ในระบบ View เมื่อมีการเปลี่ยนแปลงการกำหนดค่าที่คุณปิดใช้การสร้าง Activity ใหม่ กิจกรรมจะได้รับการเรียกไปยัง Activity.onConfigurationChanged มุมมองที่แนบมาจะได้รับการเรียกไปยัง View.onConfigurationChanged ด้วย สําหรับการเปลี่ยนแปลงการกําหนดค่าที่คุณยังไม่ได้เพิ่มลงใน android:configChanges ระบบจะสร้างกิจกรรมขึ้นมาใหม่ตามปกติ

เมธอด Callback onConfigurationChanged จะรับออบเจ็กต์ Configuration ที่ระบุการกำหนดค่าอุปกรณ์ใหม่ อ่านฟิลด์ในออบเจ็กต์ Configuration เพื่อดูว่าการกำหนดค่าใหม่ของคุณคืออะไร หากต้องการทำการเปลี่ยนแปลงในภายหลัง ให้อัปเดตทรัพยากรที่คุณใช้ในอินเทอร์เฟซ เมื่อระบบเรียกใช้เมธอดนี้ ระบบจะอัปเดตออบเจ็กต์ Resources ของกิจกรรมเพื่อแสดงผลทรัพยากรตามการกำหนดค่าใหม่ ซึ่งช่วยให้คุณรีเซ็ตองค์ประกอบของ UI ได้โดยไม่ต้องให้ระบบ รีสตาร์ทกิจกรรม

ตัวอย่างเช่น การติดตั้งใช้งาน onConfigurationChanged ต่อไปนี้จะตรวจสอบ ว่ามีคีย์บอร์ดหรือไม่

Kotlin

override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)

    // Checks whether a keyboard is available
    if (newConfig.keyboardHidden === Configuration.KEYBOARDHIDDEN_YES) {
        Toast.makeText(this, "Keyboard available", Toast.LENGTH_SHORT).show()
    } else if (newConfig.keyboardHidden === Configuration.KEYBOARDHIDDEN_NO) {
        Toast.makeText(this, "No keyboard", Toast.LENGTH_SHORT).show()
    }
}

Java

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    // Checks whether a keyboard is available
    if (newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_YES) {
        Toast.makeText(this, "Keyboard available", Toast.LENGTH_SHORT).show();
    } else if (newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO){
        Toast.makeText(this, "No keyboard", Toast.LENGTH_SHORT).show();
    }
}

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

รักษาสถานะ

เมื่อใช้เทคนิคนี้ คุณยังคงต้องรักษาสถานะไว้ในระหว่างวงจรกิจกรรมปกติ เนื่องจากสาเหตุต่อไปนี้

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

การเปลี่ยนแปลงการกำหนดค่า: แนวคิดหลักและแนวทางปฏิบัติแนะนำ

แนวคิดสำคัญที่ควรทราบเมื่อทำการเปลี่ยนแปลงการกำหนดค่ามีดังนี้

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

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

  • เตรียมพร้อมสำหรับการเปลี่ยนแปลงการกำหนดค่าบ่อยๆ: อย่าคิดว่าการเปลี่ยนแปลงการกำหนดค่าจะเกิดขึ้นไม่บ่อยหรือไม่มีเลย ไม่ว่าจะเป็นระดับ API, รูปแบบ ของอุปกรณ์ หรือชุดเครื่องมือ UI เมื่อผู้ใช้ทำการเปลี่ยนแปลงการกำหนดค่า ผู้ใช้จะคาดหวังให้ แอปอัปเดตและทำงานต่อไปอย่างถูกต้องด้วยการกำหนดค่าใหม่
  • รักษาสถานะ: รักษาสถานะของผู้ใช้ไว้เมื่อเกิดActivityการสร้างใหม่ เก็บรักษาสถานะตามที่อธิบายไว้ในบันทึกสถานะ UI
  • หลีกเลี่ยงการเลือกไม่ใช้เพื่อเป็นการแก้ไขอย่างรวดเร็ว: อย่าเลือกไม่ใช้Activityการสร้างใหม่ เพื่อเป็นทางลัดในการหลีกเลี่ยงการสูญเสียสถานะ การเลือกไม่ใช้การสร้างกิจกรรมใหม่กำหนดให้คุณต้องทำตามสัญญาในการจัดการการเปลี่ยนแปลง และคุณอาจยังคงสูญเสียสถานะเนื่องจากActivityการสร้างใหม่จากการเปลี่ยนแปลงการกำหนดค่าอื่นๆ การสิ้นสุดกระบวนการ หรือการปิดแอป ไม่สามารถปิดใช้การสร้างActivityใหม่ได้ทั้งหมด เก็บรักษาสถานะตามที่อธิบายไว้ในบันทึกสถานะ UI
  • อย่าหลีกเลี่ยงการเปลี่ยนแปลงการกำหนดค่า: อย่าจำกัดการวางแนว สัดส่วนภาพ หรือการปรับขนาดเพื่อหลีกเลี่ยงการเปลี่ยนแปลงการกำหนดค่าและActivityการสร้างใหม่ ซึ่งจะส่งผลเสียต่อผู้ใช้ที่ต้องการใช้แอปของคุณ ในวิธีที่ต้องการ

จัดการการเปลี่ยนแปลงการกำหนดค่าตามขนาด

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

การเปลี่ยนแปลงขนาดมี 2 ประเภทหลักๆ ได้แก่ การเปลี่ยนแปลงที่สำคัญและ การเปลี่ยนแปลงที่ไม่สำคัญ การเปลี่ยนแปลงขนาดอย่างมีนัยสำคัญคือการเปลี่ยนแปลงที่ชุดทรัพยากรสำรอง ที่แตกต่างกันมีผลกับการกำหนดค่าใหม่เนื่องจากความแตกต่างใน ขนาดหน้าจอ เช่น ความกว้าง ความสูง หรือความกว้างที่เล็กที่สุด ซึ่งรวมถึงทรัพยากรที่แอปกำหนดเองและทรัพยากรจากไลบรารีของแอป

จำกัดการสร้างกิจกรรมใหม่สำหรับการเปลี่ยนแปลงการกำหนดค่าตามขนาด

เมื่อปิดใช้การสร้าง Activity ใหม่สำหรับการเปลี่ยนแปลงการกำหนดค่าตามขนาด ระบบจะไม่สร้าง Activity ใหม่ แต่จะรับสายที่ Activity.onConfigurationChanged แทน มุมมองที่แนบมาจะได้รับการโทรไปยัง View.onConfigurationChanged

ระบบจะปิดใช้การสร้างใหม่ Activity สำหรับการเปลี่ยนแปลงการกำหนดค่าตามขนาดเมื่อคุณมี android:configChanges="screenSize|smallestScreenSize|orientation|screenLayout" ในไฟล์ Manifest

อนุญาตให้สร้างกิจกรรมใหม่สำหรับการเปลี่ยนแปลงการกำหนดค่าตามขนาด

ใน Android 7.0 (API ระดับ 24) ขึ้นไป Activityการสร้างใหม่จะเกิดขึ้นสำหรับการเปลี่ยนแปลงการกำหนดค่าตามขนาดก็ต่อเมื่อการเปลี่ยนแปลงขนาดมีความสำคัญ เมื่อระบบไม่สร้าง Activity ใหม่เนื่องจากมีขนาดไม่เพียงพอ ระบบอาจเรียกใช้ Activity.onConfigurationChanged และ View.onConfigurationChanged แทน

ข้อควรระวังบางประการเกี่ยวกับแฮนเดิล Activity และ View การเรียกกลับเมื่อไม่ได้สร้าง Activity ใหม่มีดังนี้

  • ใน Android 11 (ระดับ API 30) ถึง Android 13 (ระดับ API 33) ระบบจะไม่เรียกใช้ Activity.onConfigurationChanged
  • มีปัญหาที่ทราบแล้วซึ่งในบางกรณี View.onConfigurationChanged อาจไม่ได้รับการเรียกใช้ใน Android 12L (ระดับ API 32) และ Android 13 (ระดับ API 33) เวอร์ชันแรกๆ ดูข้อมูลเพิ่มเติมได้ที่ปัญหาสาธารณะนี้ เราได้แก้ไขปัญหานี้แล้วใน Android 13 รุ่นต่อๆ มาและ Android 14

สำหรับโค้ดที่ขึ้นอยู่กับการรอรับการเปลี่ยนแปลงการกำหนดค่าตามขนาด เราขอแนะนำให้ใช้ยูทิลิตี View ที่มีการลบล้าง View.onConfigurationChanged แทนการพึ่งพาการสร้าง Activity ใหม่หรือ Activity.onConfigurationChanged