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

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

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

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

จำกัดกิจกรรมสันทนาการ

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

ในการปิดใช้การสร้างกิจกรรมใหม่สำหรับการเปลี่ยนแปลงการกำหนดค่าบางอย่าง เพิ่มประเภทการกำหนดค่าลงใน 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 12L (API ระดับ 32)

ดำเนินการกับการเปลี่ยนแปลงการกำหนดค่าในระบบดู

ในระบบ 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() แทนได้ ในนั้น ทรัพยากรทั้งหมดที่ใช้ก่อนการเปลี่ยนแปลงการกำหนดค่าจะยังใช้อยู่ และคุณทำเพียงแค่หลีกเลี่ยงการเริ่มกิจกรรมใหม่เท่านั้น เช่น แอปทีวี อาจไม่ต้องการตอบสนองเมื่อมีการต่อหรือถอดแป้นพิมพ์บลูทูธออก

สถานะการเก็บรักษา

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

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

ดำเนินการกับการเปลี่ยนแปลงการกำหนดค่าใน Jetpack Compose

Jetpack Compose ช่วยให้แอปตอบสนองต่อการเปลี่ยนแปลงการกําหนดค่าได้ง่ายขึ้น แต่ถ้าคุณปิดใช้การสร้างใหม่ Activity สำหรับการเปลี่ยนแปลงการกำหนดค่าทั้งหมด ที่สามารถทำได้ แอปของคุณยังต้องจัดการ การเปลี่ยนแปลงการกำหนดค่า

ออบเจ็กต์ Configuration พร้อมใช้งานในลำดับชั้น UI ของ Compose ด้วย การเรียบเรียงเพลง LocalConfiguration ในเครื่อง เมื่อมีการเปลี่ยนแปลง ฟังก์ชันที่ประกอบกันได้ซึ่งอ่านจากการเขียน LocalConfiguration.current ใหม่ สำหรับ ดูข้อมูลเกี่ยวกับวิธีการทำงานขององค์ประกอบท้องถิ่นได้ที่การกำหนดขอบเขตเฉพาะเครื่อง ด้วย 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 เช่น ขนาดการแสดงผลของแอป ภาษา หรือธีมของระบบ
  • การเปลี่ยนแปลงการกำหนดค่า: เปลี่ยนการกำหนดค่าผ่านการโต้ตอบของผู้ใช้ สำหรับ เช่น ผู้ใช้อาจเปลี่ยนการตั้งค่าอุปกรณ์ หรือการโต้ตอบทางกายภาพ กับอุปกรณ์ ไม่มีวิธีป้องกัน การเปลี่ยนแปลงการกำหนดค่า
  • การสร้าง Activity ใหม่: การเปลี่ยนแปลงการกำหนดค่าจะทำให้เกิดการสร้าง Activity ขึ้นมาใหม่ โดยค่าเริ่มต้น ซึ่งเป็นกลไกในตัวเพื่อเริ่มต้นสถานะของแอปอีกครั้งสำหรับ การกำหนดค่าใหม่
  • การทำลาย Activity: การสร้างใหม่ Activity ทำให้ระบบทำลาย อินสแตนซ์เก่าของ Activity แล้วสร้างอินสแตนซ์ใหม่ในตำแหน่งนั้น อินสแตนซ์เก่าคือ ล้าสมัยแล้ว การอ้างอิงที่เหลืออยู่จะทำให้หน่วยความจำรั่วไหล ข้อบกพร่อง หรือข้อขัดข้อง
  • สถานะ: สถานะในอินสแตนซ์ Activity เก่าไม่มีอยู่ในอินสแตนซ์ใหม่ Activity เนื่องจากเป็นอินสแตนซ์ออบเจ็กต์ 2 รายการที่แตกต่างกัน เก็บรักษา สถานะของแอปและผู้ใช้ตามที่อธิบายไว้ในบันทึกสถานะ 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 Callback เมื่อไม่มีการสร้าง 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()