การทำโปรไฟล์ตามทริกเกอร์

ProfilingManager รองรับการบันทึกโปรไฟล์ตามทริกเกอร์ของระบบ โดยระบบจะจัดการกระบวนการบันทึกและส่งโปรไฟล์ที่ได้ไปยังแอป

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

บันทึกข้อมูลย้อนหลัง

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

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

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

เหตุผลที่ควรใช้การบันทึกตามทริกเกอร์

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

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

ตั้งค่าทริกเกอร์

โค้ดต่อไปนี้แสดงวิธีลงทะเบียนทริกเกอร์ TRIGGER_TYPE_APP_FULLY_DRAWN และใช้การจำกัดอัตราคำขอ

Kotlin

fun recordWithTrigger() {
    val profilingManager = applicationContext.getSystemService(ProfilingManager::class.java)

    val triggers = ArrayList<ProfilingTrigger>()

    val triggerBuilder = ProfilingTrigger.Builder(ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN)
        .setRateLimitingPeriodHours(1)

    triggers.add(triggerBuilder.build())

    val mainExecutor: 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(mainExecutor, resultCallback)
    profilingManager.addProfilingTriggers(triggers)

}

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 mainExecutor = 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(mainExecutor, resultCallback);
  profilingManager.addProfilingTriggers(triggers);

}

โค้ดจะดำเนินการตามขั้นตอนต่อไปนี้

  1. รับผู้จัดการ: ดึงข้อมูลบริการ ProfilingManager
  2. กำหนดทริกเกอร์: สร้าง ProfilingTrigger สำหรับ TRIGGER_TYPE_APP_FULLY_DRAWN เหตุการณ์นี้จะเกิดขึ้นเมื่อแอปรายงานว่าได้เริ่มต้นเสร็จสมบูรณ์แล้วและพร้อมให้โต้ตอบ
  3. ตั้งค่าการจำกัดอัตรา: ใช้การจำกัดอัตรา 1 ชั่วโมงกับทริกเกอร์เฉพาะนี้ (setRateLimitingPeriodHours(1)) ซึ่งจะป้องกันไม่ให้แอปบันทึกโปรไฟล์การเริ่มต้นมากกว่า 1 รายการต่อชั่วโมง
  4. ลงทะเบียน Listener: เรียก registerForAllProfilingResults เพื่อกำหนด การเรียกกลับที่จะจัดการผลลัพธ์ การเรียกกลับนี้จะได้รับเส้นทางของโปรไฟล์ที่บันทึกไว้ผ่าน getResultFilePath()
  5. เพิ่มทริกเกอร์: ลงทะเบียนรายการทริกเกอร์กับ ProfilingManager โดยใช้ addProfilingTriggers
  6. เริ่มเหตุการณ์: เรียก reportFullyDrawn() ซึ่งจะปล่อยเหตุการณ์ TRIGGER_TYPE_APP_FULLY_DRAWN ไปยังระบบเพื่อเริ่มการรวบรวมโปรไฟล์ โดยสมมติว่าการติดตามในเบื้องหลังของระบบกำลังทำงานอยู่และมีโควต้าการจำกัดอัตราคำขอ ขั้นตอนนี้เป็นขั้นตอนที่ไม่บังคับและแสดงโฟลว์ตั้งแต่ต้นจนจบ เนื่องจากแอปต้องเรียก reportFullyDrawn() สำหรับทริกเกอร์นี้

ดึงข้อมูลการติดตาม

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

profile_trigger_<profile_type_code>_<datetime>.<profile-type-name>

คุณสามารถดึงข้อมูลไฟล์โดยใช้ ADB ตัวอย่างเช่น หากต้องการดึงข้อมูลการติดตามของระบบที่บันทึก ด้วย โค้ดตัวอย่าง โดยใช้ ADB คำสั่งอาจมีลักษณะดังนี้

adb pull /data/user/0/com.example.sampleapp/files/profiling/profile_trigger_1_2025-05-06-14-12-40.perfetto-trace

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

วิธีการทำงานของการติดตามในเบื้องหลัง

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

เมื่อบันทึกโปรไฟล์แล้ว ระบบจะแจ้งให้แอปทราบโดยใช้การเรียกกลับที่ระบุไว้ใน registerForAllProfilingResults การเรียกกลับนี้จะระบุเส้นทางไปยัง โปรไฟล์ที่บันทึกไว้ ซึ่งเข้าถึงได้โดยการเรียก ProfilingResult#getResultFilePath()

แผนภาพแสดงวิธีการทำงานของสแนปชอตการติดตามในเบื้องหลัง โดยมีบัฟเฟอร์แบบวงแหวนที่บันทึกข้อมูลก่อนเหตุการณ์ทริกเกอร์
รูปที่ 1: วิธีการทำงานของสแนปช็อตการติดตามในเบื้องหลัง

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

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

ใช้การจำกัดอัตราคำขอเฉพาะทริกเกอร์

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

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

แก้ไขข้อบกพร่องของทริกเกอร์ในเครื่อง

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

adb shell device_config put profiling_testing system_triggered_profiling.testing_package_name <com.example.myapp>

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

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