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