การกำหนดค่าอุปกรณ์บางอย่างอาจเปลี่ยนแปลงได้ขณะที่แอปทำงานอยู่ ซึ่งรวมถึงแต่ไม่จำกัดเพียงรายการต่อไปนี้
- ขนาดการแสดงผลของแอป
- ความหนาแน่นของหน้าจอ (การปรับขนาดการแสดงผล)
- การวางแนวหน้าจอ
- ขนาดและน้ำหนักแบบอักษร
- ภาษา
- ธีมมืดเทียบกับธีมสว่าง
- ความพร้อมใช้งานของแป้นพิมพ์
การเปลี่ยนแปลงการกำหนดค่าเหล่านี้ส่วนใหญ่เกิดขึ้นเนื่องจากการโต้ตอบของผู้ใช้บางอย่าง ตัวอย่างเช่น การหมุนหรือพับอุปกรณ์จะเปลี่ยนปริมาณพื้นที่หน้าจอที่แอปใช้ได้ การเชื่อมต่อกับจอแสดงผลภายนอกหรือการย้ายแอประหว่างจอแสดงผลที่มีความหนาแน่นของพิกเซลต่างกันจะเปลี่ยนความหนาแน่นของหน้าจอและขนาดการแสดงผล ในทำนองเดียวกัน การเปลี่ยนแปลงการตั้งค่าอุปกรณ์ เช่น การปรับขนาดการแสดงผล ขนาดแบบอักษร ภาษา หรือธีมที่ต้องการจะเปลี่ยนค่าที่เกี่ยวข้องในออบเจ็กต์ Configuration
โดยปกติแล้ว พารามิเตอร์เหล่านี้จำเป็นต้องมีการเปลี่ยนแปลง UI ของแอปพลิเคชันมากพอที่แพลตฟอร์ม Android จะมีกลไกที่สร้างขึ้นโดยเฉพาะเมื่อมีการเปลี่ยนแปลง
กลไกนี้คือการสร้าง Activity ขึ้นมาใหม่
กิจกรรมสันทนาการ
ระบบจะสร้าง Activity ขึ้นมาใหม่เมื่อมีการเปลี่ยนแปลงการกำหนดค่า โดยระบบจะเรียก onDestroy และทำลายอินสแตนซ์ Activity ที่มีอยู่ จากนั้นจะสร้างอินสแตนซ์ใหม่โดยใช้ onCreate และอินสแตนซ์ Activity ใหม่นี้จะเริ่มต้นด้วยการกำหนดค่าใหม่ที่อัปเดตแล้ว ซึ่งหมายความว่าระบบจะสร้าง UI ขึ้นมาใหม่ด้วยการกำหนดค่าใหม่ด้วย
โดยปกติแล้ว Activity จะทำหน้าที่เป็นโฮสต์สำหรับ Composable เมื่อสร้าง Activity ขึ้นมาใหม่ Compose จะสร้าง UI ขึ้นมาใหม่โดยใช้ค่าการกำหนดค่าใหม่ด้วย
ลักษณะการทำงานของการสร้างใหม่ช่วยให้แอปพลิเคชันของคุณปรับให้เข้ากับการกำหนดค่าใหม่ได้โดยการโหลดแอปพลิเคชันซ้ำโดยอัตโนมัติด้วยทรัพยากรทางเลือกที่ตรงกับการกำหนดค่าอุปกรณ์ใหม่
ตัวอย่างสันทนาการ
พิจารณา Composable ที่แสดงชื่อแบบคงที่โดยใช้ทรัพยากรสตริง
// In the res/values/strings.xml file // <string name="compose">Jetpack Compose</string> // In your Compose code Text( text = stringResource(R.string.compose) )
เมื่อสร้าง Activity แล้ว Text Composable จะอ่านการกำหนดค่าปัจจุบัน (เช่น ภาษา) และแก้ไขทรัพยากรสตริงที่เหมาะสม
หากภาษาเปลี่ยนไป ระบบจะสร้างกิจกรรมขึ้นมาใหม่ เมื่อเกิดเหตุการณ์นี้ขึ้น
Compose จะสร้าง UI ขึ้นมาใหม่ เนื่องจาก stringResource อ่านจากการกำหนดค่าปัจจุบัน ชื่อจึงอัปเดตเป็นค่าที่แปลเป็นภาษาท้องถิ่นที่ถูกต้องโดยอัตโนมัติ
การสร้างใหม่ยังล้างสถานะที่เก็บไว้เป็นฟิลด์ใน Activity ด้วย
หากต้องการรักษาสถานะ UI ไว้ตลอดการเปลี่ยนแปลงการกำหนดค่า ให้ใช้รูปแบบการจัดการสถานะที่แนะนำ ใช้ ViewModel สำหรับข้อมูลและตรรกะทางธุรกิจ และใช้ rememberSaveable สำหรับสถานะระดับ UI กลไกเหล่านี้ช่วยให้สถานะของคุณคงอยู่แม้จะมีการสร้าง Activity ขึ้นมาใหม่ ขณะที่ UI จะอัปเดตเพื่อแสดงการกำหนดค่าใหม่
ดูข้อมูลเพิ่มเติมเกี่ยวกับการบันทึกสถานะใน Compose ได้ที่บันทึกสถานะ UI ใน Compose
ความคาดหวังของผู้ใช้
ผู้ใช้แอปคาดหวังว่าระบบจะเก็บสถานะไว้ หากผู้ใช้กำลังกรอกแบบฟอร์มและเปิดแอปอื่นในโหมดหลายหน้าต่างเพื่ออ้างอิงข้อมูล ประสบการณ์ของผู้ใช้จะไม่ดีหากกลับมาที่แบบฟอร์มที่ถูกล้างหรือไปยังที่อื่นในแอปโดยสิ้นเชิง ในฐานะนักพัฒนาแอป คุณต้องมอบประสบการณ์ที่สอดคล้องกันแก่ผู้ใช้ผ่านการเปลี่ยนแปลงการกำหนดค่าและการสร้างกิจกรรมใหม่
หากต้องการยืนยันว่าสถานะยังคงอยู่ในแอปพลิเคชันหรือไม่ คุณสามารถดำเนินการที่ทำให้เกิดการเปลี่ยนแปลงการกำหนดค่าทั้งในขณะที่แอปทำงานอยู่เบื้องหน้าและเบื้องหลัง การทำงานดังกล่าว ได้แก่:
- การหมุนอุปกรณ์
- กำลังเข้าสู่โหมดหลายหน้าต่าง
- การปรับขนาดแอปพลิเคชันขณะอยู่ในโหมดหลายหน้าต่างหรือหน้าต่างรูปแบบอิสระ
- การพับอุปกรณ์แบบพับได้ที่มีจอแสดงผลหลายจอ
- การเปลี่ยนขนาดการแสดงผลหรือการปรับขนาดการแสดงผลในการตั้งค่าระบบ
- การย้ายแอประหว่างจอแสดงผลที่มีความหนาแน่นของหน้าจอต่างกัน (เช่น จอภาพภายนอกที่เชื่อมต่อหรือจอแสดงผลของอุปกรณ์แบบพับได้)
- การเปลี่ยนธีมของระบบ เช่น ธีมมืดกับธีมสว่าง
- การเปลี่ยนขนาดแบบอักษร
- การเปลี่ยนภาษาของระบบหรือแอป
- การเชื่อมต่อหรือยกเลิกการเชื่อมต่อแป้นพิมพ์ฮาร์ดแวร์
- การเชื่อมต่อหรือยกเลิกการเชื่อมต่อแท่นชาร์จ
คุณสามารถใช้แนวทางต่างๆ เพื่อรักษาสถานะที่เกี่ยวข้องผ่านการสร้าง Activity ใหม่ การเลือกใช้ขึ้นอยู่กับประเภทของสถานะที่ต้องการเก็บรักษา ดังนี้
- การคงอยู่ภายในเครื่องเพื่อจัดการการสิ้นสุดการประมวลผลสำหรับข้อมูลที่ซับซ้อนหรือมีขนาดใหญ่
พื้นที่เก็บข้อมูลในเครื่องแบบถาวรประกอบด้วยฐานข้อมูลหรือ
DataStore - ออบเจ็กต์ที่เก็บไว้ เช่น อินสแตนซ์
ViewModelเพื่อจัดการสถานะที่เกี่ยวข้องกับ UI ในหน่วยความจำขณะที่ผู้ใช้กำลังใช้แอปอยู่ rememberSaveableเพื่อคงสถานะ UI ชั่วคราวไว้ตลอดการเปลี่ยนแปลงการกำหนดค่าและการสิ้นสุดการประมวลผลที่ระบบเริ่มขึ้น ซึ่งเหมาะสำหรับสถานะที่ขึ้นอยู่กับอินพุตของผู้ใช้ ตำแหน่งการเลื่อน หรือการนำทาง แต่ไม่ได้อยู่ในViewModel
หากต้องการอ่านเกี่ยวกับ API สำหรับแต่ละรายการโดยละเอียดและกรณีที่เหมาะสมในการใช้แต่ละรายการ โปรดดูบันทึกสถานะ UI
จำกัดการสร้างกิจกรรมใหม่
คุณสามารถป้องกันการสร้างกิจกรรมใหม่โดยอัตโนมัติสำหรับการเปลี่ยนแปลงการกำหนดค่าบางอย่างได้ ในแอป Compose-only สมัยใหม่ UI ของคุณจะได้รับการจัดองค์ประกอบใหม่ไม่ว่าจะด้วยวิธีใดก็ตาม แต่เราขอแนะนำให้จัดการการเปลี่ยนแปลงการกำหนดค่าโดยตรง
โดยค่าเริ่มต้น การเปลี่ยนแปลงการกำหนดค่าจะบังคับให้ระบบทำลายและสร้างกิจกรรมขึ้นมาใหม่ ซึ่งรวมถึง UI และออบเจ็กต์ที่ได้มาจากกิจกรรม หากคุณประกาศว่ากิจกรรมจะจัดการการเปลี่ยนแปลงการกำหนดค่าเอง ระบบจะป้องกันไม่ให้เกิดเหตุการณ์นี้ แต่จะอัปเดตเฉพาะออบเจ็กต์ Configuration และ Compose จะจัดองค์ประกอบ UI ใหม่ด้วยค่าใหม่
การจัดการการเปลี่ยนแปลงการกำหนดค่าใน Compose โดยตรงมีประโยชน์หลายประการ ดังนี้
- ประสิทธิภาพที่ดีขึ้น: การจัดองค์ประกอบ UI ใหม่ใช้ทรัพยากรน้อยกว่าวงจรการสร้างกิจกรรมใหม่ทั้งหมด โดยเฉพาะอย่างยิ่งสำหรับการเปลี่ยนแปลงเล็กน้อย
- ภาพเคลื่อนไหวที่ลื่นไหล: การหลีกเลี่ยงการรีสตาร์ทกิจกรรมช่วยให้คุณเรียกใช้ภาพเคลื่อนไหวอย่างต่อเนื่องเมื่อมีการเปลี่ยนแปลงการกำหนดค่า เช่น การเปลี่ยนเลย์เอาต์ที่ราบรื่นระหว่างการหมุนอุปกรณ์
- การคงสถานะ: การคงอินสแตนซ์กิจกรรมไว้จะช่วยลดความเสี่ยงที่สถานะ UI ชั่วคราวจะหายไประหว่างเหตุการณ์ต่างๆ เช่น การหมุนหน้าจอ โปรดทราบว่าคุณยังคงต้องจัดการการเก็บรักษาสถานะสำหรับการสิ้นสุดการประมวลผลที่ระบบเริ่มขึ้น
หากต้องการปิดใช้การสร้างกิจกรรมใหม่สำหรับการเปลี่ยนแปลงการกำหนดค่าบางอย่าง ให้เพิ่มประเภทการกำหนดค่าลงใน android:configChanges ในรายการ <activity> ในไฟล์ AndroidManifest.xml ค่าที่เป็นไปได้จะปรากฏในเอกสารประกอบสำหรับแอตทริบิวต์ android:configChanges
โค้ดไฟล์ Manifest ต่อไปนี้จะปิดใช้การสร้าง Activity ใหม่สำหรับ MyActivity เมื่อการวางแนวหน้าจอและความพร้อมใช้งานของแป้นพิมพ์มีการเปลี่ยนแปลง
<activity
android:name=".MyActivity"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
android:label="@string/app_name">
Android 17
ตั้งแต่ Android 17 (API ระดับ 37) เป็นต้นไป ระบบจะไม่รีสตาร์ทกิจกรรมโดยค่าเริ่มต้นอีกต่อไปสำหรับการเปลี่ยนแปลงการกำหนดค่าหลายอย่างที่โดยปกติแล้วไม่จำเป็นต้องสร้าง UI ใหม่ทั้งหมด
แต่กิจกรรมจะยังคงทำงานอยู่และได้รับการอัปเดตผ่าน Callback onConfigurationChanged()
การเปลี่ยนแปลงการกำหนดค่าที่เฉพาะเจาะจงมีดังนี้
CONFIG_KEYBOARDCONFIG_KEYBOARD_HIDDENCONFIG_NAVIGATIONCONFIG_TOUCHSCREENCONFIG_COLOR_MODECONFIG_UI_MODE(เฉพาะเมื่อโหมด UI เปลี่ยนเป็นUI_MODE_TYPE_DESKหรือจากUI_MODE_TYPE_DESKเป็นประเภทอื่น)
หากแอปพลิเคชันของคุณอาศัยการรีสตาร์ทเต็มรูปแบบเพื่อโหลดทรัพยากรซ้ำสำหรับการเปลี่ยนแปลงเหล่านี้ ตอนนี้คุณต้องเลือกใช้ลักษณะการทำงานเดิมอย่างชัดแจ้งโดยใช้แอตทริบิวต์ android:recreateOnConfigChanges ในไฟล์ Manifest
แอตทริบิวต์นี้ช่วยให้คุณระบุได้ว่าการเปลี่ยนแปลงการกำหนดค่าใดควรทริกเกอร์วงจรการหยุดกิจกรรม ทำลาย และสร้างใหม่ทั้งหมด เช่น
<activity
android:name=".MyActivity"
android:recreateOnConfigChanges="keyboard|keyboardHidden|navigation|colorMode|touchscreen|...">
...
</activity>
ตอบสนองต่อการเปลี่ยนแปลงการกำหนดค่า
Jetpack Compose ช่วยให้แอปตอบสนองต่อการเปลี่ยนแปลงการกำหนดค่าได้ง่ายขึ้น
อย่างไรก็ตาม หากคุณปิดใช้การสร้าง Activity ใหม่สำหรับการเปลี่ยนแปลงการกำหนดค่าทั้งหมดในกรณีที่ทำได้ แอปของคุณยังคงต้องจัดการการเปลี่ยนแปลงการกำหนดค่าอย่างถูกต้อง
ออบเจ็กต์ Configuration พร้อมใช้งานในลำดับชั้น UI ของ Compose ที่มี LocalConfiguration CompositionLocal เมื่อใดก็ตามที่มีการเปลี่ยนแปลง ฟังก์ชันที่ประกอบกันได้ซึ่งอ่านจาก LocalConfiguration.current จะจัดองค์ประกอบใหม่ ดูข้อมูลเกี่ยวกับวิธีการทำงานของ CompositionLocal ได้ที่ข้อมูลที่กำหนดขอบเขตในเครื่องด้วย CompositionLocal
ตัวอย่าง
ในตัวอย่างต่อไปนี้ Composable จะแสดงวันที่ในรูปแบบที่เฉพาะเจาะจง
Composable จะตอบสนองต่อการเปลี่ยนแปลงการกำหนดค่าภาษาของระบบโดยเรียกใช้ ConfigurationCompat.getLocales ด้วย LocalConfiguration.current
@Composable
fun DateText(year: Int, dayOfYear: Int) {
val dateTimeFormatter = DateTimeFormatter.ofPattern(
"MMM dd",
ConfigurationCompat.getLocales(LocalConfiguration.current)[0]
)
Text(
dateTimeFormatter.format(LocalDate.ofYearDay(year, dayOfYear))
)
}
เพื่อหลีกเลี่ยงการสร้าง Activity ใหม่เมื่อภาษาเปลี่ยนไป Activity ที่โฮสต์โค้ด Compose จะต้องเลือกไม่รับการเปลี่ยนแปลงการกำหนดค่าภาษา โดยตั้งค่า android:configChanges เป็น locale|layoutDirection
การเปลี่ยนแปลงการกำหนดค่า: แนวคิดหลักและแนวทางปฏิบัติแนะนำ
แนวคิดสำคัญที่ควรทราบเมื่อทำการเปลี่ยนแปลงการกำหนดค่ามีดังนี้
- การกำหนดค่า: การกำหนดค่าอุปกรณ์จะกำหนดวิธีที่ UI แสดงต่อผู้ใช้ เช่น ขนาดการแสดงผลของแอป ความหนาแน่นของหน้าจอ ภาษา หรือธีมของระบบ
ใน Compose คุณสามารถเข้าถึงค่าการกำหนดค่าได้โดยใช้
LocalConfiguration - การเปลี่ยนแปลงการกำหนดค่า: การกำหนดค่าจะเปลี่ยนแปลงผ่านการโต้ตอบของผู้ใช้ เช่น ผู้ใช้อาจเปลี่ยนการตั้งค่าอุปกรณ์หรือวิธีที่โต้ตอบกับอุปกรณ์ คุณไม่สามารถป้องกันการเปลี่ยนแปลงการกำหนดค่าได้
- การสร้าง
Activityใหม่: การเปลี่ยนแปลงการกำหนดค่าจะส่งผลให้มีการสร้างActivityใหม่โดยค่าเริ่มต้น ซึ่งเป็นกลไกในตัวเพื่อเริ่มต้นสถานะแอปใหม่สำหรับการกำหนดค่าใหม่ - การทำลาย
Activity: การสร้างActivityขึ้นมาใหม่จะทำให้ระบบทำลายอินสแตนซ์Activityเดิมและสร้างอินสแตนซ์ใหม่ขึ้นมาแทน อินสแตนซ์เก่าจะล้าสมัยแล้ว หลีกเลี่ยงการเก็บการอ้างอิงไปยังออบเจ็กต์ที่กำหนดขอบเขตตามวงจรไว้เกินขอบเขตที่กำหนดไว้ - สถานะ: สถานะในอินสแตนซ์
Activityเก่าจะไม่แสดงในอินสแตนซ์Activityใหม่ เนื่องจากเป็นอินสแตนซ์ออบเจ็กต์ที่แตกต่างกัน 2 รายการ แทนที่จะเชื่อมโยงสถานะกับกิจกรรม ให้ใช้ API ที่แนะนำเพื่อคงสถานะของแอปและผู้ใช้ไว้ตามที่อธิบายไว้ในบันทึกสถานะ UI - การเลือกไม่ใช้: การเลือกไม่ใช้การสร้างกิจกรรมใหม่สำหรับการเปลี่ยนแปลงการกำหนดค่าประเภทหนึ่งๆ กำหนดให้แอปของคุณต้องอัปเดตอย่างเหมาะสมเพื่อตอบสนองต่อการกำหนดค่าใหม่
โปรดปฏิบัติตามแนวทางปฏิบัติแนะนำต่อไปนี้เพื่อมอบประสบการณ์การใช้งานที่ดีแก่ผู้ใช้
- เตรียมพร้อมสำหรับการเปลี่ยนแปลงการกำหนดค่าที่เกิดขึ้นบ่อย: อย่าคิดว่าการเปลี่ยนแปลงการกำหนดค่าเกิดขึ้นไม่บ่อยหรือไม่เคยเกิดขึ้นเลย ไม่ว่าจะเป็นระดับ API, รูปแบบของอุปกรณ์ หรือชุดเครื่องมือ UI เมื่อผู้ใช้ทำการเปลี่ยนแปลงการกำหนดค่า ผู้ใช้คาดหวังให้แอปอัปเดตและทำงานได้อย่างถูกต้องต่อไปด้วยการกำหนดค่าใหม่
- รักษาสถานะ: อย่าทำให้สถานะของผู้ใช้หายไปเมื่อมีการสร้าง
Activityขึ้นมาใหม่ คงสถานะไว้ตามที่อธิบายไว้ในบันทึกสถานะ UI โดยใช้ API เช่นViewModelและrememberSaveable - หลีกเลี่ยงการเลือกไม่ใช้เพื่อแก้ไขปัญหาอย่างรวดเร็ว: อย่าเลือกไม่ใช้การสร้าง
Activityใหม่เป็นทางลัดเพื่อหลีกเลี่ยงการสูญเสียสถานะ การเลือกไม่สร้างกิจกรรมใหม่กำหนดให้คุณต้องทำตามสัญญาในการจัดการการเปลี่ยนแปลง และคุณอาจยังคงสูญเสียสถานะเนื่องจากการสร้างActivityใหม่จากการเปลี่ยนแปลงการกำหนดค่าอื่นๆ การสิ้นสุดการประมวลผล หรือการปิดแอป คุณไม่สามารถปิดใช้การสร้างActivityใหม่ทั้งหมดได้ เก็บรักษาสถานะตามที่อธิบายไว้ในบันทึกสถานะ UI - อย่าหลีกเลี่ยงการเปลี่ยนแปลงการกำหนดค่า: อย่าจำกัดการวางแนว สัดส่วนภาพ หรือความสามารถในการปรับขนาดเพื่อหลีกเลี่ยงการเปลี่ยนแปลงการกำหนดค่าและการสร้าง
Activityขึ้นมาใหม่ ซึ่งจะส่งผลเสียต่อผู้ใช้ที่ต้องการใช้แอปของคุณในแบบที่ต้องการ
จัดการการเปลี่ยนแปลงการกำหนดค่าตามขนาด
การเปลี่ยนแปลงการกำหนดค่าตามขนาดอาจเกิดขึ้นได้ทุกเมื่อและมีแนวโน้มที่จะเกิดขึ้นมากขึ้นเมื่อแอปทำงานในอุปกรณ์หน้าจอขนาดใหญ่ที่ผู้ใช้เข้าสู่โหมดหลายหน้าต่างได้ ผู้ใช้คาดหวังว่าแอปของคุณจะทำงานได้ดีในสภาพแวดล้อมดังกล่าว
การเปลี่ยนแปลงขนาดมี 2 ประเภทหลักๆ ได้แก่ การเปลี่ยนแปลงที่สำคัญและการเปลี่ยนแปลงที่ไม่สำคัญ การเปลี่ยนแปลงขนาดที่สำคัญคือการเปลี่ยนแปลงที่ใช้ชุดทรัพยากรทางเลือกที่แตกต่างกันกับการกำหนดค่าใหม่เนื่องจากขนาดหน้าจอที่แตกต่างกัน เช่น ความกว้าง ความสูง หรือความกว้างที่เล็กที่สุด ทรัพยากรเหล่านี้รวมถึงทรัพยากรที่แอปกำหนดเองและทรัพยากรจากไลบรารีใดๆ ของแอป
จำกัดการสร้างกิจกรรมใหม่สำหรับการเปลี่ยนแปลงการกำหนดค่าตามขนาด
เมื่อคุณปิดใช้การสร้าง Activity ใหม่สำหรับการเปลี่ยนแปลงการกำหนดค่าตามขนาด ระบบจะไม่สร้าง Activity ขึ้นมาใหม่ แต่จะได้รับการเรียกไปยัง Activity.onConfigurationChanged แทน คอมโพสิเบิลที่อ่าน LocalConfiguration.current จะจัดองค์ประกอบใหม่โดยอัตโนมัติเพื่อแสดงขนาดใหม่
ระบบจะปิดใช้การสร้าง Activity ใหม่สำหรับการเปลี่ยนแปลงการกำหนดค่าตามขนาดเมื่อคุณมี android:configChanges="screenSize|smallestScreenSize|orientation|screenLayout" ในไฟล์ Manifest
แหล่งข้อมูลเพิ่มเติม
ดูข้อมูลเพิ่มเติมเกี่ยวกับการจัดการการเปลี่ยนแปลงการกำหนดค่าได้ที่แหล่งข้อมูลเพิ่มเติมต่อไปนี้