इस सेक्शन में, 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 यूज़र इंटरफ़ेस (यूआई) में खोलें.
ट्रेस का विश्लेषण करना
एएनआर की वजह से ट्रेस ट्रिगर हुआ. इसलिए, आपको पता है कि ट्रेस तब खत्म हुआ, जब सिस्टम ने आपके ऐप्लिकेशन के मुख्य थ्रेड में जवाब न देने की समस्या का पता लगाया. पहली इमेज में दिखाया गया है कि यूज़र इंटरफ़ेस (यूआई) में टैग की गई, ऐप्लिकेशन की मुख्य थ्रेड पर कैसे जाएं.
ट्रेस का आखिरी हिस्सा, एएनआर के टाइमस्टैंप से मेल खाता है. इसे दूसरी इमेज में दिखाया गया है.
ट्रेस से यह भी पता चलता है कि ANR की गड़बड़ी होने के दौरान, ऐप्लिकेशन कौनसी कार्रवाइयां कर रहा था.
खास तौर पर, ऐप्लिकेशन ने handleNetworkResponse ट्रेस स्लाइस में कोड चलाया. यह स्लाइस, MyApp:SubmitButton स्लाइस के अंदर था. इसमें 1.48 सेकंड का सीपीयू समय लगा (तीसरी इमेज).
अगर डीबग करने के लिए, ANR के समय सिर्फ़ स्टैक ट्रेस पर भरोसा किया जाता है, तो हो सकता है कि आप ANR की गड़बड़ी को पूरी तरह से उस कोड की वजह से मान लें जो handleNetworkResponse ट्रेस स्लाइस में चल रहा था. यह स्लाइस, प्रोफ़ाइल रिकॉर्डिंग के खत्म होने तक खत्म नहीं हुआ था. हालांकि, 1.48 सेकंड में ANR ट्रिगर नहीं होता. भले ही, यह एक महंगा ऑपरेशन है. इस तरीके से पहले, मुख्य थ्रेड को ब्लॉक करने वाली वजहों को समझने के लिए, आपको और पीछे के समय में देखना होगा.
एएनआर की वजह का पता लगाने के लिए, हम यूज़र इंटरफ़ेस (यूआई) थ्रेड से जनरेट किए गए आखिरी फ़्रेम के बाद से जांच शुरू करते हैं
. यह
Choreographer#doFrame 551275 स्लाइस से मेल खाता है. साथ ही,
MyApp:SubmitButton स्लाइस शुरू होने से पहले, एएनआर की वजह से होने वाली देरी के बड़े सोर्स नहीं होते हैं (चौथी इमेज).
ब्लॉक किए गए हिस्से को समझने के लिए, ज़ूम आउट करके MyApp:SubmitButton का पूरा स्लाइस देखें. आपको थ्रेड की स्थितियों में एक ज़रूरी जानकारी दिखेगी. जैसा कि चौथे डायग्राम में दिखाया गया है: थ्रेड ने 75% समय (6.7 सेकंड) Sleeping स्थिति में बिताया और सिर्फ़ 24% समय Running स्थिति में बिताया.

इससे पता चलता है कि ANR की मुख्य वजह इंतज़ार करना था, न कि कंप्यूटेशन. नींद के पैटर्न का पता लगाने के लिए, नींद के हर सेशन की जांच करें.
नींद के पहले तीन इंटरवल (आंकड़े 6–8) लगभग एक जैसे हैं. हर इंटरवल करीब दो सेकंड का है. आउटलायर चौथी नींद (आंकड़ा 9) 0.7 सेकंड की है. कंप्यूटिंग एनवायरमेंट में, ठीक दो सेकंड की अवधि का होना बहुत कम ही होता है. इससे पता चलता है कि टाइम आउट को प्रोग्राम किया गया है. ऐसा रैंडम रिसॉर्स कंटेंशन की वजह से नहीं हुआ है. थ्रेड के आखिरी बार स्लीप मोड में जाने की वजह यह हो सकती है कि जिस ऑपरेशन के पूरा होने का इंतज़ार किया जा रहा था वह पूरा हो गया है.
इस हाइपोथेसिस के मुताबिक, ऐप्लिकेशन में उपयोगकर्ता की ओर से तय की गई दो सेकंड की समयसीमा कई बार पूरी नहीं हो रही थी. हालांकि, आखिर में यह पूरी हो जाती थी. इस वजह से, एएनआर ट्रिगर होने में काफ़ी समय लग रहा था.
इसकी पुष्टि करने के लिए, 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) { // ... } void prepareNetworkRequest() { // ... } public void handleNetworkResponse() { Trace.beginSection("handleNetworkResponse"); // ... Trace.endSection(); }
इस कोड से इस हाइपोथेसिस की पुष्टि होती है. onClickSubmit तरीके से, यूज़र इंटरफ़ेस (यूआई) थ्रेड पर नेटवर्क अनुरोध को एक्ज़ीक्यूट किया जाता है. इसमें 2000 मि॰से॰ का NETWORK_TIMEOUT_MILLISECS हार्डकोड किया जाता है.
खास तौर पर, यह while लूप के अंदर चलता है, जो ज़्यादा से ज़्यादा 10 बार कोशिश करता है.
इस ट्रेस में, ऐसा लगता है कि उपयोगकर्ता का नेटवर्क कनेक्शन ठीक नहीं था. शुरुआती तीन कोशिशें पूरी नहीं हो सकीं. इस वजह से, दो सेकंड के तीन टाइमआउट हुए. कुल मिलाकर, छह सेकंड का टाइमआउट हुआ.
चौथी बार कोशिश करने पर, कोड 0.7 सेकंड में पूरा हो गया. इससे कोड को handleNetworkResponse पर जाने की अनुमति मिल गई. हालांकि, इंतज़ार करने के समय की वजह से पहले ही एएनआर ट्रिगर हो चुका है.
इस तरह की ANR से बचने के लिए, नेटवर्क से जुड़ी उन कार्रवाइयों को बैकग्राउंड थ्रेड में रखें जिनमें अलग-अलग समय लगता है. इसके बजाय, उन्हें मुख्य थ्रेड में न चलाएं. इससे खराब कनेक्टिविटी होने पर भी यूज़र इंटरफ़ेस (यूआई) रिस्पॉन्सिव बना रहता है. साथ ही, इस तरह के एएनआर पूरी तरह से खत्म हो जाते हैं.