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

ปัญหาด้านประสิทธิภาพอาจเกิดขึ้นระหว่างการสร้างแอปและการสร้างกิจกรรม
การสร้างแอป
เมื่อแอปเปิดขึ้น หน้าต่างเริ่มต้นเปล่าจะยังคงอยู่บนหน้าจอจนกว่าระบบจะวาดแอปเสร็จเป็นครั้งแรก เมื่อถึงจุดนี้ กระบวนการของระบบจะสลับหน้าต่างเริ่มต้นสําหรับแอปของคุณเพื่อให้ผู้ใช้โต้ตอบกับแอปได้
หากคุณลบล้าง Application.onCreate()
ในแอปของคุณเอง ระบบจะเรียกใช้เมธอด onCreate()
บนออบเจ็กต์แอป หลังจากนั้น แอปจะสร้างเทรดหลักหรือที่เรียกว่าเทรด UI และมอบหมายให้สร้างกิจกรรมหลัก
จากจุดนี้ กระบวนการระดับระบบและแอปจะดำเนินไปตามระยะต่างๆ ของวงจรชีวิตของแอป
การสร้างกิจกรรม
หลังจากกระบวนการของแอปสร้างกิจกรรมแล้ว กิจกรรมจะดําเนินการต่อไปนี้
- เริ่มต้นค่า
- เรียกเครื่องมือสร้าง
- เรียกเมธอดการเรียกกลับ เช่น
Activity.onCreate()
ที่เหมาะสมกับสถานะวงจรชีวิตของกิจกรรมปัจจุบัน
โดยทั่วไปแล้ว เมธอด onCreate()
จะมีผลต่อเวลาในการโหลดมากที่สุด เนื่องจากจะทํางานโดยมีค่าใช้จ่ายเพิ่มเติมสูงสุด ซึ่งได้แก่ การโหลดและการขยายวิว และการจัดเตรียมออบเจ็กต์ที่จําเป็นสําหรับให้กิจกรรมทํางาน
Warm Start
การเริ่มแอปแบบ Warm Start ประกอบด้วยการดำเนินการชุดย่อยที่เกิดขึ้นระหว่างการเริ่มแอปแบบ Cold Start ในขณะเดียวกัน วิธีการนี้ยังทำให้เกิดค่าใช้จ่ายเพิ่มเติมมากกว่าการเริ่มต้นแบบ Hot Start สถานะที่เป็นไปได้หลายสถานะอาจถือเป็นการเริ่มต้นแบบอุ่นเครื่อง เช่น สถานะต่อไปนี้
ผู้ใช้ออกจากแอปของคุณแล้วเปิดแอปอีกครั้ง กระบวนการอาจทำงานต่อไป แต่แอปต้องสร้างกิจกรรมขึ้นมาใหม่ตั้งแต่ต้นโดยใช้การเรียกใช้
onCreate()
ระบบจะย้ายแอปของคุณออกจากหน่วยความจำ จากนั้นผู้ใช้จะเปิดแอปอีกครั้ง กระบวนการและกิจกรรมต้องเริ่มต้นใหม่ แต่งานอาจได้รับประโยชน์บางอย่างจากกลุ่มสถานะอินสแตนซ์ที่บันทึกไว้ซึ่งส่งไปยัง
onCreate()
Hot Start
การเริ่มแอปแบบ Hot Start จะมีค่าใช้จ่ายเพิ่มเติมต่ำกว่าการเริ่มแอปแบบ Cold Start ในการเริ่มต้นแบบร้อน ระบบจะนำกิจกรรมของคุณไปไว้ที่เบื้องหน้า หากกิจกรรมทั้งหมดของแอปยังคงอยู่ในหน่วยความจํา แอปจะหลีกเลี่ยงการเริ่มต้นวัตถุ การจัดวาง และการเรนเดอร์ซ้ำได้
อย่างไรก็ตาม หากมีการล้างหน่วยความจำบางส่วนเพื่อตอบสนองต่อเหตุการณ์การลดหน่วยความจำ เช่น onTrimMemory()
จะต้องสร้างออบเจ็กต์เหล่านี้ขึ้นมาใหม่เพื่อตอบสนองต่อเหตุการณ์การเริ่มต้นอย่างรวดเร็ว
การเริ่มต้นแบบร้อนจะแสดงลักษณะการทำงานบนหน้าจอเหมือนกับสถานการณ์การเริ่มต้นแบบเย็น กระบวนการของระบบจะแสดงหน้าจอว่างเปล่าจนกว่าแอปจะแสดงผลกิจกรรมเสร็จ

วิธีระบุการเริ่มต้นของแอปใน Perfetto
หากต้องการแก้ไขข้อบกพร่องเกี่ยวกับการเริ่มต้นของแอป คุณควรระบุสิ่งที่รวมอยู่ในระยะเริ่มต้นของแอป หากต้องการระบุระยะเริ่มต้นของแอปทั้งหมดใน Perfetto ให้ทําตามขั้นตอนต่อไปนี้
ใน Perfetto ให้ค้นหาแถวที่มีเมตริกที่มาจาก "การเริ่มต้นแอป Android" หากไม่เห็น ให้ลองบันทึกการติดตามโดยใช้แอปการติดตามระบบในอุปกรณ์
รูปที่ 3. ข้อมูลเมตริกที่ได้จาก "การเริ่มต้นแอป Android" ใน Perfetto คลิกส่วนที่เกี่ยวข้องแล้วกด m เพื่อเลือกส่วน วงเล็บจะปรากฏรอบๆ ส่วนของภาพและระบุระยะเวลา ระยะเวลาจะแสดงในแท็บการเลือกปัจจุบันด้วย
ปักหมุดแถวแอป Android ที่เริ่มต้นโดยคลิกไอคอนปักหมุด ซึ่งจะปรากฏขึ้นเมื่อคุณวางเคอร์เซอร์เหนือแถว
เลื่อนไปที่แถวที่มีแอปที่เป็นปัญหา แล้วคลิกเซลล์แรกเพื่อขยายแถว
ซูมเข้าชุดข้อความหลัก ซึ่งมักจะอยู่ด้านบนโดยกด w (กด s, a, d เพื่อซูมออก เลื่อนไปทางซ้าย และเลื่อนไปทางขวาตามลำดับ)
รูปที่ 4. ข้อมูลเมตริกที่ได้จาก "การเริ่มต้นแอป Android" ถัดจากเทรดหลักของแอป ส่วนเมตริกที่ได้จากข้อมูลที่มีอยู่ช่วยให้คุณเห็นสิ่งที่รวมอยู่ในการเริ่มต้นแอปได้อย่างชัดเจนยิ่งขึ้น คุณจึงแก้ไขข้อบกพร่องได้ละเอียดยิ่งขึ้น
ใช้เมตริกเพื่อตรวจสอบและปรับปรุงสตาร์ทอัป
หากต้องการวิเคราะห์ประสิทธิภาพเวลาเริ่มต้นอย่างถูกต้อง คุณสามารถติดตามเมตริกที่แสดงเวลาที่แอปใช้ในการเริ่มต้น 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
คุณต้องปิดใช้ตัวกรองเนื่องจากเซิร์ฟเวอร์ของระบบจะแสดงบันทึกนี้ ไม่ใช่แอป

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 นี้ช่วยให้เวลาเริ่มต้นเร็วขึ้นและให้คุณปรับแต่งหน้าจอแนะนำได้ดังนี้
- ตั้งค่าธีมเพื่อเปลี่ยนลักษณะที่ปรากฏของหน้าจอแนะนำ
- ควบคุมระยะเวลาที่หน้าจอแนะนำจะแสดงด้วย
windowSplashScreenAnimationDuration
- ปรับแต่งภาพเคลื่อนไหวของหน้าจอแนะนำ และจัดการภาพเคลื่อนไหวเพื่อปิดหน้าจอแนะนำอย่างราบรื่น
นอกจากนี้ ไลบรารี compat ยังพอร์ต SplashScreen
API ไปยังเวอร์ชันเก่าเพื่อให้ใช้งานร่วมกันได้แบบย้อนหลัง และสร้างรูปลักษณ์ที่สอดคล้องกับการแสดงภาพหน้าจอใน Android ทุกเวอร์ชัน
ดูรายละเอียดได้จากคำแนะนำในการย้ายข้อมูลหน้าจอแนะนำ
แนะนำสำหรับคุณ
- หมายเหตุ: ข้อความลิงก์จะแสดงเมื่อ JavaScript ปิดอยู่
- การแสดงผลช้า
- บันทึกเมตริกการเปรียบเทียบประสิทธิภาพแบบแมโคร
- สร้างโปรไฟล์พื้นฐาน{:#creating-profile-rules}