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

इससे पता चलता है कि ANR की मुख्य वजह इंतज़ार करना था, न कि कंप्यूटेशन. नींद के हर पैटर्न की जांच करके, यह पता लगाएं कि आपको किस समय नींद आती है.
नींद के पहले तीन अंतराल (आंकड़े 6–8) लगभग एक जैसे हैं. हर अंतराल लगभग दो सेकंड का है. आउटलायर के तौर पर चौथी नींद (आंकड़ा 9) 0.7 सेकंड की है. कंप्यूटिंग एनवायरमेंट में, ठीक दो सेकंड की अवधि का होना बहुत कम ही होता है. इससे पता चलता है कि टाइम आउट को प्रोग्राम किया गया है. ऐसा रैंडम तरीके से संसाधन के लिए विवाद की वजह से नहीं हुआ है. थ्रेड के इंतज़ार की अवधि खत्म होने की वजह से, आखिरी बार स्लीप मोड चालू हुआ होगा. ऐसा इसलिए हुआ, क्योंकि जिस ऑपरेशन के लिए थ्रेड इंतज़ार कर रहा था वह पूरा हो गया है.
इस हाइपोथेसिस के मुताबिक, ऐप्लिकेशन में उपयोगकर्ता की ओर से तय किया गया 2 सेकंड का टाइमआउट कई बार पूरा हो रहा था. हालांकि, आखिर में यह काम कर रहा था. इस वजह से, एएनआर ट्रिगर होने में काफ़ी समय लग रहा था.
इसकी पुष्टि करने के लिए, 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 से बचने के लिए, नेटवर्क से जुड़ी उन कार्रवाइयों को बैकग्राउंड थ्रेड में रखें जिनमें अलग-अलग समय लगता है. इसके बजाय, उन्हें मुख्य थ्रेड में न चलाएं. इससे इंटरनेट की स्पीड कम होने पर भी यूज़र इंटरफ़ेस (यूआई) काम करता रहता है. साथ ही, इस तरह के एएनआर पूरी तरह से खत्म हो जाते हैं.