ใน Android 16 (ระดับ API 36) ขึ้นไป ProfilingManager จะช่วยให้คุณบันทึกโปรไฟล์โดยอัตโนมัติเพื่อตอบสนองต่อทริกเกอร์ของระบบ เช่น ข้อผิดพลาดแอปพลิเคชันไม่ตอบสนอง (ANR), ข้อขัดข้องเกี่ยวกับหน่วยความจำไม่เพียงพอ (OutOfMemoryError) หรือเหตุการณ์การเริ่มต้นแอป เนื่องจากคุณไม่สามารถคาดเดาได้ว่าเหตุการณ์เหล่านี้จะเกิดขึ้นเมื่อใดในพื้นที่ทำงาน การเริ่มบันทึกด้วยตนเองล่วงหน้าจึงเป็นไปไม่ได้ในทางปฏิบัติ เมื่อใช้การสร้างโปรไฟล์ตามทริกเกอร์ ระบบจะจัดการการบันทึกและส่งโปรไฟล์ที่ได้ไปยังแอปเพื่อให้คุณทำสิ่งต่อไปนี้ได้
- แก้ไขข้อบกพร่องเกี่ยวกับปัญหาด้านความเสถียร: วิเคราะห์ ANR และหน่วยความจำรั่วไหลที่คาดเดาไม่ได้
- เพิ่มประสิทธิภาพเส้นทางของผู้ใช้ที่สำคัญ (CUJ): วิเคราะห์ขั้นตอนต่างๆ เช่น การเริ่มต้นแอป (
TRIGGER_TYPE_COLD_STARTและTRIGGER_TYPE_APP_FULLY_DRAWN) - ทำความเข้าใจพฤติกรรมของผู้ใช้: ตรวจสอบเหตุการณ์ต่างๆ เช่น การออกจากแอปที่ผู้ใช้เริ่ม
ดูรายการทริกเกอร์ที่ใช้ได้ทั้งหมดได้ที่ ProfilingTrigger
ประเภทของทริกเกอร์
ทริกเกอร์จะแตกต่างกันไปตามวิธีที่ระบบบันทึกโปรไฟล์เมื่อเกิดเหตุการณ์
ทริกเกอร์ที่ใช้การติดตามในเบื้องหลัง
ทริกเกอร์หลายอย่าง เช่น ANR เป็นผลมาจากปัญหาก่อนหน้านี้ การเริ่มการติดตามหลังจากที่ระบบตรวจพบ ANR เท่านั้นจะพลาดงานที่บล็อกซึ่งเป็นสาเหตุของ ANR หากต้องการบันทึกกิจกรรมที่นำไปสู่เหตุการณ์ ทริกเกอร์ต่อไปนี้จะบันทึกภาพรวมของการติดตามเบื้องหลังที่ทำงานอยู่ของระบบ หากไม่มีการติดตามในเบื้องหลัง ทำงานอยู่เมื่อเกิดเหตุการณ์ ระบบจะไม่รวบรวมโปรไฟล์
TRIGGER_TYPE_ANR(ระดับ API 36)TRIGGER_TYPE_APP_FULLY_DRAWN(ระดับ API 36)TRIGGER_TYPE_APP_REQUEST_RUNNING_TRACE(ระดับ API 36.1)TRIGGER_TYPE_KILL_FORCE_STOP(ระดับ API 36.1)TRIGGER_TYPE_KILL_RECENTS(ระดับ API 36.1)TRIGGER_TYPE_KILL_TASK_MANAGER(ระดับ API 36.1)TRIGGER_TYPE_KILL_EXCESSIVE_CPU_USAGE(ระดับ API 37)
ทริกเกอร์ที่เริ่มการจับภาพใหม่
ทริกเกอร์ต่อไปนี้ไม่ได้ขึ้นอยู่กับการติดตามในเบื้องหลัง แต่ระบบจะเริ่มบันทึกโปรไฟล์ใหม่เมื่อเกิดเหตุการณ์ขึ้น ดังนี้
TRIGGER_TYPE_COLD_START(ระดับ API 37)TRIGGER_TYPE_OOM(ระดับ API 37)
ทริกเกอร์ที่ลักษณะการจับภาพแตกต่างกันไปตามความผิดปกติ
สำหรับทริกเกอร์ความผิดปกติต่อไปนี้ ประเภทโปรไฟล์ที่บันทึก (การบันทึกภาพรวมของการติดตามเบื้องหลังที่ใช้งานอยู่หรือการเริ่มบันทึกใหม่) และลักษณะการทำงานของการจำกัดอัตราคำขอจะขึ้นอยู่กับความผิดปกติที่ระบบตรวจพบ
TRIGGER_TYPE_ANOMALY(ระดับ API 37)TRIGGER_TYPE_APP_COMPAT(ระดับ API 37)
ตั้งค่าทริกเกอร์
โค้ดต่อไปนี้แสดงวิธีลงทะเบียนสำหรับทริกเกอร์ TRIGGER_TYPE_APP_FULLY_DRAWN และตั้งค่าระยะเวลาพักใช้งาน
Kotlin
@RequiresApi(Build.VERSION_CODES.BAKLAVA) fun recordWithTrigger() { val profilingManager = applicationContext.getSystemService(ProfilingManager::class.java) ?: return val triggers = ArrayList<ProfilingTrigger>() val triggerBuilder = ProfilingTrigger.Builder(ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN) .setRateLimitingPeriodHours(1) triggers.add(triggerBuilder.build()) val callbackExecutor: Executor = Executors.newSingleThreadExecutor() val resultCallback = Consumer<ProfilingResult> { profilingResult -> if (profilingResult.errorCode == ProfilingResult.ERROR_NONE) { Log.d( "ProfileTest", "Received profiling result file=" + profilingResult.resultFilePath ) setupProfileUploadWorker(profilingResult.resultFilePath) } else { Log.e( "ProfileTest", "Profiling failed errorcode=" + profilingResult.errorCode + " errormsg=" + profilingResult.errorMessage ) } } profilingManager.registerForAllProfilingResults(callbackExecutor, resultCallback) profilingManager.addProfilingTriggers(triggers) Choreographer.getInstance().postFrameCallback { frameTimeNanos -> // This will cause the TRIGGER_TYPE_APP_FULLY_DRAWN to be emitted. reportFullyDrawn() } }
Java
public void recordWithTrigger() { ProfilingManager profilingManager = getApplicationContext().getSystemService( ProfilingManager.class); List<ProfilingTrigger> triggers = new ArrayList<>(); ProfilingTrigger.Builder triggerBuilder = new ProfilingTrigger.Builder( ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN); triggerBuilder.setRateLimitingPeriodHours(1); triggers.add(triggerBuilder.build()); Executor callbackExecutor = Executors.newSingleThreadExecutor(); Consumer<ProfilingResult> resultCallback = new Consumer<ProfilingResult>() { @Override public void accept(ProfilingResult profilingResult) { if (profilingResult.getErrorCode() == ProfilingResult.ERROR_NONE) { Log.d( "ProfileTest", "Received profiling result file=" + profilingResult.getResultFilePath()); setupProfileUploadWorker(profilingResult.getResultFilePath()); } else { Log.e( "ProfileTest", "Profiling failed errorcode=" + profilingResult.getErrorCode() + " errormsg=" + profilingResult.getErrorMessage()); } } }; profilingManager.registerForAllProfilingResults(callbackExecutor, resultCallback); profilingManager.addProfilingTriggers(triggers); Choreographer.getInstance().postFrameCallback((f) -> { // This will cause the TRIGGER_TYPE_APP_FULLY_DRAWN to be emitted. reportFullyDrawn(); }); }
โค้ดจะทำตามขั้นตอนต่อไปนี้
- รับผู้จัดการ: ดึงข้อมูลบริการ
ProfilingManager - กำหนดทริกเกอร์: สร้าง
ProfilingTriggerสำหรับTRIGGER_TYPE_APP_FULLY_DRAWNเหตุการณ์นี้จะเกิดขึ้นเมื่อแอปเรียกใช้reportFullyDrawn()ระหว่างการเริ่มระบบใหม่ทั้งหมดที่เริ่มต้นโดยกิจกรรมเพื่อรายงานว่าแอปเริ่มต้นระบบเสร็จแล้วและโต้ตอบได้ - ตั้งค่าระยะเวลาพักใช้งานทริกเกอร์: ใช้ระยะเวลาพักใช้งาน 1 ชั่วโมงกับทริกเกอร์นี้โดยเฉพาะโดยใช้
setRateLimitingPeriodHoursนอกเหนือจากตัวจำกัดอัตราของระบบ ระยะเวลาพักนี้จะป้องกันไม่ให้แอปบันทึกโปรไฟล์การเริ่มต้นทำงานมากกว่า 1 รายการต่อชั่วโมง - ลงทะเบียน Listener: เรียก
registerForAllProfilingResultsเพื่อกำหนด Callback ที่จัดการผลลัพธ์ Callback นี้จะได้รับเส้นทางของโปรไฟล์ที่บันทึกไว้ผ่านgetResultFilePath() - เพิ่มทริกเกอร์: ลงทะเบียนรายการทริกเกอร์กับ
ProfilingManagerโดยใช้addProfilingTriggers - เริ่มเหตุการณ์: โพสต์ Callback เฟรม
Choreographerที่เรียกreportFullyDrawn()เมื่อวาดเฟรมถัดไปแล้ว ซึ่งจะส่งเหตุการณ์TRIGGER_TYPE_APP_FULLY_DRAWNไปยังระบบเพื่อทริกเกอร์การรวบรวมโปรไฟล์ โดยสมมติว่ามีการติดตามเบื้องหลังของระบบอยู่และมีโควต้าการจำกัดอัตราคำขอ เพื่อให้ทริกเกอร์นี้เกิดขึ้น โปรดตรวจสอบว่าแอปเรียกใช้reportFullyDrawn()
วงจรของทริกเกอร์
ทริกเกอร์ที่ลงทะเบียนจะยังคงอยู่แม้จะรีสตาร์ทแอปและรีบูตระบบ คุณไม่จำเป็นต้องลงทะเบียนทริกเกอร์อีกครั้งทุกครั้งที่แอปเริ่มทำงานหรือหลังจากรีบูตระบบ เมื่อลงทะเบียนทริกเกอร์โดยใช้ addProfilingTriggers แล้ว ระบบจะเก็บรักษาทริกเกอร์เหล่านั้นไว้ให้แอปโดยอัตโนมัติจนกว่าคุณจะนำออกหรือถอนการติดตั้งแอป
กรณีที่ควรยกเลิกการลงทะเบียนทริกเกอร์
เนื่องจากทริกเกอร์ที่ลงทะเบียนจะยังคงอยู่แม้จะรีสตาร์ทแอปและรีบูตระบบ
การข้าม addProfilingTriggers ในการเปิดแอปครั้งต่อๆ ไปจึงไม่หยุดทริกเกอร์ที่ลงทะเบียนไว้ในเซสชันก่อนหน้า หากต้องการหยุดบันทึกโปรไฟล์สำหรับเหตุการณ์ที่เฉพาะเจาะจงหรือเหตุการณ์ทั้งหมด ให้เรียกใช้ removeProfilingTriggersByType หรือ clearProfilingTriggers อย่างชัดแจ้ง
ยกเลิกการลงทะเบียนทริกเกอร์ในสถานการณ์ต่อไปนี้
- การกำหนดเป้าหมายกลุ่มประชากรตามรุ่นหรือการย้อนกลับแฟล็กระยะไกล: คุณอาจสร้างโปรไฟล์กลุ่มในกลุ่มประชากรตามรุ่นโดยใช้แฟล็กการกำหนดค่าระยะไกลหรือการทดสอบ A/B เมื่อใดก็ตามที่อุปกรณ์ออกจากกลุ่มประชากรตามรุ่นที่สุ่มตัวอย่างหรือมีการปิดแฟล็ก ให้ยกเลิกการลงทะเบียนทริกเกอร์
- การสิ้นสุดแคมเปญการแก้ไขข้อบกพร่อง: เมื่อรวบรวมโปรไฟล์ได้มากพอที่จะวินิจฉัยปัญหาหรือส่งการแก้ไขแล้ว ให้นำทริกเกอร์ออก การยกเลิกการลงทะเบียนทริกเกอร์ที่ไม่จำเป็นจะช่วยประหยัดพื้นที่เก็บข้อมูลของอุปกรณ์และโควต้าตัวจำกัดอัตราของแอปสำหรับคำขอการทำโปรไฟล์อื่นๆ
วงจรของ Callback
โปรไฟล์ที่อิงตามทริกเกอร์จะส่งผลลัพธ์ไปยัง Listener ส่วนกลางที่ลงทะเบียนกับ registerForAllProfilingResults เท่านั้น ซึ่งต่างจากคำขอการทำโปรไฟล์โดยตรง ใน Callback ให้เรียกใช้ ProfilingResult.getTriggerType เพื่อระบุทริกเกอร์ที่เริ่มการบันทึก และเรียกใช้ ProfilingResult.getResultFilePath เพื่อค้นหาไฟล์โปรไฟล์ที่บันทึกไว้ ดูรายละเอียดเกี่ยวกับชื่อไฟล์โปรไฟล์ทริกเกอร์ การดึงไฟล์ด้วย adb และการอัปโหลดผลลัพธ์ในเบื้องหลังได้ที่หัวข้อเรียกข้อมูลและอัปโหลดโปรไฟล์
แม้ว่าทริกเกอร์จะยังคงอยู่ตลอดการรีสตาร์ทแอปและการรีบูตระบบ แต่ Callback ของผลลัพธ์จะไม่คงอยู่ ลงทะเบียน Callback ใน Executor เบื้องหลังหลังจากที่แอปเริ่มต้นเสร็จสมบูรณ์ (เพื่อไม่ให้ Cold Start ช้าลง) เพื่อรับโปรไฟล์ในทั้ง 2 สถานการณ์ต่อไปนี้
- ขณะที่แอปทำงานอยู่: สำหรับทริกเกอร์ที่ไม่หยุดหรือทำให้แอปขัดข้อง ระบบจะเรียกใช้ Callback ใน
Executorที่ระบุเมื่อทริกเกอร์เกิดขึ้นและบันทึกโปรไฟล์ไว้ หลังจากสิ้นสุดการทำงานของแอป: สำหรับทริกเกอร์ที่มักเกี่ยวข้องหรือเกิดขึ้นก่อนการสิ้นสุดการทำงานของแอป (เช่น
TRIGGER_TYPE_ANR,TRIGGER_TYPE_OOMหรือTRIGGER_TYPE_KILL_RECENTS) หรือหากแอปหยุดทำงานก่อนที่การบันทึกจะเสร็จสิ้น ระบบจะบันทึกโปรไฟล์ในเบื้องหลังและจัดคิวผลลัพธ์ไว้สำหรับการรีสตาร์ทแอปและการรีบูตระบบ ระบบจะเก็บผลลัพธ์ที่อยู่ในคิวซึ่งยังไม่ได้ส่งไว้เป็นเวลาสูงสุด 7 วัน และจะส่งผลลัพธ์ดังกล่าวในครั้งถัดไปที่แอปทำงานและเรียกใช้registerForAllProfilingResults
วิธีการทำงานของการติดตามในเบื้องหลัง
ระบบจะไม่เรียกใช้การติดตามในเบื้องหลังอย่างต่อเนื่องเพื่อสร้างสมดุลระหว่างค่าใช้จ่ายในการสร้างโปรไฟล์กับการบันทึกอาร์ติแฟกต์ที่เป็นประโยชน์ แต่จะสุ่มตัวอย่างเป็นระยะๆ โดยเริ่มการติดตามเบื้องหลังตามช่วงเวลาแบบสุ่ม
การติดตามเบื้องหลังที่ระบบเรียกให้แสดงจะใช้บัฟเฟอร์แบบวงแหวนที่มีขนาดสูงสุดตามที่ระบบกำหนด เมื่อบัฟเฟอร์เต็ม ข้อมูลการติดตามใหม่จะเขียนทับข้อมูลที่เก่าที่สุด ดังที่แสดงในรูปที่ 1 เมื่อทริกเกอร์ที่ลงทะเบียนเริ่มทำงานขณะที่การติดตามเบื้องหลังทำงานอยู่และโควต้าตัวจำกัดอัตราคำขอพร้อมใช้งาน ระบบจะบันทึกสแนปชอตบัฟเฟอร์แบบวงแหวนที่มีกิจกรรมล่าสุดซึ่งนำไปสู่ทริกเกอร์
ตั้งค่าระยะเวลาพักเฉพาะทริกเกอร์
ทริกเกอร์ความถี่สูงอาจใช้โควต้าการจำกัดอัตราคำขอของแอปอย่างรวดเร็ว
หากต้องการป้องกันไม่ให้ทริกเกอร์ประเภทเดียวใช้โควต้าจนหมด ให้ใช้ setRateLimitingPeriodHours เพื่อเพิ่มระยะเวลาพักใช้งานขั้นต่ำต่อทริกเกอร์นอกเหนือจากตัวจำกัดอัตราของระบบ
แก้ไขข้อบกพร่องของทริกเกอร์ในเครื่อง
เนื่องจากการติดตามในเบื้องหลังจะทำงานตามช่วงเวลาแบบสุ่มในเวอร์ชันที่ใช้งานจริง การทดสอบทริกเกอร์ในเครื่องจึงต้องบังคับให้มีการติดตามในเบื้องหลังอย่างต่อเนื่องและข้ามตัวจำกัดอัตราคำขอระดับระบบสำหรับแพ็กเกจของคุณ (ระยะเวลาพักการใช้งานต่อทริกเกอร์ที่ตั้งค่าด้วย setRateLimitingPeriodHours จะยังคงมีผล และ rate_limiter.disabled จะมีผลกับการเรียกใช้ requestProfiling ที่ขับเคลื่อนโดยแอปเท่านั้น)
ดูคำสั่ง adb เพื่อบังคับให้มีการติดตามในเบื้องหลัง จำลองทริกเกอร์จากบรรทัดคำสั่ง และตรวจสอบสถานะบริการได้ที่บังคับให้เปิดใช้การติดตามในเบื้องหลังสำหรับการทดสอบทริกเกอร์ในคำสั่งแก้ไขข้อบกพร่องสำหรับการสร้างโปรไฟล์ในเครื่อง