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

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

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

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

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

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

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

เมตริกสำคัญ 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 ภาพแสดงส่วนสำคัญของ การเปิดแอปแบบเย็น

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

การสร้างแอป

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

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

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

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

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

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

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

Warm Start

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

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

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

Hot Start

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

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

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

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

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

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

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

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

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

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

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

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

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

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

ประโยชน์ของการใช้เมตริกของสตาร์ทอัพ

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

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

Android Vitals

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

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

  • Cold Startup ใช้เวลา 5 วินาทีขึ้นไป
  • Startup Warm ใช้เวลา 2 วินาทีขึ้นไป
  • Startup Hot ใช้เวลา 1.5 วินาทีขึ้นไป

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

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

เวลาที่ใช้ในการเริ่มแสดงผล (TTID) คือระยะเวลาที่ใช้ในการแสดงเฟรมแรก ของ UI ของแอป เมตริกนี้วัดระยะเวลาที่แอปใช้ในการสร้างเฟรมแรก ซึ่งรวมถึงการเริ่มต้นกระบวนการระหว่าง Cold Start การสร้างกิจกรรมระหว่าง Cold Start หรือ Warm Start และการแสดงเฟรมแรก การรักษา 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 activity manager ตัวอย่าง

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 เมื่อ 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 คือ 1 วินาที 54 มิลลิวินาที

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

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

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

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

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

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

ตัวอย่างเช่น หาก UI มีรายการแบบไดนามิก เช่น RecyclerView หรือรายการแบบเลื่อน อาจมีการป้อนข้อมูลโดยงานที่ทำงานเบื้องหลังซึ่งจะเสร็จสมบูรณ์หลังจาก วาดรายการเป็นครั้งแรก และหลังจากทำเครื่องหมาย 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: แสดงว่า Composable พร้อมสำหรับการโต้ตอบทันที
  • ReportDrawnWhen: ใช้เพรดิเคต เช่น list.count > 0 เพื่อระบุว่าเมื่อใดที่ Composable พร้อมสำหรับการโต้ตอบ
  • ReportDrawnAfter: ใช้เมธอดระงับที่เมื่อเสร็จสมบูรณ์แล้ว จะระบุว่า Composable พร้อมสำหรับการโต้ตอบ
ระบุคอขวด

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

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

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

การเริ่มต้นกิจกรรมที่มีการใช้งานสูง

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

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

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

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