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