কমন ইউনিটি গেম ANR

ইউনিটি ANR বিভিন্ন কারণে ঘটে। বেশিরভাগ সাধারণ ANRগুলি Android এবং Unity উপাদানগুলির অপব্যবহার এবং তাদের ভুল যোগাযোগের কারণে ঘটে।

ওয়েবভিউ

WebView হল একটি Android ক্লাস যা ওয়েব পৃষ্ঠাগুলি প্রদর্শন করে। থার্ড-পার্টি SDKs (যেমন বিজ্ঞাপন) UnityPlayerActivity ছাড়া অন্য কার্যকলাপে গতিশীল ওয়েব সামগ্রী প্রদর্শন করতে WebView ব্যবহার করে। ANR হয় যখন তৃতীয় পক্ষের SDKs WebView অপব্যবহার করে।

স্ট্যাক ট্রেস

ANR এর কারণ বোঝার জন্য স্ট্যাক ট্রেস হল আপনার প্রথম উপায়।

/data/app/~~p-0ksfCD6bF6Sdq6kpVePg==/com.google.android.webview-5YQZOqKbbqp-uoLY6WYnTw==/base.apk!libmonochrome.so
  at J.N.Mhc_M_H$ (Native method)
  at org.chromium.components.viz.service.frame_sinks.ExternalBeginFrameSourceAndroid.doFrame (chromium-TrichromeWebViewGoogle.aab-stable-579013831:60)
  at android.view.Choreographer$CallbackRecord.run (Choreographer.java:1054)
  at android.view.Choreographer.doCallbacks (Choreographer.java:878)
  at android.view.Choreographer.doFrame (Choreographer.java:807)
  at android.view.Choreographer$FrameDisplayEventReceiver.run (Choreographer.java:1041)
  at android.os.Handler.handleCallback (Handler.java:938)
  at android.os.Handler.dispatchMessage (Handler.java:99)
  at android.os.Looper.loop (Looper.java:223)
  at android.app.ActivityThread.main (ActivityThread.java:7721)
  at java.lang.reflect.Method.invoke (Native method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:592)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:952)

চিত্র 1. Futex অপেক্ষার কারণে ANR স্ট্যাক ট্রেস।

কারণ

এখন পর্যন্ত, এই সমস্যার মূল কারণ অস্পষ্ট। কিছু সম্ভাব্য কারণ অন্তর্ভুক্ত হতে পারে:

  • খারাপ বিজ্ঞাপন বাস্তবায়ন।
  • WebView -এর একটি পুরানো সংস্করণ যেহেতু ব্যবহারকারী স্বয়ংক্রিয়ভাবে অ্যাপটি আপডেট না করা বেছে নিয়েছেন।
  • সিস্টেম সম্পদের উচ্চ ব্যবহার (CPU, GPU, ইত্যাদি), যার জন্য অনেক প্রোফাইলিং প্রয়োজন হতে পারে।
  • Shader সংকলন ক্র্যাশ, যা নির্দেশ করতে পারে যে বিষয়বস্তুতে একটি বেমানান শেডার আছে বা ব্যবহারকারীর একটি পুরানো WebView সংস্করণ ইনস্টল করা আছে।

সমাধান

  • কোন ধরনের বিষয়বস্তু WebView প্রধান থ্রেডকে ব্লক করে দিচ্ছে তা সংকুচিত করতে, যখনই একটি ওয়েব পৃষ্ঠা লোড, প্রদর্শিত বা বন্ধ করা হয় তখনই আপনার গেমটিতে লগ যোগ করুন।
    • আপনি Backtrace বা Crashlytics রিপোর্টিং পরিষেবা ব্যবহার করতে পারেন।
    • তারপর, ডেটা বিশ্লেষণ করার পরে এবং সমস্যাটি খুঁজে বের করার পরে, আপত্তিকর বিজ্ঞাপন প্রদানকারীদের অক্ষম করার চেষ্টা করুন।
    • সমস্যাটি মেমরি সম্পর্কিত নয় তা নিশ্চিত করতে মেমরি লগ অন্তর্ভুক্ত করুন।
  • Google Play থেকে WebView আপডেট করার জন্য ব্যবহারকারীকে সতর্ক করুন। Android 5.0 (API স্তর 21) এবং উচ্চতর থেকে, WebView একটি APK-এ সরানো হয়েছে। অতএব, এটি অ্যান্ড্রয়েড প্ল্যাটফর্ম থেকে আলাদাভাবে আপডেট করা যেতে পারে। একটি ডিভাইসে WebView -এর কোন সংস্করণ ব্যবহার করা হচ্ছে তা দেখতে, সেটিংস > অ্যাপস > অ্যান্ড্রয়েড সিস্টেম ওয়েবভিউ- এ যান এবং পৃষ্ঠার নীচের সংস্করণটি দেখুন।
ওয়েবভিউ ভার্সন দেখানো অ্যাপের তথ্যের স্ক্রীন।
চিত্র 1. WebView সংস্করণ পরীক্ষা করুন।

ঐক্য বিরতি

যখন UnityPlayerActivity একটি onPause() কল পায়, তখন নিম্নলিখিত ক্রিয়াকলাপগুলি শুরু হয়:

  1. UnityPlayerActivity ইউনিটি রানটাইম ইঞ্জিনকে সূচিত করে যে কার্যকলাপটি বিরাম দেওয়া হয়েছে।
  2. Unity প্রতিটি MonoBehaviour কে কল করে যা OnApplicationPause ইভেন্ট বাস্তবায়ন করে।
  3. ইউনিটি তার উপাদান এবং মডিউলগুলিকে থামিয়ে দেয়, যেমন সাউন্ড প্লেব্যাক, রেন্ডারিং, গেম লুপ এবং অ্যানিমেশন।
  4. Unity Android Player (ইউএপি) এবং ইঞ্জিন উভয়ই সিঙ্ক্রোনাইজ করা হয়েছে তা নিশ্চিত করতে, ইউএপি ইঞ্জিন বন্ধ হওয়ার জন্য 4 সেকেন্ড অপেক্ষা করে।
  5. যদি সেই অপারেশনটি 5 সেকেন্ডের বেশি সময় নেয়, সিস্টেমটি একটি ANR ট্রিগার করে।

স্ট্যাক ট্রেস

"main" tid=1 Timed Waiting
jdk.internal.misc.Unsafe.park (Native method)
java.util.concurrent.locks.LockSupport.parkNanos (LockSupport.java:234)
java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedNanos (AbstractQueuedSynchronizer.java:1079)
java.util.concurrent.locks.AbstractQueuedSynchronizer.tryAcquireSharedNanos (AbstractQueuedSynchronizer.java:1369)
java.util.concurrent.Semaphore.tryAcquire (Semaphore.java:415)
com.unity3d.player.UnityPlayer.pauseUnity (UnityPlayer.java:833)
com.unity3d.player.UnityPlayer.pause (UnityPlayer.java:796)
com.unity3d.player.UnityPlayerActivity.onPause (UnityPlayerActivity.java:117)
android.app.Activity.performPause (Activity.java:8517)
android.app.Instrumentation.callActivityOnPause (Instrumentation.java:1618)
android.app.ActivityThread.performPauseActivityIfNeeded (ActivityThread.java:5061)
android.app.ActivityThread.performPauseActivity (ActivityThread.java:5022)
android.app.ActivityThread.handlePauseActivity (ActivityThread.java:4974)
android.app.servertransaction.PauseActivityItem.execute (PauseActivityItem.java:48)
android.app.servertransaction.ActivityTransactionItem.execute (ActivityTransactionItem.java:45)
android.app.servertransaction.TransactionExecutor.executeLifecycleState (TransactionExecutor.java:179)
android.app.servertransaction.TransactionExecutor.execute (TransactionExecutor.java:97)
android.app.ActivityThread$H.handleMessage (ActivityThread.java:2303)
android.os.Handler.dispatchMessage (Handler.java:106)
android.os.Looper.loopOnce (Looper.java:201)
android.os.Looper.loop (Looper.java:288)
android.app.ActivityThread.main (ActivityThread.java:7884)
java.lang.reflect.Method.invoke (Native method)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:548)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:936)

চিত্র 3. একটি সেমাফোর দ্বারা সৃষ্ট ANR যা কখনই প্রকাশিত হয় না।

সমাধান

নিশ্চিত করুন যে আপনার C# গেম কোডটি একটি বিরতি বা পুনরায় শুরু করার ইভেন্টের সময় এক্সিকিউশন শেষ করতে খুব বেশি সময় নেয় না।

  • আপনার গেমটি প্রোফাইল করুন এবং OnApplicationPause একটি ব্যয়বহুল অপারেশন কিনা তা পরীক্ষা করুন। আপনি একটি Stopwatch ব্যবহার করতে পারেন।
  • I/O অপারেশন বা সিঙ্ক্রোনাস নেটওয়ার্ক অনুরোধগুলি এড়িয়ে চলুন।
  • Task ব্যবহার করে অপারেশনগুলিকে অন্য Thread সরান। ইউনিটি 2023.1 C# async এবং await কীওয়ার্ড ব্যবহার করে একটি সরলীকৃত অ্যাসিঙ্ক্রোনাস প্রোগ্রামিং মডেল সমর্থন করে।

UnitySendMessage ব্লক করা হয়েছে

জাভা ইউনিটি প্লাগইন এবং SDKs JNI ব্যবহার করে C# গেম লেয়ারে ডেটা পাঠায়। যাইহোক, মিউটেক্সের মতো একটি নেটিভ সিঙ্ক্রোনাইজেশন রুটিনের কারণে এই যোগাযোগটি মূল থ্রেডকে ব্লক করতে পারে, লক কনটেন্টেশনের কারণে ANR হতে পারে।

স্ট্যাক ট্রেস

চিত্র 4-এ ANR একটি জাভা প্লাগইন দ্বারা ডাকা C# কোডে একটি দীর্ঘ অপারেশনের কারণে ঘটেছে। ইউনিটি ইঞ্জিন সঠিক নির্বাহ নিশ্চিত করতে একটি নন-প্রোরিটি ইনহেরিটেন্স মিউটেক্স ব্যবহার করে।

libc.so NonPI::MutexLockWithTimeout(pthread_mutex_internal_t*, bool, timespec const*) + 604
com.unity3d.player.UnityPlayer.nativeUnitySendMessage (Native method)
com.unity3d.player.UnityPlayer.UnitySendMessage (UnityPlayer.java:665)

চিত্র 4. ANR একটি লক বিরোধের কারণে সৃষ্ট।

কারণ

সমস্যা হল যে অ্যাপ্লিকেশনটি পুনরায় চালু করার সময় বেশ কয়েকটি বার্তা পাঠানো হচ্ছে। বার্তাগুলি সারিবদ্ধ থাকে কারণ গেমটি ব্যাকগ্রাউন্ডে থাকাকালীন সেগুলি পাঠানো যায় না৷ অ্যাপটি পুনরায় চালু হলে বার্তাগুলি একই সাথে পাঠানো হয়।

একটি বিরতি সময়কালে, আপনি সাধারণত সার্ভারে আপনার গেমের তথ্য সংরক্ষণ করেন; উদাহরণস্বরূপ, আপনি গেমটিতে একজন খেলোয়াড়ের অবস্থান রেকর্ড করেন যাতে খেলা পুনরায় শুরু হলে খেলোয়াড় একই স্থানে ফিরে যেতে পারে।

এই কাজের চাপ, অন্য তৃতীয় পক্ষের কোডের সাথে মিলিত হয়ে তার নিজস্ব কাজের চাপ তৈরি করে, ডিভাইসের সংস্থান, বিশেষ করে প্রধান থ্রেডকে ওভারলোড করতে পারে। মূল থ্রেডটি একটি অ্যাপের ইউজার ইন্টারফেস চালায় এবং এটি প্রায়শই ANR-এর প্রধান অবস্থান। সুতরাং, মূল থ্রেডে যেকোন অতিরিক্ত কাজের চাপ ANR এর সম্ভাবনা বাড়িয়ে দেয়।

সমাধান

একটি অ্যাপ্লিকেশন বিরতির সময়, আপনার সমস্ত কোড ক্রিয়াগুলি প্রয়োজনীয় তা নিশ্চিত করুন বা আপনার স্থানীয় ডিভাইস মেমরিতে ব্যবহারকারীর অবস্থা সংরক্ষণ করার চেষ্টা করুন৷ এবং, অবশ্যই, আপনি বিরতি সময়ের বাইরেও এই ক্রিয়াগুলি সম্পূর্ণ করতে পারেন কিনা তা দেখুন।

কয়েকটি পন্থা :

  • C# অপারেশনটি সরান যা মূল থ্রেড ছাড়া অন্য একটি থ্রেডে একটি বার্তা পরিচালনা করে।
    • যদি আপনার কোড ইউনিটির মূল থ্রেড প্রসঙ্গে নির্ভর না করে, তাহলে বার্তার পরিবর্তে যোগাযোগের জন্য Task ব্যবহার করুন।
  • গেমটি বিরতি দিলে আপনার প্লাগইন থেকে একাধিক বার্তা পাঠাবেন না।
    • গেমটি ব্যাকগ্রাউন্ডে থাকা অবস্থায় ইঞ্জিন বার্তা পাঠাতে পারে না।
    • শুধুমাত্র আপনার গেমে শেষ ডেটা স্টেট পাঠান যদি এটি আপনার গেমের কার্যকারিতাকে প্রভাবিত না করে।

রেফারার ইনস্টল করুন

Play Install Referrer হল একটি অনন্য স্ট্রিং যা প্লে স্টোরে পাঠানো হয় যখনই কোনো ব্যবহারকারী কোনো বিজ্ঞাপনে ক্লিক করে। এটি একটি Android-নির্দিষ্ট বিজ্ঞাপন ট্র্যাকিং শনাক্তকারী৷ একবার ইনস্টল হয়ে গেলে, অ্যাপটি ইনস্টল রেফারারকে অ্যাট্রিবিউশন অংশীদারের কাছে পাঠায়, যেটি ইনস্টলের সাথে উৎসের সাথে মেলে (রূপান্তরটির বৈশিষ্ট্য)।

স্ট্যাক ট্রেস

চিত্র 5 একটি গেম থেকে একটি ANR স্ট্যাক ট্রেস দেখায় যা ইনস্টল অ্যাট্রিবিউশন পুনরুদ্ধার করতে Facebook SDK ব্যবহার করে।

চিত্র 5. একটি বাইন্ডার কল ধারণকারী অ্যান্ড্রয়েড ভাইটাল রিপোর্ট।

কারণ

ANR একটি ধীর বাইন্ডার কলের কারণে হয়েছিল৷ যাইহোক, SDK সোর্স কোড অ্যাক্সেস না করে মূল কারণ নির্ধারণ করা যাবে না।

সমাধান

এই ধরনের সমস্যা সমাধানের জন্য SDK বিকাশকারীর সাথে যোগাযোগ বা সম্ভাব্য সমাধানের জন্য প্রচুর অনলাইন অনুসন্ধান করা, SDK-এর একটি নতুন সংস্করণ অন্যদের জন্য ANR সমাধান করে কিনা তা পরীক্ষা করা বা এমনকি একটি ছোট রোলআউট কৌশল নিয়ে পরীক্ষা করা জড়িত।

Google একটি SDK ইনডেক্স পৃষ্ঠা প্রদান করে যা কোড সনাক্তকরণের মাধ্যমে সংগৃহীত তথ্যের সাথে Google Play অ্যাপের ব্যবহারের ডেটাকে একত্রিত করে যাতে আপনি আপনার অ্যাপ থেকে SDK গ্রহণ করবেন, রাখবেন বা সরাতে পারবেন কিনা তা সিদ্ধান্ত নিতে সাহায্য করার জন্য ডিজাইন করা বৈশিষ্ট্য এবং সংকেত প্রদান করে।

অতিরিক্ত সম্পদ

ANR সম্পর্কে আরও জানতে, নিম্নলিখিত সংস্থানগুলি দেখুন:

  • ডিবাগ ANR - অ্যান্ড্রয়েড গেম ডেভেলপমেন্ট
  • ANR - অ্যাপের গুণমান