পারফর্ম্যান্স সংক্রান্ত সমস্যা সমাধানের ব্যবহারিক উদাহরণ: ANR

এই বিভাগে দেখানো হয়েছে যে কীভাবে অ্যাপ্লিকেশন উত্তর দিচ্ছে না (ANR) সংক্রান্ত সমস্যা ProfilingManager ডিবাগ করতে হয়, এর সাথে একটি ট্রেস করার উদাহরণও দেওয়া হয়েছে।

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-এর মধ্যে যথাযথভাবে ট্যাগ করা আপনার অ্যাপের মূল থ্রেডে কীভাবে নেভিগেট করতে হয় তা ১ নম্বর ছবিতে দেখানো হয়েছে।

অ্যাপের মূল থ্রেডে Perfetto UI নেভিগেশন।
ছবি ১. অ্যাপের মূল থ্রেডে নেভিগেশন।

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

Perfetto UI-তে ট্রেসের শেষ দেখানো হচ্ছে, ANR ট্রিগার
 লোকেশন হাইলাইট করা হচ্ছে।
ছবি ২. ANR ট্রিগার লোকেশন।

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

ANR-এর সময় handleNetworkResponse
 এক্সিকিউশনের জন্য ব্যবহৃত CPU টাইম Perfetto UI-তে দেখানো হচ্ছে।
ছবি ৩. ANR-এর সময় এক্সিকিউশন।

ডিবাগ করার জন্য ANR-এর সময় আপনি যদি শুধুমাত্র স্ট্যাক ট্রেসের উপর নির্ভর করেন, তাহলে আপনি হয়ত ভুল করে ANR-এর সম্পূর্ণ কারণ হিসেবে সেই কোডকে চিহ্নিত করবেন যেটি handleNetworkResponse ট্রেস স্লাইসের মধ্যে রান করছে এবং প্রোফাইল রেকর্ড করা শেষ হওয়ার সময়ও শেষ হয়নি। তবে, ১.৪৮ সেকেন্ডে নিজে থেকে ANR ট্রিগার করা যায় না, যদিও এটি একটি কঠিন অপারেশন। এই পদ্ধতির আগে কোন জিনিসটি মূল থ্রেডকে ব্লক করেছিল তা বুঝতে আপনাকে আরও আগের সময় দেখতে হবে।

ANR-এর কারণ খোঁজার জন্য একটি স্টার্টিং পয়েন্ট পেতে, আমরা UI থ্রেড দ্বারা জেনারেট করা শেষ ফ্রেমের পরে খোঁজা শুরু করি, যা Choreographer#doFrame 551275 স্লাইসের সাথে সম্পর্কিত এবং ANR-এ শেষ হওয়া MyApp:SubmitButton স্লাইস শুরু হওয়ার আগে বিলম্বের কোনও বড় সোর্স নেই (ছবি ৪)।

ANR হওয়ার আগে UI থ্রেড দ্বারা রেন্ডার করা শেষ ফ্রেম Perfetto UI-তে দেখানো হচ্ছে।
ছবি ৪. ANR হওয়ার আগে তৈরি হওয়া অ্যাপের শেষ ফ্রেম।

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

অপারেশন চলাকালীন থ্রেড স্টেট দেখানো Perfetto UI, ৭৫%
 স্লিপিং এবং ২৪% রানিং টাইম হাইলাইট করছে।
ছবি ৫. `MyApp:SubmitButton` অপারেশনের সময় থ্রেডের স্টেট।

এটি থেকে বোঝা যায় যে ANR-এর মূল কারণ হল অপেক্ষা করা, গণনা করা নয়। প্যাটার্ন খুঁজে পেতে আলাদা আলাদা ঘুমের ঘটনা পরীক্ষা করুন।

Perfetto UI-তে
 MyAppSubmitButton ট্রেস স্লাইসের মধ্যে প্রথম স্লিপিং ইন্টার্ভাল দেখানো হচ্ছে।
ছবি ৬. `MyAppSubmitButton`-এর মধ্যে প্রথমবার স্লিপ টাইম।
Perfetto UI-তে
 MyAppSubmitButton ট্রেস স্লাইসের মধ্যে দ্বিতীয় স্লিপিং ইন্টারভ্যাল দেখানো হচ্ছে।
ছবি ৭. `MyAppSubmitButton`-এর মধ্যে দ্বিতীয়বার স্লিপ টাইম।
Perfetto UI-তে
 MyAppSubmitButton ট্রেস স্লাইসের মধ্যে থার্ড স্লিপিং ইন্টারভ্যাল দেখানো হচ্ছে।
ছবি ৮. `MyAppSubmitButton`-এর মধ্যে তৃতীয়বার স্লিপ টাইম।
Perfetto UI-তে
 MyAppSubmitButton ট্রেস স্লাইসের মধ্যে চতুর্থ স্লিপিং ইন্টার্ভাল দেখানো হচ্ছে।
ছবি ৯. `MyAppSubmitButton`-এর মধ্যে চতুর্থ ঘুমের সময়।

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

এই হাইপোথিসিস হল যে অ্যাপটি একাধিকবার ২ সেকেন্ডের ব্যবহারকারী-সংজ্ঞায়িত টাইম-আউট হিট করছিল এবং অবশেষে সফল হয়েছিল, যার ফলে ANR ট্রিগার করার জন্য যথেষ্ট বিলম্ব হয়েছিল।

Perfetto UI-তে MyApp:SubmitButton
 ট্রেস স্লাইস চলাকালীন হওয়া বিলম্বের সারসংক্ষেপ দেখানো হচ্ছে, যা একাধিক ২-সেকেন্ডের স্লিপ ইন্টারভ্যালের দিকে ইঙ্গিত করছে।
ছবি ১০. `MyApp:SubmitButton` স্লাইস চলাকালীন হওয়া দেরি সম্পর্কিত সারসংক্ষেপ।

এটি যাচাই করতে, MyApp:SubmitButton trace বিভাগের সাথে যুক্ত কোডটি ভালোভাবে দেখুন:

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 সম্পূর্ণভাবে বন্ধ হয়ে যায়।