เวลาเริ่มต้นของแอป

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

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

ทำความเข้าใจสถานะการเริ่มต้นแอปแบบต่างๆ

การเปิดแอปอาจเกิดขึ้นในสถานะใดสถานะหนึ่งจาก 3 สถานะ ได้แก่ Cold Start, Warm Start หรือ Hot Start แต่ละสถานะจะส่งผลต่อระยะเวลาที่แอปจะปรากฏต่อผู้ใช้ ในการเริ่มต้นแบบ Cold Start แอปจะเริ่มตั้งแต่ต้น ในสถานะอื่นๆ ระบบจะต้องนำแอปที่ทำงานอยู่จากเบื้องหลังมาไว้ที่เบื้องหน้า

เราขอแนะนำให้คุณเพิ่มประสิทธิภาพโดยอิงตามสมมติฐานของ Cold Start เสมอ การดำเนินการนี้จะช่วยปรับปรุงประสิทธิภาพของ Warm Start และ Hot Start ด้วยเช่นกัน

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

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

Cold Start

Cold Start หมายถึงการที่แอปเริ่มทำงานตั้งแต่ต้น ซึ่งหมายความว่ากระบวนการของระบบจะสร้างกระบวนการของแอปจนกว่าจะเริ่มดำเนินการนี้ การเริ่มระบบใหม่ทั้งหมดจะเกิดขึ้นในกรณีต่างๆ เช่น แอปเปิดขึ้นเป็นครั้งแรกนับตั้งแต่ที่อุปกรณ์เปิดเครื่องหรือนับตั้งแต่ที่ระบบปิดแอป

การเริ่มต้นประเภทนี้เป็นความท้าทายที่ยิ่งใหญ่ที่สุดในการลดเวลาเริ่มต้นระบบ เนื่องจากระบบและแอปมีงานที่ต้องทำมากกว่าในสถานะการเปิดใช้งานอื่นๆ

เมื่อเริ่ม Cold Start ระบบจะมีงาน 3 อย่างต่อไปนี้

  1. โหลดและเปิดแอป
  2. แสดงหน้าต่างเริ่มต้นที่ว่างเปล่าสำหรับแอปทันทีหลังจากเปิดตัว
  3. สร้างกระบวนการของแอป

ทันทีที่ระบบสร้างกระบวนการของแอป กระบวนการของแอปจะรับผิดชอบขั้นตอนถัดไป ดังนี้

  1. สร้างออบเจ็กต์แอป
  2. เปิดตัวเทรดหลัก
  3. สร้างกิจกรรมหลัก
  4. เริ่มต้น UI
  5. วาด UI บนหน้าจอ

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

ดูข้อมูลเพิ่มเติมเกี่ยวกับระยะของเลย์เอาต์ใน Compose ได้ที่ระยะของ Jetpack Compose

ปัญหาด้านประสิทธิภาพอาจเกิดขึ้นระหว่างการสร้างแอปและการสร้างกิจกรรมโฮสต์

การสร้างแอป

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

หากคุณลบล้าง Application.onCreate ในแอปของคุณเอง ระบบจะเรียกใช้เมธอด onCreate ในออบเจ็กต์แอปของคุณ หลังจากนั้น แอปจะสร้างเทรดหลักหรือที่เรียกว่าเทรด UI และมอบหมายงานให้สร้างกิจกรรมโฮสต์ของแอป

จากจุดนี้ กระบวนการระดับระบบและระดับแอปจะดำเนินต่อไปตามระยะวงจรของแอป

การสร้างกิจกรรม

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

  1. เริ่มต้นค่า
  2. เรียกใช้เครื่องมือสร้าง
  3. เรียกใช้เมธอด Callback เช่น Activity.onCreate ที่เหมาะสมกับสถานะวงจรปัจจุบันของกิจกรรม

โดยปกติแล้ว วิธี onCreate จะส่งผลต่อเวลาในการโหลดมากที่สุด ในแอป Jetpack Compose โอเวอร์เฮดของเมธอด onCreate มักมาจากองค์ประกอบเริ่มต้น กรณีนี้จะเกิดขึ้นเมื่อแอปเรียกใช้ setContent และเรียกใช้ Composable ระดับบนสุด ลำดับชั้น UI ที่ซับซ้อนหรือลึก หรือ Composable ที่ทำการคำนวณอย่างหนักในเทรดหลักอาจเพิ่มเวลาในการคอมโพสนี้

การเริ่มต้นแบบวอร์มสตาร์ท

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

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

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

การสตาร์ทเครื่องยนต์ขณะร้อน

Hot Start ของแอปมีค่าใช้จ่ายในการดำเนินการต่ำกว่า Cold Start ในการเริ่มต้นแบบร้อน ระบบจะนำกิจกรรมโฮสต์ของแอปมาไว้ที่เบื้องหน้า หาก UI ทั้งหมดของแอปยังคงอยู่ในหน่วยความจำ แอปจะหลีกเลี่ยงการเริ่มต้นออบเจ็กต์ การเริ่มต้น UI และการแสดงผลซ้ำได้

อย่างไรก็ตาม หากมีการล้างหน่วยความจำบางส่วนเพื่อตอบสนองต่อเหตุการณ์การตัดแต่งหน่วยความจำ เช่น onTrimMemory คุณจะต้องสร้างออบเจ็กต์เหล่านี้ขึ้นมาใหม่เพื่อตอบสนองต่อเหตุการณ์ Hot Start

Hot Start จะแสดงลักษณะการทำงานบนหน้าจอเหมือนกับสถานการณ์ Cold Start กระบวนการของระบบจะแสดงหน้าจอว่างเปล่าจนกว่าแอปจะแสดงผลกิจกรรมเสร็จสิ้น

รูปที่ 1 แผนภาพแสดงสถานะการเริ่มต้นระบบต่างๆ และกระบวนการที่เกี่ยวข้อง โดยแต่ละสถานะจะเริ่มต้นจากเฟรมแรกที่วาด

วิธีระบุการเริ่มต้นแอปใน Perfetto

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

  1. ใน Perfetto ให้ค้นหาแถวที่มีเมตริกที่ได้มาจากการเริ่มต้นแอป Android หากไม่เห็น ให้ลองบันทึกการติดตามโดยใช้แอป System Tracing ในอุปกรณ์

    รูปที่ 2 ส่วนเมตริกที่ได้มาจากการเริ่มต้นแอป Android ใน Perfetto
  2. คลิกชิ้นส่วนที่เกี่ยวข้อง แล้วกด m เพื่อเลือกชิ้นส่วนนั้น วงเล็บจะปรากฏรอบๆ ส่วนนั้นและระบุระยะเวลาที่ใช้ ระยะเวลาจะแสดงในแท็บรายการที่เลือกในปัจจุบันด้วย

  3. ปักหมุดแถว "การเริ่มต้นแอป Android" โดยคลิกไอคอนหมุด ซึ่งจะปรากฏขึ้นเมื่อคุณวางตัวชี้เหนือแถว

  4. เลื่อนไปที่แถวที่มีแอปที่เป็นปัญหา แล้วคลิกเซลล์แรกเพื่อขยายแถว

  5. ซูมเข้าในชุดข้อความหลัก ซึ่งมักจะอยู่ด้านบน โดยกด w (กด s, a, d เพื่อซูมออก เลื่อนไปทางซ้าย และเลื่อนไปทางขวาตามลำดับ)

    รูปที่ 3 ส่วนเมตริกที่ได้มาจากการเริ่มต้นแอป Android ถัดจากชุดข้อความหลักของแอป
  6. ส่วนเมตริกที่ได้มาจะช่วยให้เห็นสิ่งที่รวมอยู่ในการเริ่มต้นแอปได้ง่ายขึ้น คุณจึงแก้ไขข้อบกพร่องโดยละเอียดได้ต่อไป

เมื่อวิเคราะห์แอปพลิเคชัน Jetpack Compose คุณสามารถใช้การติดตามองค์ประกอบเพื่อดูรายละเอียดที่ละเอียดยิ่งขึ้นเกี่ยวกับประสิทธิภาพของ UI ให้ความสนใจเป็นพิเศษกับส่วนการติดตามภายในชุดข้อความหลักที่ขึ้นต้นด้วย Choreographer#doFrame มองหาชิ้นส่วนอย่าง Compose:recompose, Compose:layout และ Compose:draw ซึ่งจะแสดงระยะเวลาที่แอปใช้ในการเขียน วัดค่าและวาง รวมถึงวาดคอมโพเนนต์ที่เฉพาะเจาะจง

ใช้เมตริกเพื่อตรวจสอบและปรับปรุงการเริ่มต้นระบบ

หากต้องการวิเคราะห์ประสิทธิภาพเวลาเริ่มต้นระบบอย่างเหมาะสม คุณสามารถติดตามเมตริกที่แสดงระยะเวลาที่แอปใช้ในการเริ่มต้นระบบได้ Android มีวิธีต่างๆ ในการแสดงให้เห็นว่าแอปของคุณมีปัญหาและช่วยคุณวิเคราะห์ปัญหา

ประโยชน์ของการใช้เมตริกการเริ่มต้นระบบ

Android ใช้เมตริกเวลาที่ใช้ในการแสดงผลครั้งแรก (TTID) และเวลาที่ใช้ในการแสดงผลทั้งหมด (TTFD) เพื่อเพิ่มประสิทธิภาพการเริ่มต้นแอปแบบ Cold Start และ Warm Start Android Runtime (ART) จะใช้ข้อมูลจากเมตริกเหล่านี้เพื่อคอมไพล์โค้ดล่วงหน้าอย่างมีประสิทธิภาพเพื่อเพิ่มประสิทธิภาพการเริ่มต้นระบบในอนาคต

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

เวลาที่ใช้ในการแสดงผลครั้งแรก

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

TTID วัดเป็นค่าเวลาที่แสดงถึงเวลาที่ผ่านไปทั้งหมดซึ่งรวมถึงลำดับเหตุการณ์ต่อไปนี้

  • กำลังเริ่มกระบวนการ
  • กำลังเริ่มต้นออบเจ็กต์
  • กำลังสร้างและเริ่มต้นกิจกรรมของโฮสต์
  • กำลังเริ่มต้น UI
  • การวาดแอปเป็นครั้งแรก

ดึงข้อมูล TTID

หากต้องการค้นหา TTID ให้ค้นหาบรรทัดเอาต์พุตที่มีค่าชื่อ Displayed ในเครื่องมือบรรทัดคำสั่ง Logcat ค่านี้คือ TTID และมีลักษณะคล้ายกับตัวอย่างต่อไปนี้ ซึ่ง TTID คือ 3s534ms

ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms

หากต้องการค้นหา TTID ใน Android Studio ให้ปิดใช้ตัวกรองในมุมมอง Logcat จากเมนูตัวกรองแบบเลื่อนลง แล้วค้นหาเวลา Displayed ดังที่แสดงในรูปที่ 4 คุณต้องปิดใช้ตัวกรองเนื่องจากเซิร์ฟเวอร์ระบบเป็นผู้แสดงบันทึกนี้ ไม่ใช่ตัวแอปเอง

รูปที่ 4 ปิดใช้ตัวกรองและค่า Displayed ใน Logcat

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

บางครั้งบรรทัด Displayed ในเอาต์พุต Logcat จะมีช่องเพิ่มเติมสำหรับเวลารวม ดังตัวอย่างต่อไปนี้

ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms (total +1m22s643ms)

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

เราขอแนะนำให้ใช้ Logcat ใน Android Studio แต่หากไม่ได้ใช้ Android Studio คุณก็วัด TTID ได้โดยเรียกใช้แอปด้วยคำสั่งเครื่องมือจัดการกิจกรรม Shell adb ตัวอย่าง

adb [-d|-e|-s <serialNumber>] shell am start -S -W
com.example.app/.MainActivity
-c android.intent.category.LAUNCHER
-a android.intent.action.MAIN

เมตริก Displayed จะปรากฏในเอาต์พุต Logcat เหมือนเดิม หน้าต่างเทอร์มินัลจะแสดงข้อความต่อไปนี้

Starting: Intent
Activity: com.example.app/.MainActivity
ThisTime: 2044
TotalTime: 2044
WaitTime: 2054
Complete

อาร์กิวเมนต์ -c และ -a จะใส่หรือไม่ใส่ก็ได้ และช่วยให้คุณระบุ <category> และ <action> ได้

เวลาที่ใช้ในการแสดงผลเต็มรูปแบบ

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

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

TTFD ของการดึงข้อมูล

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

หากต้องการวัด TTFD ให้เรียกใช้ reportFullyDrawn หลังจากวาด UI และข้อมูลทั้งหมดเสร็จสมบูรณ์แล้ว อย่าเรียกใช้ reportFullyDrawn ก่อนที่หน้าต่างของกิจกรรมแรกจะวาดและแสดงเป็นครั้งแรกตามที่ระบบวัด เนื่องจากระบบจะรายงานเวลาที่ระบบวัด กล่าวคือ หากคุณเรียกใช้ reportFullyDrawn ก่อนที่ระบบจะตรวจพบ TTID ระบบจะรายงานทั้ง TTID และ TTFD เป็นค่าเดียวกัน และค่านี้คือค่า TTID

เมื่อคุณใช้ reportFullyDrawn Logcat จะแสดงเอาต์พุตเหมือนตัวอย่างต่อไปนี้ ซึ่ง TTFD คือ 1s54ms

system_process I/ActivityManager: Fully drawn {package}/.MainActivity: +1s54ms

บางครั้งเอาต์พุต Logcat จะมีเวลา total ตามที่อธิบายไว้ในเวลาที่ใช้ในการแสดงผลครั้งแรก

หากเวลาในการแสดงผลช้ากว่าที่ต้องการ คุณสามารถลองระบุจุดคอขวดในกระบวนการเริ่มต้นระบบได้

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

ปรับปรุงความแม่นยำของเวลาเริ่มต้น

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

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

หากต้องการรวมจำนวนผู้ใช้ในรายการเป็นส่วนหนึ่งของเวลาการเปรียบเทียบ ให้รับ FullyDrawnReporter โดยใช้ fullyDrawnReporter และเพิ่ม Reporter ลงในโค้ดแอป ปล่อย Reporter หลังจากงานที่ทำอยู่เบื้องหลังสร้างรายการเสร็จแล้ว

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

หากแอปใช้ Jetpack Compose คุณสามารถใช้ API ต่อไปนี้เพื่อระบุสถานะที่วาดเสร็จสมบูรณ์แล้ว

  • ReportDrawn: บ่งบอกว่า Composable พร้อมสำหรับการโต้ตอบทันที
  • ReportDrawnWhen: รับเพรดิเคต เช่น list.count > 0 เพื่อระบุว่า Composable พร้อมสำหรับการโต้ตอบเมื่อใด
  • ReportDrawnAfter: ใช้เมธอดการระงับซึ่งเมื่อเสร็จสมบูรณ์แล้วจะระบุว่า Composable พร้อมสำหรับการโต้ตอบ

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

class MainActivity : ComponentActivity() {

    sealed interface ActivityState {
        data object LOADING : ActivityState
        data object LOADED : ActivityState
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            var activityState by remember {
                mutableStateOf(ActivityState.LOADING as ActivityState)
            }
            fullyDrawnReporter.addOnReportDrawnListener {
                activityState = ActivityState.LOADED
            }
            ReportFullyDrawnTheme {
                when(activityState) {
                    is ActivityState.LOADING -> {
                        // Display the loading UI.
                    }
                    is ActivityState.LOADED -> {
                        // Display the full UI.
                    }
                }
            }
            SideEffect {
                fullyDrawnReporter.addReporter()
                lifecycleScope.launch(Dispatchers.IO) {
                    // Perform the background operation.
                    fullyDrawnReporter.removeReporter()
                }
                fullyDrawnReporter.addReporter()
                lifecycleScope.launch(Dispatchers.IO) {
                    // Perform the background operation.
                    fullyDrawnReporter.removeReporter()
                }
            }
        }
    }
}
ระบุจุดคอขวด

หากต้องการค้นหาจุดคอขวด คุณสามารถใช้เครื่องมือสร้างโปรไฟล์ CPU ของ Android Studio ดูข้อมูลเพิ่มเติมได้ที่ตรวจสอบกิจกรรมของ CPU ด้วยเครื่องมือสร้างโปรไฟล์ CPU

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

แก้ปัญหาที่พบได้ทั่วไป

ส่วนนี้จะพูดถึงปัญหาหลายอย่างที่มักส่งผลต่อประสิทธิภาพการเริ่มต้นแอป ปัญหาเหล่านี้เกี่ยวข้องกับการเริ่มต้นออบเจ็กต์แอปและกิจกรรมเป็นหลัก รวมถึงการโหลดหน้าจอ

การเริ่มต้นแอปที่ใช้ทรัพยากรมาก

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

การเริ่มต้นบางอย่างอาจไม่จำเป็นเลย เช่น เมื่อเริ่มต้นข้อมูลสถานะสำหรับกิจกรรมหลักเมื่อแอปเริ่มทำงานเพื่อตอบสนองต่อ Intent เมื่อมี Intent แอปจะใช้เฉพาะชุดย่อยของข้อมูลสถานะที่เริ่มต้นก่อนหน้านี้

ความท้าทายอื่นๆ ในระหว่างการเริ่มต้นแอป ได้แก่ เหตุการณ์การเก็บขยะที่ส่งผลกระทบหรือมีจำนวนมาก หรือ I/O ของดิสก์ที่เกิดขึ้นพร้อมกันกับการเริ่มต้น ซึ่งจะบล็อกกระบวนการเริ่มต้นเพิ่มเติม การเก็บขยะเป็นสิ่งที่ต้องพิจารณาเป็นพิเศษเมื่อใช้รันไทม์ Dalvik ส่วน Android Runtime (ART) จะเก็บขยะพร้อมกัน ซึ่งช่วยลดผลกระทบจากการดำเนินการดังกล่าว

วิเคราะห์ปัญหา

คุณสามารถใช้การติดตามเมธอดหรือการติดตามในบรรทัดเพื่อลองวิเคราะห์ปัญหาได้

การติดตามเมธอด

การเรียกใช้เครื่องมือสร้างโปรไฟล์ CPU แสดงให้เห็นว่าเมธอด callApplicationOnCreate จะเรียกใช้เมธอด com.example.customApplication.onCreate ในที่สุด หากเครื่องมือแสดงให้เห็นว่าเมธอดเหล่านี้ใช้เวลานานในการดำเนินการจนเสร็จสิ้น ให้สำรวจเพิ่มเติมเพื่อดูว่ามีงานใดเกิดขึ้นบ้าง

การติดตามในบรรทัด

ใช้การติดตามในบรรทัดเพื่อตรวจสอบสาเหตุที่เป็นไปได้ ซึ่งรวมถึงสิ่งต่อไปนี้

  • ฟังก์ชัน onCreate เริ่มต้นของแอป
  • ออบเจ็กต์ Singleton ส่วนกลางที่แอปของคุณเริ่มต้น
  • I/O ของดิสก์ การดีซีเรียลไลซ์ หรือลูปที่รัดกุมซึ่งอาจเกิดขึ้นระหว่างคอขวด

วิธีแก้ปัญหา

ไม่ว่าปัญหาจะเกิดจากการเริ่มต้นที่ไม่จำเป็นหรือเกิดจาก I/O ของดิสก์ วิธีแก้ปัญหาคือการเริ่มต้นแบบ Lazy กล่าวคือ ให้เริ่มต้นเฉพาะออบเจ็กต์ที่จำเป็นต้องใช้ทันทีเท่านั้น แทนที่จะสร้างออบเจ็กต์แบบคงที่ส่วนกลาง ให้เปลี่ยนไปใช้รูปแบบ Singleton ที่แอปจะเริ่มต้นออบเจ็กต์เฉพาะในครั้งแรกที่ต้องการใช้เท่านั้น

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

หากแอปใช้ Content Provider เพื่อเริ่มต้นคอมโพเนนต์ของแอปเมื่อเริ่มต้นระบบ ให้พิจารณาใช้ไลบรารี App Startup แทน

การเริ่มต้นกิจกรรมที่หนักหน่วง

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

  • การเริ่มต้น UI ขนาดใหญ่หรือซับซ้อน
  • การเริ่มต้นที่หนักหน่วงใน Composable
  • การบล็อกการวาดหน้าจอในดิสก์หรือ I/O ของเครือข่าย
  • กำลังโหลดและถอดรหัสบิตแมป
  • กำลังแรสเตอร์ออบเจ็กต์ VectorDrawable รายการ
  • การเริ่มต้นระบบย่อยอื่นๆ ของกิจกรรมโฮสต์ของแอป

วิเคราะห์ปัญหา

ในกรณีนี้ ทั้งการติดตามเมธอดและการติดตามในบรรทัดก็มีประโยชน์เช่นกัน

การติดตามเมธอด

เมื่อใช้เครื่องมือสร้างโปรไฟล์ CPU ให้สังเกตตัวสร้างคลาสย่อย Application และเมธอด com.example.customApplication.onCreate ของแอป

หากเครื่องมือแสดงให้เห็นว่าเมธอดเหล่านี้ใช้เวลานานในการดำเนินการจนเสร็จสิ้น ให้สำรวจเพิ่มเติมเพื่อดูว่ามีงานใดเกิดขึ้นบ้าง

การติดตามในบรรทัด

ใช้การติดตามในบรรทัดเพื่อตรวจสอบสาเหตุที่น่าจะเป็นไปได้ ซึ่งรวมถึงสิ่งต่อไปนี้

  • ฟังก์ชัน onCreate เริ่มต้นของแอป
  • ออบเจ็กต์ Singleton ส่วนกลางที่เริ่มต้น
  • I/O ของดิสก์ การดีซีเรียลไลซ์ หรือลูปที่รัดกุมซึ่งอาจเกิดขึ้นระหว่างคอขวด

วิธีแก้ปัญหา

ปัญหาคอขวดที่อาจเกิดขึ้นมีหลายอย่าง แต่ปัญหาที่พบบ่อย 2 ประการและวิธีแก้ไขมีดังนี้

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

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

หน้าจอเริ่มต้น (Splash Screen) ที่กำหนดเอง

คุณอาจเห็นเวลาที่เพิ่มขึ้นในระหว่างการเริ่มต้นระบบหากก่อนหน้านี้คุณใช้วิธีใดวิธีหนึ่งต่อไปนี้เพื่อใช้หน้าจอเริ่มต้น (Splash Screen) ที่กำหนดเองใน Android 11 (API ระดับ 30) หรือเวอร์ชันก่อนหน้า

  • การใช้แอตทริบิวต์ธีม windowDisablePreview เพื่อปิดหน้าจอว่างเปล่าเริ่มต้นที่ระบบแสดงขึ้นระหว่างการเปิดตัว
  • ใช้ Activity โดยเฉพาะ

ตั้งแต่ Android 12 เป็นต้นไป คุณต้องย้ายข้อมูลไปยัง SplashScreen API API นี้ช่วยให้เริ่มต้นระบบได้เร็วขึ้นและให้คุณปรับแต่งหน้าจอเริ่มต้น (Splash Screen) ได้ด้วยวิธีต่อไปนี้

นอกจากนี้ ไลบรารีความเข้ากันได้ยังพอร์ต SplashScreen API กลับมาเพื่อเปิดใช้ความเข้ากันได้แบบย้อนหลัง และสร้างรูปลักษณ์ที่สอดคล้องกันสำหรับการแสดงหน้าจอเริ่มต้น (Splash Screen) ใน Android ทุกเวอร์ชัน

ดูรายละเอียดได้จากคำแนะนำในการย้ายข้อมูลหน้าจอเริ่มต้น (Splash Screen)