ผู้ใช้คาดหวังให้แอปโหลดเร็วและตอบสนองได้ดี แอปที่ใช้เวลาเริ่มต้นนานไม่เป็นไปตามความคาดหวังนี้และอาจทำให้ผู้ใช้ผิดหวัง ประสบการณ์ที่ไม่ดีประเภทนี้อาจทำให้ผู้ใช้ให้คะแนนแอปของคุณต่ำใน 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 อย่างต่อไปนี้
- โหลดและเปิดแอป
- แสดงหน้าต่างเริ่มต้นที่ว่างเปล่าสำหรับแอปทันทีหลังจากเปิดตัว
- สร้างกระบวนการของแอป
ทันทีที่ระบบสร้างกระบวนการของแอป กระบวนการของแอปจะรับผิดชอบขั้นตอนถัดไป ดังนี้
- สร้างออบเจ็กต์แอป
- เปิดตัวเทรดหลัก
- สร้างกิจกรรมหลัก
- เริ่มต้น UI
- วาด UI บนหน้าจอ
เมื่อกระบวนการของแอปวาดภาพครั้งแรกเสร็จสมบูรณ์ กระบวนการของระบบจะสลับหน้าต่างพื้นหลังที่แสดงออก โดยแทนที่ด้วยกิจกรรมหลัก ในขั้นตอนนี้ ผู้ใช้จะเริ่มใช้แอปได้
ดูข้อมูลเพิ่มเติมเกี่ยวกับระยะของเลย์เอาต์ใน Compose ได้ที่ระยะของ Jetpack Compose
ปัญหาด้านประสิทธิภาพอาจเกิดขึ้นระหว่างการสร้างแอปและการสร้างกิจกรรมโฮสต์
การสร้างแอป
เมื่อแอปเปิดขึ้น หน้าต่างเริ่มต้นที่ว่างเปล่าจะยังคงอยู่บนหน้าจอจนกว่าระบบจะวาดแอปเสร็จเป็นครั้งแรก ณ จุดนี้ กระบวนการของระบบจะสลับหน้าต่างเริ่มต้นสำหรับแอปของคุณ เพื่อให้ผู้ใช้โต้ตอบกับแอปได้
หากคุณลบล้าง Application.onCreate ในแอปของคุณเอง ระบบจะเรียกใช้เมธอด onCreate ในออบเจ็กต์แอปของคุณ หลังจากนั้น แอปจะสร้างเทรดหลักหรือที่เรียกว่าเทรด UI และมอบหมายงานให้สร้างกิจกรรมโฮสต์ของแอป
จากจุดนี้ กระบวนการระดับระบบและระดับแอปจะดำเนินต่อไปตามระยะวงจรของแอป
การสร้างกิจกรรม
หลังจากกระบวนการของแอปสร้างกิจกรรมแล้ว กิจกรรมจะดำเนินการต่อไปนี้
- เริ่มต้นค่า
- เรียกใช้เครื่องมือสร้าง
- เรียกใช้เมธอด 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 กระบวนการของระบบจะแสดงหน้าจอว่างเปล่าจนกว่าแอปจะแสดงผลกิจกรรมเสร็จสิ้น
วิธีระบุการเริ่มต้นแอปใน Perfetto
ในการแก้ไขข้อบกพร่องเกี่ยวกับปัญหาการเริ่มต้นแอป คุณควรระบุสิ่งที่รวมอยู่ในระยะการเริ่มต้นแอปให้แน่ชัด หากต้องการระบุระยะการเริ่มต้นแอปทั้งหมดใน Perfetto ให้ทำตามขั้นตอนต่อไปนี้
ใน Perfetto ให้ค้นหาแถวที่มีเมตริกที่ได้มาจากการเริ่มต้นแอป Android หากไม่เห็น ให้ลองบันทึกการติดตามโดยใช้แอป System Tracing ในอุปกรณ์
รูปที่ 2 ส่วนเมตริกที่ได้มาจากการเริ่มต้นแอป Android ใน Perfetto คลิกชิ้นส่วนที่เกี่ยวข้อง แล้วกด m เพื่อเลือกชิ้นส่วนนั้น วงเล็บจะปรากฏรอบๆ ส่วนนั้นและระบุระยะเวลาที่ใช้ ระยะเวลาจะแสดงในแท็บรายการที่เลือกในปัจจุบันด้วย
ปักหมุดแถว "การเริ่มต้นแอป Android" โดยคลิกไอคอนหมุด ซึ่งจะปรากฏขึ้นเมื่อคุณวางตัวชี้เหนือแถว
เลื่อนไปที่แถวที่มีแอปที่เป็นปัญหา แล้วคลิกเซลล์แรกเพื่อขยายแถว
ซูมเข้าในชุดข้อความหลัก ซึ่งมักจะอยู่ด้านบน โดยกด w (กด s, a, d เพื่อซูมออก เลื่อนไปทางซ้าย และเลื่อนไปทางขวาตามลำดับ)
รูปที่ 3 ส่วนเมตริกที่ได้มาจากการเริ่มต้นแอป Android ถัดจากชุดข้อความหลักของแอป ส่วนเมตริกที่ได้มาจะช่วยให้เห็นสิ่งที่รวมอยู่ในการเริ่มต้นแอปได้ง่ายขึ้น คุณจึงแก้ไขข้อบกพร่องโดยละเอียดได้ต่อไป
เมื่อวิเคราะห์แอปพลิเคชัน 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
คุณต้องปิดใช้ตัวกรองเนื่องจากเซิร์ฟเวอร์ระบบเป็นผู้แสดงบันทึกนี้ ไม่ใช่ตัวแอปเอง
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) ได้ด้วยวิธีต่อไปนี้
- ตั้งค่าธีมเพื่อเปลี่ยนลักษณะที่ปรากฏของหน้าจอเริ่มต้น (Splash Screen)
- ควบคุมระยะเวลาที่หน้าจอเริ่มต้น (Splash Screen) จะแสดงด้วย
windowSplashScreenAnimationDuration - ปรับแต่งภาพเคลื่อนไหวของหน้าจอเริ่มต้น และจัดการภาพเคลื่อนไหวสำหรับการปิดหน้าจอเริ่มต้นอย่างราบรื่น
นอกจากนี้ ไลบรารีความเข้ากันได้ยังพอร์ต SplashScreen API กลับมาเพื่อเปิดใช้ความเข้ากันได้แบบย้อนหลัง และสร้างรูปลักษณ์ที่สอดคล้องกันสำหรับการแสดงหน้าจอเริ่มต้น (Splash Screen) ใน Android ทุกเวอร์ชัน
ดูรายละเอียดได้จากคำแนะนำในการย้ายข้อมูลหน้าจอเริ่มต้น (Splash Screen)
แนะนำสำหรับคุณ
- หมายเหตุ: ข้อความลิงก์จะแสดงเมื่อ JavaScript ปิดอยู่
- การแสดงผลช้า
- บันทึกเมตริก Macrobenchmark
- สร้างโปรไฟล์พื้นฐาน{:#creating-profile-rules}