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

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

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

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

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

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

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

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

Cold Start

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

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

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

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

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

  1. สร้างออบเจ็กต์แอป
  2. เปิดชุดข้อความหลัก
  3. สร้างกิจกรรมหลัก
  4. เพิ่มยอดดู
  5. จัดเลย์เอาต์หน้าจอ
  6. ดึงข้อมูลครั้งแรก

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

รูปที่ 1 แสดงวิธีที่ระบบและแอปส่งต่องานระหว่างกัน

รูปที่ 1 ภาพแสดงส่วนสําคัญของการเปิดแอปแบบ Cold Start

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

การสร้างแอป

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

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

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

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

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

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

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

Warm Start

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

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

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

Hot Start

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

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

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

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

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

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

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

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

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

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

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

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

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

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

ประโยชน์ของการใช้เมตริกสําหรับธุรกิจเริ่มต้น

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

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

Android Vitals

Android Vitals ช่วยปรับปรุงประสิทธิภาพของแอปได้โดยแจ้งเตือนคุณใน Play Console เมื่อเวลาเริ่มต้นของแอปนานเกินไป

Android Vitals จะถือว่าเวลาเริ่มต้นของแอปต่อไปนี้นานเกินไป

  • การเริ่มต้นจากต้นใช้เวลา 5 วินาทีขึ้นไป
  • การเริ่มต้นอุ่นเครื่องใช้เวลา 2 วินาทีขึ้นไป
  • การเริ่มต้นร้อนใช้เวลา 1.5 วินาทีขึ้นไป

Android Vitals ใช้เมตริกเวลาที่ใช้ในการแสดงผลครั้งแรก (TTID) ดูข้อมูลเกี่ยวกับวิธีที่ Google Play รวบรวมข้อมูล Android Vitals ได้ที่เอกสารประกอบของ Play Console

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

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

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

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

เรียกข้อมูล TTID

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

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

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

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

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

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

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

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

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

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

ระบบจะกำหนด TTID เมื่อ Choreographer เรียกใช้เมธอด onDraw() ของกิจกรรม และเมื่อทราบว่ามีการเรียกใช้เมธอดนี้เป็นครั้งแรก อย่างไรก็ตาม ระบบไม่ทราบว่าจะกำหนด 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() ได้ที่ส่วนต่อไปนี้

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

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

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

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

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

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

Kotlin

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()
                }
            }
        }
    }
}

Java

public class MainActivity extends ComponentActivity {
    private FullyDrawnReporter fullyDrawnReporter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        fullyDrawnReporter = getFullyDrawnReporter();
        fullyDrawnReporter.addOnReportDrawnListener(() -> {
            // Trigger the UI update.
            return Unit.INSTANCE;
        });

        new Thread(new Runnable() {
            @Override
            public void run() {
                fullyDrawnReporter.addReporter();
                // Do the background work.
                fullyDrawnReporter.removeReporter();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                fullyDrawnReporter.addReporter();
                // Do the background work.
                fullyDrawnReporter.removeReporter();
            }
        }).start();
    }
}

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

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

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

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

แก้ปัญหาที่พบบ่อย

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

การสร้างอินสแตนซ์แอปจำนวนมาก

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

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

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

วินิจฉัยปัญหา

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

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

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

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

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

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

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

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

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

หากแอปใช้ผู้ให้บริการเนื้อหาเพื่อเริ่มต้นคอมโพเนนต์แอปเมื่อเริ่มต้น ให้ลองใช้ไลบรารีการเริ่มต้นแอปแทน

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

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

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

วินิจฉัยปัญหา

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

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

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

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

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

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

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

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

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

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

หน้าจอแนะนำที่กําหนดเอง

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

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

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

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

ดูรายละเอียดได้จากคำแนะนำในการย้ายข้อมูลหน้าจอแนะนำ