परफ़ॉर्मेंस से जुड़ी गड़बड़ी को डीबग करने का उदाहरण: एएनआर

इस सेक्शन में, ProfilingManager का इस्तेमाल करके, ऐप्लिकेशन के काम न करने (एएनआर) की गड़बड़ी को डीबग करने का तरीका बताया गया है. साथ ही, इसमें एक उदाहरण ट्रेस भी दिया गया है.

एएनआर इकट्ठा करने के लिए ऐप्लिकेशन सेट अप करना

अपने ऐप्लिकेशन में एएनआर ट्रिगर सेट अप करके शुरू करें:

public void addANRTrigger() {
  ProfilingManager profilingManager = getApplicationContext().getSystemService(
      ProfilingManager.class);
  List<ProfilingTrigger> triggers = new ArrayList<>();
  ProfilingTrigger.Builder triggerBuilder = new ProfilingTrigger.Builder(
      ProfilingTrigger.TRIGGER_TYPE_ANR);
  triggers.add(triggerBuilder.build());
  Executor mainExecutor = Executors.newSingleThreadExecutor();
  Consumer<ProfilingResult> resultCallback =
      profilingResult -> {
        // Handle uploading trace to your back-end
      };
  profilingManager.registerForAllProfilingResults(mainExecutor, resultCallback);
  profilingManager.addProfilingTriggers(triggers);
}

एएनआर ट्रेस कैप्चर करने और अपलोड करने के बाद, उसे Perfetto यूज़र इंटरफ़ेस (यूआई) में खोलें.

ट्रेस का विश्लेषण करना

एएनआर की वजह से ट्रेस ट्रिगर हुआ. इसलिए, आपको पता है कि ट्रेस तब खत्म हुआ, जब सिस्टम ने आपके ऐप्लिकेशन के मुख्य थ्रेड में जवाब न देने की समस्या का पता लगाया. पहली इमेज में दिखाया गया है कि अपने ऐप्लिकेशन के मुख्य थ्रेड पर कैसे जाएं. इसे यूज़र इंटरफ़ेस (यूआई) में टैग किया गया है.

Perfetto यूज़र इंटरफ़ेस से ऐप्लिकेशन की मुख्य थ्रेड पर नेविगेट करने की सुविधा.
पहली इमेज. ऐप्लिकेशन के मुख्य थ्रेड पर नेविगेट करना.

ट्रेस का आखिरी हिस्सा, एएनआर के टाइमस्टैंप से मैच होता है. जैसा कि दूसरी इमेज में दिखाया गया है.

Perfetto के यूज़र इंटरफ़ेस (यूआई) की इमेज, जिसमें ट्रेस के खत्म होने की जानकारी दिख रही है. इसमें एएनआर ट्रिगर होने की जगह को हाइलाइट किया गया है.
दूसरी इमेज. ANR ट्रिगर करने वाली जगह.

ट्रेस से यह भी पता चलता है कि ANR की गड़बड़ी होने के दौरान, ऐप्लिकेशन कौनसी कार्रवाइयां कर रहा था. खास तौर पर, ऐप्लिकेशन ने handleNetworkResponse ट्रेस स्लाइस में कोड चलाया. यह स्लाइस, MyApp:SubmitButton स्लाइस के अंदर था. इसमें सीपीयू का 1.48 सेकंड का समय लगा (तीसरी इमेज).

Perfetto यूज़र इंटरफ़ेस (यूआई) में, ANR के समय handleNetworkResponse को चलाने में सीपीयू का इस्तेमाल दिखाया गया है.
तीसरी इमेज. ANR के समय एक्ज़ीक्यूट किया गया.

अगर डीबग करने के लिए, ANR के समय सिर्फ़ स्टैक ट्रेस पर भरोसा किया जाता है, तो हो सकता है कि आप ANR की गड़बड़ी को पूरी तरह से handleNetworkResponse ट्रेस स्लाइस में चल रहे कोड की वजह से हुई गड़बड़ी मान लें. ऐसा तब होता है, जब प्रोफ़ाइल रिकॉर्डिंग खत्म होने के समय तक ट्रेस स्लाइस खत्म नहीं हुआ होता है. हालांकि, 1.48 सेकंड में ANR ट्रिगर नहीं होता है. भले ही, यह एक महंगा ऑपरेशन है. इस तरीके से पहले, मुख्य थ्रेड को ब्लॉक करने वाली वजहों को समझने के लिए, आपको और पीछे जाना होगा.

एएनआर की वजह का पता लगाने के लिए, हम यूआई थ्रेड से जनरेट किए गए आखिरी फ़्रेम के बाद से जांच शुरू करते हैं. यह Choreographer#doFrame 551275 स्लाइस से मेल खाता है. साथ ही, Choreographer#doFrame 551275 स्लाइस शुरू होने से पहले, एएनआर की वजह बनने वाले बड़े सोर्स नहीं होते (चौथी इमेज).MyApp:SubmitButton

Perfetto यूज़र इंटरफ़ेस (यूआई) की इमेज, जिसमें एएनआर से पहले यूज़र इंटरफ़ेस (यूआई) थ्रेड से रेंडर किया गया आखिरी फ़्रेम दिखाया गया है.
चौथी इमेज. ANR से पहले जनरेट किया गया ऐप्लिकेशन का आखिरी फ़्रेम.

ब्लॉक किए गए हिस्से को समझने के लिए, ज़ूम आउट करके MyApp:SubmitButton के पूरे स्लाइस की जांच करें. आपको थ्रेड की स्थितियों में एक ज़रूरी जानकारी दिखेगी. जैसा कि चौथे डायग्राम में दिखाया गया है: थ्रेड ने 75% समय (6.7 सेकंड) Sleeping स्थिति में बिताया और सिर्फ़ 24% समय Running स्थिति में बिताया.

Perfetto यूज़र इंटरफ़ेस (यूआई) में, किसी ऑपरेशन के दौरान थ्रेड की स्थितियां दिखाई गई हैं. इसमें 75% स्लीपिंग और 24% रनिंग टाइम को हाइलाइट किया गया है.
पांचवीं इमेज. `MyApp:SubmitButton` ऑपरेशन के दौरान थ्रेड की स्थितियां.

इससे पता चलता है कि ANR की मुख्य वजह इंतज़ार करना था, न कि कंप्यूटेशन. नींद के हर पैटर्न की जांच करके, यह पता लगाएं कि आपको किस समय नींद आती है.

Perfetto यूज़र इंटरफ़ेस (यूआई) में, MyAppSubmitButton ट्रेस स्लाइस में पहला स्लीपिंग इंटरवल दिखाया गया है.
छठी इमेज. `MyAppSubmitButton` में पहली बार स्लीपिंग मोड में जाने का समय.
Perfetto यूज़र इंटरफ़ेस (यूआई) में, MyAppSubmitButton ट्रेस स्लाइस में दूसरा स्लीपिंग इंटरवल दिखाया गया है.
सातवीं इमेज. `MyAppSubmitButton` में दूसरी बार सोने का समय.
Perfetto यूज़र इंटरफ़ेस (यूआई), जिसमें MyAppSubmitButton ट्रेस स्लाइस में तीसरा स्लीपिंग इंटरवल दिखाया गया है.
आठवीं इमेज. `MyAppSubmitButton` में तीसरी बार सोने का समय.
Perfetto यूज़र इंटरफ़ेस (यूआई) में, MyAppSubmitButton ट्रेस स्लाइस के अंदर चौथा स्लीपिंग इंटरवल दिखाया गया है.
नौवीं इमेज. `MyAppSubmitButton` में चौथी बार सोने का समय.

नींद के पहले तीन अंतराल (आंकड़े 6–8) लगभग एक जैसे हैं. हर अंतराल लगभग दो सेकंड का है. चौथी नींद (इमेज 9) में, 0.7 सेकंड का आउटलायर है. कंप्यूटिंग एनवायरमेंट में, ठीक दो सेकंड की अवधि का होना बहुत कम ही होता है. इससे पता चलता है कि टाइम आउट को प्रोग्राम किया गया है. ऐसा रैंडम तरीके से संसाधन के लिए विवाद की वजह से नहीं हुआ है. थ्रेड के इंतज़ार की अवधि खत्म होने की वजह से, आखिरी बार स्लीप मोड चालू हुआ होगा. ऐसा इसलिए हुआ, क्योंकि जिस ऑपरेशन के लिए थ्रेड इंतज़ार कर रहा था वह पूरा हो गया है.

इस हाइपोथेसिस के मुताबिक, ऐप्लिकेशन में उपयोगकर्ता की ओर से तय किया गया 2 सेकंड का टाइमआउट कई बार पूरा हो रहा था. हालांकि, आखिर में यह काम कर रहा था. इस वजह से, एएनआर ट्रिगर होने में काफ़ी समय लग रहा था.

Perfetto यूज़र इंटरफ़ेस (यूआई) में, MyApp:SubmitButton ट्रेस स्लाइस के दौरान हुई देरी की खास जानकारी दिखाई गई है. इसमें दो सेकंड के कई स्लीप इंटरवल दिखाए गए हैं.
दसवीं इमेज. `MyApp:SubmitButton` स्लाइस के दौरान होने वाली देरी की खास जानकारी.

इसकी पुष्टि करने के लिए, MyApp:SubmitButton ट्रेस सेक्शन से जुड़े कोड की जांच करें:

private static final int NETWORK_TIMEOUT_MILLISECS = 2000;
public void setupButtonCallback() {
  findViewById(R.id.submit).setOnClickListener(submitButtonView -> {
    Trace.beginSection("MyApp:SubmitButton");
    onClickSubmit();
    Trace.endSection();
  });
}

public void onClickSubmit() {
  prepareNetworkRequest();

  boolean networkRequestSuccess = false;
  int maxAttempts = 10;
  while (!networkRequestSuccess && maxAttempts > 0) {
    networkRequestSuccess = performNetworkRequest(NETWORK_TIMEOUT_MILLISECS);
    maxAttempts--;
  }

  if (networkRequestSuccess) {
    handleNetworkResponse();
  }
}

boolean performNetworkRequest(int timeoutMiliseconds) {
  // ...
}

  // ...
}

public void handleNetworkResponse() {
  Trace.beginSection("handleNetworkResponse");
  // ...
  Trace.endSection();
}

इस कोड से इस हाइपोथेसिस की पुष्टि होती है. onClickSubmit तरीका, यूज़र इंटरफ़ेस (यूआई) थ्रेड पर नेटवर्क अनुरोध को एक्ज़ीक्यूट करता है. इसमें 2000 मि॰से॰ का NETWORK_TIMEOUT_MILLISECS हार्डकोड किया गया है. खास बात यह है कि यह while लूप के अंदर चलता है, जो ज़्यादा से ज़्यादा 10 बार फिर से कोशिश करता है.

इस ट्रेस में, ऐसा लगता है कि उपयोगकर्ता का नेटवर्क कनेक्शन ठीक नहीं था. शुरुआती तीन कोशिशें पूरी नहीं हो सकीं. इस वजह से, दो सेकंड के तीन टाइमआउट हुए. कुल मिलाकर, छह सेकंड का टाइमआउट हुआ. चौथी बार कोशिश करने पर, 0.7 सेकंड के बाद कोड को handleNetworkResponse पर ले जाया गया. हालांकि, इंतज़ार करने के समय की वजह से पहले ही एएनआर ट्रिगर हो चुका है.

इस तरह की ANR से बचने के लिए, नेटवर्क से जुड़ी उन कार्रवाइयों को बैकग्राउंड थ्रेड में रखें जिनमें अलग-अलग समय लगता है. इसके बजाय, उन्हें मुख्य थ्रेड में न चलाएं. इससे इंटरनेट की स्पीड कम होने पर भी यूज़र इंटरफ़ेस (यूआई) काम करता रहता है. साथ ही, इस तरह के एएनआर पूरी तरह से खत्म हो जाते हैं.