এই বিভাগে একটি উদাহরণ ট্রেস সহ ProfilingManager ব্যবহার করে কীভাবে একটি অ্যাপ্লিকেশন নট রেসপন্ডিং (ANR) ডিবাগ করতে হয় তা দেখানো হয়েছে।
ANR সংগ্রহ করতে অ্যাপটি সেট আপ করুন
আপনার অ্যাপে একটি ANR ট্রিগার সেট আপ করে শুরু করুন:
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); }
ANR ট্রেস ক্যাপচার ও আপলোড করার পর, Perfetto UI- তে সেটি খুলুন।
চিহ্নটি বিশ্লেষণ করুন
যেহেতু ANR ট্রেসটি চালু করেছিল, আপনি বুঝতে পারবেন যে আপনার অ্যাপের প্রধান থ্রেডে সিস্টেম সাড়া না দেওয়ার বিষয়টি শনাক্ত করার সাথে সাথেই ট্রেসটি শেষ হয়েছে। চিত্র ১-এ দেখানো হয়েছে কীভাবে আপনার অ্যাপের প্রধান থ্রেডে যেতে হয়, যা UI-এর মধ্যে সেই অনুযায়ী ট্যাগ করা থাকে।

ট্রেসের শেষ অংশটি ANR-এর টাইমস্ট্যাম্পের সাথে মিলে যায়, যেমনটি চিত্র ২-এ দেখানো হয়েছে।

ট্রেসটি আরও দেখায় যে ANR ঘটার সময় অ্যাপটি কোন অপারেশনগুলো চালাচ্ছিল। বিশেষ করে, অ্যাপটি handleNetworkResponse ট্রেস স্লাইসে থাকা কোডটি রান করেছিল। এই স্লাইসটি MyApp:SubmitButton স্লাইসের ভিতরে ছিল। এটি ১.৪৮ সেকেন্ড সিপিইউ সময় ব্যবহার করেছিল (চিত্র ৩)।

ডিবাগিংয়ের জন্য যদি আপনি শুধুমাত্র ANR-এর মুহূর্তের স্ট্যাক ট্রেসের উপর নির্ভর করেন, তাহলে আপনি ভুলবশত ANR-এর সম্পূর্ণ কারণ হিসেবে handleNetworkResponse ট্রেস স্লাইসের মধ্যে চলমান সেই কোডটিকে দায়ী করতে পারেন, যা প্রোফাইল রেকর্ডিং শেষ হওয়ার পরেও শেষ হয়নি। তবে, ১.৪৮ সেকেন্ড নিজে থেকে একটি ANR ট্রিগার করার জন্য যথেষ্ট নয়, যদিও এটি একটি ব্যয়বহুল অপারেশন। এই মেথডটির আগে কী মেইন থ্রেডকে ব্লক করেছিল তা বোঝার জন্য আপনাকে আরও আগের ঘটনাগুলো খতিয়ে দেখতে হবে।
ANR-এর কারণ খোঁজার জন্য একটি সূচনা বিন্দু পেতে, আমরা UI থ্রেড দ্বারা জেনারেট করা শেষ ফ্রেমটি দেখা শুরু করি, যা Choreographer#doFrame 551275 স্লাইসের সাথে সঙ্গতিপূর্ণ, এবং MyApp:SubmitButton স্লাইসটি শুরু হওয়ার আগে বিলম্বের কোনো বড় উৎস নেই, যেটি ANR-এ শেষ হয়েছিল (চিত্র 4)।

বাধাটি বোঝার জন্য, সম্পূর্ণ MyApp:SubmitButton স্লাইসটি জুম আউট করে পরীক্ষা করুন। আপনি থ্রেডের অবস্থাগুলোতে একটি গুরুত্বপূর্ণ বিষয় লক্ষ্য করবেন, যেমনটি চিত্র ৪-এ দেখানো হয়েছে: থ্রেডটি তার সময়ের ৭৫% (৬.৭ সেকেন্ড) Sleeping অবস্থায় এবং মাত্র ২৪% সময় Running অবস্থায় কাটিয়েছে।

এটি নির্দেশ করে যে ANR-এর প্রাথমিক কারণ ছিল অপেক্ষা, গণনা নয়। একটি ধরণ খুঁজে পেতে ঘুমের প্রতিটি ঘটনা পরীক্ষা করুন।




প্রথম তিনটি স্লিপ ইন্টারভ্যাল (চিত্র ৬-৮) প্রায় অভিন্ন, প্রতিটি প্রায় ২ সেকেন্ডের। একটি ব্যতিক্রমী চতুর্থ স্লিপ (চিত্র ৯) হলো ০.৭ সেকেন্ডের। কম্পিউটিং পরিবেশে ঠিক ২ সেকেন্ডের একটি সময়কাল খুব কমই কাকতালীয় হয়। এটি দৈবচয়নের মাধ্যমে রিসোর্স দখলের পরিবর্তে একটি প্রোগ্রাম করা টাইমআউটের জোরালো ইঙ্গিত দেয়। শেষ স্লিপটি সম্ভবত এই কারণে হয়েছে যে, থ্রেডটি তার অপেক্ষা শেষ করেছে, কারণ যে অপারেশনটির জন্য এটি অপেক্ষা করছিল তা সফল হয়েছে।
এই অনুমানটি হলো যে, অ্যাপটি একাধিকবার ব্যবহারকারী-নির্ধারিত ২ সেকেন্ডের টাইমআউটে পৌঁছাচ্ছিল এবং অবশেষে সফল হচ্ছিল, যা একটি ANR ট্রিগার করার জন্য যথেষ্ট বিলম্ব ঘটাচ্ছিল।

এটি যাচাই করার জন্য, 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 মেথডটি UI থ্রেডে একটি নেটওয়ার্ক রিকোয়েস্ট এক্সিকিউট করে, যেখানে NETWORK_TIMEOUT_MILLISECS হিসেবে ২০০০ মিলিসেকেন্ড হার্ডকোড করা থাকে। গুরুত্বপূর্ণ বিষয় হলো, এটি একটি while লুপের ভেতরে চলে যা সর্বোচ্চ ১০ বার পর্যন্ত রিট্রাই করে।
এই নির্দিষ্ট ট্রেসটিতে, ব্যবহারকারীর নেটওয়ার্ক সংযোগ সম্ভবত দুর্বল ছিল। প্রথম তিনটি প্রচেষ্টা ব্যর্থ হয়, যার ফলে তিনটি ২-সেকেন্ডের টাইমআউট ঘটে (মোট ৬ সেকেন্ড)। ০.৭ সেকেন্ড পর চতুর্থ প্রচেষ্টাটি সফল হয়, যা কোডটিকে handleNetworkResponse দিকে অগ্রসর হতে দেয়। তবে, সঞ্চিত অপেক্ষার সময় ইতিমধ্যেই ANR ট্রিগার করে ফেলেছিল।
বিভিন্ন ল্যাটেন্সিযুক্ত নেটওয়ার্ক-সম্পর্কিত অপারেশনগুলোকে মেইন থ্রেডে সম্পাদন না করে ব্যাকগ্রাউন্ড থ্রেডে রাখার মাধ্যমে এই ধরনের ANR এড়ানো যায়। এর ফলে দুর্বল কানেক্টিভিটিতেও UI রেসপন্সিভ থাকে, যা এই শ্রেণীর ANR সম্পূর্ণরূপে নির্মূল করে।