ক্র্যাশ

যখনই কোনো আনহ্যান্ডেলড এক্সেপশন বা সিগন্যালের কারণে অপ্রত্যাশিতভাবে অ্যাপ বন্ধ হয়ে যায়, তখনই একটি অ্যান্ড্রয়েড অ্যাপ ক্র্যাশ করে। জাভা বা কোটলিনে লেখা কোনো অ্যাপ ক্র্যাশ করে যদি এটি কোনো আনহ্যান্ডেলড এক্সেপশন থ্রো করে, যা Throwable ক্লাস দ্বারা নির্দেশিত হয়। মেশিন কোড বা C++ ব্যবহার করে লেখা কোনো অ্যাপ ক্র্যাশ করে যদি এর এক্সিকিউশনের সময় SIGSEGV মতো কোনো আনহ্যান্ডেলড সিগন্যাল আসে।

যখন কোনো অ্যাপ ক্র্যাশ করে, অ্যান্ড্রয়েড অ্যাপটির প্রসেস বন্ধ করে দেয় এবং ব্যবহারকারীকে জানানোর জন্য একটি ডায়ালগ প্রদর্শন করে যে অ্যাপটি বন্ধ হয়ে গেছে, যেমনটি চিত্র ১-এ দেখানো হয়েছে।

অ্যান্ড্রয়েড চালিত ডিভাইসে একটি অ্যাপ ক্র্যাশ করেছে
চিত্র ১. অ্যান্ড্রয়েড চালিত ডিভাইসে একটি অ্যাপ ক্র্যাশ।

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

আপনার অ্যাপটি ক্র্যাশ করলে, সমস্যাটি নির্ণয় ও সমাধান করার জন্য আপনি এই পৃষ্ঠার নির্দেশিকা ব্যবহার করতে পারেন।

সমস্যাটি শনাক্ত করুন

আপনার ব্যবহারকারীরা যখন আপনার অ্যাপ ব্যবহার করেন, তখন যে অ্যাপ ক্র্যাশ করছে, তা আপনি সবসময় জানতে পারেন না। আপনি যদি ইতিমধ্যেই আপনার অ্যাপটি প্রকাশ করে থাকেন, তাহলে আপনার অ্যাপের ক্র্যাশ রেট দেখতে অ্যান্ড্রয়েড ভাইটালস ব্যবহার করতে পারেন।

অ্যান্ড্রয়েড ভাইটালস

অ্যান্ড্রয়েড ভাইটালস আপনার অ্যাপের ক্র্যাশ রেট নিরীক্ষণ ও উন্নত করতে সাহায্য করতে পারে। অ্যান্ড্রয়েড ভাইটালস বিভিন্ন ধরনের ক্র্যাশ রেট পরিমাপ করে:

  • ক্র্যাশ রেট: আপনার দৈনিক সক্রিয় ব্যবহারকারীদের শতকরা হার, যারা কোনো না কোনো ধরনের ক্র্যাশের সম্মুখীন হয়েছেন।
  • ব্যবহারকারীর অনুভূত ক্র্যাশ হার: আপনার দৈনিক সক্রিয় ব্যবহারকারীদের সেই শতাংশ, যারা আপনার অ্যাপটি সক্রিয়ভাবে ব্যবহার করার সময় অন্তত একটি ক্র্যাশের সম্মুখীন হয়েছেন (একটি ব্যবহারকারী-অনুভূত ক্র্যাশ)। একটি অ্যাপকে সক্রিয়ভাবে ব্যবহৃত হচ্ছে বলে মনে করা হয় যদি এটি কোনো কার্যকলাপ প্রদর্শন করে বা কোনো ফোরগ্রাউন্ড পরিষেবা সম্পাদন করে।

  • একাধিক ক্র্যাশের হার: আপনার দৈনিক সক্রিয় ব্যবহারকারীদের সেই শতাংশ, যারা অন্তত দুটি ক্র্যাশের সম্মুখীন হয়েছেন।

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

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

প্লে এই মেট্রিকটিতে খারাপ আচরণের দুটি সীমা নির্ধারণ করেছে:

  • সামগ্রিক ত্রুটিপূর্ণ আচরণের সীমা: সকল ডিভাইস মডেল মিলিয়ে, দৈনিক সক্রিয় ব্যবহারকারীদের মধ্যে অন্তত ১.০৯% ব্যবহারকারীকে ক্র্যাশের সম্মুখীন হতে হবে।
  • প্রতি-ডিভাইস ত্রুটিপূর্ণ আচরণের সীমা: একটি নির্দিষ্ট ডিভাইস মডেলের ক্ষেত্রে, দৈনিক সক্রিয় ব্যবহারকারীদের কমপক্ষে ৮% একটি ক্র্যাশ অনুভব করেন।

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

আপনার অ্যাপে অতিরিক্ত ক্র্যাশ হলে অ্যান্ড্রয়েড ভাইটালস আপনাকে প্লে কনসোলে সতর্ক করতে পারে।

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

ক্র্যাশগুলো নির্ণয় করুন

আপনার অ্যাপ ক্র্যাশ করছে তা শনাক্ত করার পর, পরবর্তী পদক্ষেপ হলো সেগুলোর কারণ নির্ণয় করা। ক্র্যাশের সমাধান করা কঠিন হতে পারে। তবে, যদি আপনি ক্র্যাশের মূল কারণ শনাক্ত করতে পারেন, তাহলে খুব সম্ভবত আপনি এর একটি সমাধানও খুঁজে পাবেন।

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

অ্যান্ড্রয়েডে ক্র্যাশ হলে একটি স্ট্যাক ট্রেস তৈরি হয়, যা হলো আপনার প্রোগ্রামটি ক্র্যাশ করার মুহূর্ত পর্যন্ত কল করা নেস্টেড ফাংশনগুলোর ক্রমের একটি স্ন্যাপশট। আপনি অ্যান্ড্রয়েড ভাইটালস -এ ক্র্যাশ স্ট্যাক ট্রেস দেখতে পারেন।

স্ট্যাক ট্রেস কীভাবে পড়তে হয়

ক্র্যাশ ঠিক করার প্রথম ধাপ হলো এটি কোথায় ঘটছে তা শনাক্ত করা। আপনি যদি প্লে কনসোল ব্যবহার করেন, তাহলে রিপোর্টের বিবরণে থাকা স্ট্যাক ট্রেস অথবা লগক্যাট টুলের আউটপুট ব্যবহার করতে পারেন। যদি আপনার কাছে কোনো স্ট্যাক ট্রেস না থাকে, তবে অ্যাপটি ম্যানুয়ালি পরীক্ষা করে অথবা ক্ষতিগ্রস্ত ব্যবহারকারীদের সাথে যোগাযোগ করে স্থানীয়ভাবে ক্র্যাশটি পুনরায় ঘটানো উচিত এবং লগক্যাট ব্যবহার করে তা পুনরায় ঘটানোর চেষ্টা করা উচিত।

নিম্নলিখিত ট্রেসটি Jetpack Compose ব্যবহার করে লেখা একটি অ্যাপ ক্র্যাশ করার উদাহরণ দেখাচ্ছে:

--------- beginning of crash
AndroidRuntime: FATAL EXCEPTION: main
Process: com.android.developer.crashsample, PID: 3686
java.lang.NullPointerException
    at com.android.developer.crashsample.ComposableSingletons$MainActivityKt.lambda$0(MainActivity.kt:27)
    at androidx.compose.foundation.ClickableNode.handleUpEvent(Clickable.kt:958)
    at androidx.compose.foundation.ClickableNode.onPointerEvent-H0pRuoY(Clickable.kt:895)
    at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:446)
    at androidx.compose.ui.input.pointer.HitPathTracker.dispatchChanges(HitPathTracker.kt:181)
    at androidx.compose.ui.input.pointer.PointerInputEventProcessor.process-BIzXfog(PointerInputEventProcessor.kt:118)
    at androidx.compose.ui.platform.AndroidComposeView.dispatchTouchEvent(AndroidComposeView.android.kt:2650)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2969)
    at android.app.Activity.dispatchTouchEvent(Activity.java:4683)
    at android.os.Looper.loop(Looper.java:398)
    at android.app.ActivityThread.main(ActivityThread.java:9569)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:918)

একটি স্ট্যাক ট্রেস দুটি তথ্য দেখায় যা কোনো ক্র্যাশ ডিবাগ করার জন্য অত্যন্ত গুরুত্বপূর্ণ:

  • যে ধরনের ব্যতিক্রম নিক্ষেপ করা হয়েছে।
  • কোডের সেই অংশ যেখানে এক্সেপশনটি থ্রো করা হয়।

সাধারণত, কোন ধরনের এক্সেপশন থ্রো করা হয়েছে তা থেকে কী ভুল হয়েছে সে সম্পর্কে খুব জোরালো ইঙ্গিত পাওয়া যায়। এটি IOException , OutOfMemoryError , নাকি অন্য কিছু, তা দেখুন এবং এক্সেপশন ক্লাসটির ডকুমেন্টেশন খুঁজে বের করুন।

স্ট্যাক ট্রেসের দ্বিতীয় লাইনে সোর্স ফাইলের সেই ক্লাস, মেথড, ফাইল এবং লাইন নম্বর দেখানো হয় যেখানে এক্সেপশনটি থ্রো করা হয়েছে। কল করা প্রতিটি ফাংশনের জন্য, আরেকটি লাইনে তার পূর্ববর্তী কল সাইটটি দেখানো হয় (যাকে স্ট্যাক ফ্রেম বলা হয়)।

স্ট্যাকের উপরের দিকে গিয়ে এবং কোড পরীক্ষা করে, আপনি এমন একটি জায়গা খুঁজে পেতে পারেন যেখানে ভুল মান পাস করা হচ্ছে। যদি আপনার কোড স্ট্যাক ট্রেসে না দেখা যায়, তাহলে সম্ভবত আপনি কোনো অ্যাসিঙ্ক্রোনাস অপারেশনে একটি অবৈধ প্যারামিটার পাস করেছেন। প্রায়শই স্ট্যাক ট্রেসের প্রতিটি লাইন পরীক্ষা করে, আপনার ব্যবহৃত API ক্লাসগুলো খুঁজে বের করে, এবং আপনার পাস করা প্যারামিটারগুলো সঠিক ছিল ও আপনি একটি অনুমোদিত জায়গা থেকে সেটিকে কল করেছেন কিনা তা নিশ্চিত করার মাধ্যমে কী ঘটেছে তা বের করা যায়।

C এবং C++ কোডযুক্ত অ্যাপের স্ট্যাক ট্রেস প্রায় একই ভাবে কাজ করে।

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'google/foo/bar:10/123.456/78910:user/release-keys'
ABI: 'arm64'
Timestamp: 2020-02-16 11:16:31+0100
pid: 8288, tid: 8288, name: com.example.testapp  >>> com.example.testapp <<<
uid: 1010332
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
Cause: null pointer dereference
    x0  0000007da81396c0  x1  0000007fc91522d4  x2  0000000000000001  x3  000000000000206e
    x4  0000007da8087000  x5  0000007fc9152310  x6  0000007d209c6c68  x7  0000007da8087000
    x8  0000000000000000  x9  0000007cba01b660  x10 0000000000430000  x11 0000007d80000000
    x12 0000000000000060  x13 0000000023fafc10  x14 0000000000000006  x15 ffffffffffffffff
    x16 0000007cba01b618  x17 0000007da44c88c0  x18 0000007da943c000  x19 0000007da8087000
    x20 0000000000000000  x21 0000007da8087000  x22 0000007fc9152540  x23 0000007d17982d6b
    x24 0000000000000004  x25 0000007da823c020  x26 0000007da80870b0  x27 0000000000000001
    x28 0000007fc91522d0  x29 0000007fc91522a0
    sp  0000007fc9152290  lr  0000007d22d4e354  pc  0000007cba01b640

backtrace:
  #00  pc 0000000000042f89  /data/app/com.example.testapp/lib/arm64/libexample.so (com::example::Crasher::crash() const)
  #01  pc 0000000000000640  /data/app/com.example.testapp/lib/arm64/libexample.so (com::example::runCrashThread())
  #02  pc 0000000000065a3b  /system/lib/libc.so (__pthread_start(void*))
  #03  pc 000000000001e4fd  /system/lib/libc.so (__start_thread)

যদি আপনি নেটিভ স্ট্যাক ট্রেসে ক্লাস এবং ফাংশন-স্তরের তথ্য দেখতে না পান, তাহলে আপনাকে একটি নেটিভ ডিবাগ সিম্বলস ফাইল তৈরি করে গুগল প্লে কনসোলে আপলোড করতে হতে পারে। আরও তথ্যের জন্য, ‘Deobfuscate crash stack traces’ দেখুন। নেটিভ ক্র্যাশ সম্পর্কে সাধারণ তথ্যের জন্য, ‘Diagnosing native crashes’ দেখুন।

ক্র্যাশ পুনরায় ঘটানোর জন্য কিছু পরামর্শ

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

স্মৃতি ত্রুটি

যদি আপনার OutOfMemoryError হয়, তাহলে আপনি পরীক্ষার জন্য কম মেমরি ধারণক্ষমতার একটি এমুলেটর তৈরি করতে পারেন। চিত্র ২-এ AVD ম্যানেজার সেটিংস দেখানো হয়েছে, যেখান থেকে আপনি ডিভাইসের মেমরির পরিমাণ নিয়ন্ত্রণ করতে পারেন।

এভিডি ম্যানেজারে মেমরি সেটিং
চিত্র ২. এভিডি ম্যানেজারে মেমরি সেটিং।

নেটওয়ার্কিং ব্যতিক্রম

যেহেতু ব্যবহারকারীরা প্রায়শই মোবাইল বা ওয়াই-ফাই নেটওয়ার্কের আওতার বাইরে ও ভেতরে যাতায়াত করেন, তাই কোনো অ্যাপ্লিকেশনের ক্ষেত্রে নেটওয়ার্ক সংক্রান্ত ব্যতিক্রমগুলোকে সাধারণত ত্রুটি হিসেবে গণ্য করা উচিত নয়, বরং অপ্রত্যাশিতভাবে ঘটে যাওয়া স্বাভাবিক পরিচালন পরিস্থিতি হিসেবে বিবেচনা করা উচিত।

যদি আপনাকে UnknownHostException এর মতো কোনো নেটওয়ার্ক ব্যতিক্রম পুনরায় ঘটাতে হয়, তাহলে আপনার অ্যাপ্লিকেশনটি যখন নেটওয়ার্ক ব্যবহার করার চেষ্টা করবে, তখন এয়ারপ্লেন মোড চালু করে দেখুন।

আরেকটি উপায় হলো নেটওয়ার্ক স্পিড এমুলেশন, নেটওয়ার্ক ডিলে, অথবা উভয়ই বেছে নিয়ে এমুলেটরে নেটওয়ার্কের মান কমিয়ে আনা। আপনি AVD ম্যানেজারে থাকা স্পিড এবং ল্যাটেন্সি সেটিংস ব্যবহার করতে পারেন, অথবা নিচের কমান্ড-লাইন উদাহরণে দেখানো অনুযায়ী -netdelay এবং -netspeed ফ্ল্যাগসহ এমুলেটরটি চালু করতে পারেন:

emulator -avd [your-avd-image] -netdelay 20000 -netspeed gsm

এই উদাহরণটি সমস্ত নেটওয়ার্ক অনুরোধে ২০ সেকেন্ডের একটি বিলম্ব এবং ১৪.৪ Kbps আপলোড ও ডাউনলোড গতি নির্ধারণ করে। এমুলেটরের কমান্ড-লাইন বিকল্পগুলি সম্পর্কে আরও তথ্যের জন্য, "কমান্ড লাইন থেকে এমুলেটর শুরু করুন" দেখুন।

লগক্যাট দিয়ে পড়া

একবার ক্র্যাশটি পুনরায় ঘটাতে পারলে, আপনি আরও তথ্য পেতে logcat মতো একটি টুল ব্যবহার করতে পারেন।

logcat আউটপুট আপনাকে দেখাবে যে আপনি আর কী কী লগ মেসেজ প্রিন্ট করেছেন, এবং সিস্টেমের অন্যান্য মেসেজগুলোও দেখাবে। আপনার যোগ করা যেকোনো অতিরিক্ত Log স্টেটমেন্ট বন্ধ করতে ভুলবেন না, কারণ আপনার অ্যাপ চলার সময় সেগুলো প্রিন্ট করলে সিপিইউ এবং ব্যাটারির অপচয় হয়।

নাল পয়েন্টার এক্সেপশনের কারণে সৃষ্ট ক্র্যাশ প্রতিরোধ করুন

নাল পয়েন্টার এক্সেপশন (যা NullPointerException রানটাইম এরর টাইপ দ্বারা চিহ্নিত করা হয়) তখন ঘটে যখন আপনি কোনো নাল (null) অবজেক্ট অ্যাক্সেস করার চেষ্টা করেন, সাধারণত এর মেথড কল করে বা এর মেম্বার অ্যাক্সেস করার মাধ্যমে। গুগল প্লে-তে অ্যাপ ক্র্যাশের সবচেয়ে বড় কারণ হলো নাল পয়েন্টার এক্সেপশন। নাল (null) এর উদ্দেশ্য হলো এটি বোঝানো যে অবজেক্টটি অনুপস্থিত — উদাহরণস্বরূপ, এটি এখনও তৈরি বা অ্যাসাইন করা হয়নি।

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

যেহেতু আপনি কল করা প্রতিটি মেথডের প্রতিটি প্যারামিটারের জন্য নাল চেক করতে চাইবেন না, তাই নাল হওয়ার বিষয়টি বোঝানোর জন্য আপনি IDE অথবা অবজেক্টের টাইপের উপর নির্ভর করতে পারেন।

কোটলিন

কোটলিনে, নালিবিলিটি (nullability) হলো টাইপ সিস্টেমের একটি অংশ। উদাহরণস্বরূপ, একটি ভেরিয়েবলকে শুরু থেকেই নালযোগ্য (nullable) বা নন-নালযোগ্য (non-nullable) হিসেবে ডিক্লেয়ার করতে হয়। নালযোগ্য টাইপগুলোকে একটি ? চিহ্ন দিয়ে চিহ্নিত করা হয়।

// non-null
var s: String = "Hello"

// null
var s: String? = "Hello"

নন-নাল ভেরিয়েবলে নাল ভ্যালু অ্যাসাইন করা যায় না এবং নাল ভেরিয়েবলকে নন-নাল হিসেবে ব্যবহার করার আগে সেটির নালযোগ্যতা যাচাই করে নিতে হয়।

আপনি যদি স্পষ্টভাবে নাল (null) পরীক্ষা করতে না চান, তাহলে আপনি ?. সেফ কল অপারেটরটি ব্যবহার করতে পারেন:

val length: Int? = string?.length  // length is a nullable int
                                   // if string is null, then length is null

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

নাল (null) কিনা তা যাচাই করার কয়েকটি উপায় নিচে দেওয়া হলো:

  • if চেক

    val length = if(string != null) string.length else 0
    

    স্মার্ট-কাস্ট এবং নাল চেকের কারণে, কোটলিন কম্পাইলার জানে যে স্ট্রিং ভ্যালুটি নন-নাল, তাই এটি আপনাকে সেফ কল অপারেটরের প্রয়োজন ছাড়াই সরাসরি রেফারেন্সটি ব্যবহার করতে দেয়।

  • ?: এলভিস অপারেটর

    এই অপারেটরটি আপনাকে নির্দিষ্ট করে বলতে দেয় যে, "যদি অবজেক্টটি নন-নাল হয়, তবে অবজেক্টটিই রিটার্ন করো; অন্যথায়, অন্য কিছু রিটার্ন করো"।

    val length = string?.length ?: 0
    

Kotlin-এ এখনও NullPointerException পাওয়া যেতে পারে। নিম্নলিখিতগুলি হলো সবচেয়ে সাধারণ পরিস্থিতি:

  • যখন আপনি স্পষ্টভাবে একটি NullPointerException থ্রো করছেন।
  • যখন আপনি নাল অ্যাসারশন অপারেটর !! ব্যবহার করেন, তখন এই অপারেটরটি যেকোনো ভ্যালুকে একটি নন-নাল টাইপে রূপান্তর করে এবং ভ্যালুটি নাল হলে NullPointerException থ্রো করে।
  • প্ল্যাটফর্ম টাইপের কোনো নাল রেফারেন্স অ্যাক্সেস করার সময়।

প্ল্যাটফর্মের প্রকারভেদ

প্ল্যাটফর্ম টাইপ হলো জাভা থেকে আসা অবজেক্ট ডিক্লারেশন। এই টাইপগুলোকে বিশেষ মর্যাদা দেওয়া হয় ; এখানে নাল (null) চেক ততটা কঠোরভাবে প্রয়োগ করা হয় না, তাই নন-নাল (non-null) নিশ্চয়তা জাভার মতোই থাকে। যখন আপনি একটি প্ল্যাটফর্ম টাইপ রেফারেন্স অ্যাক্সেস করেন, কোটলিন কম্পাইল-টাইম এরর তৈরি করে না, কিন্তু এই রেফারেন্সগুলো রান-টাইম এররের কারণ হতে পারে। কোটলিন ডকুমেন্টেশন থেকে নিম্নলিখিত উদাহরণটি দেখুন:

val list = ArrayList<String>() // non-null (constructor result) list.add("Item")
val size = list.size // non-null (primitive int) val item = list[0] // platform
type inferred (ordinary Java object) item.substring(1) // allowed, may throw an
                                                       // exception if item == null

যখন কোনো প্ল্যাটফর্ম ভ্যালু একটি কোটলিন ভেরিয়েবলে অ্যাসাইন করা হয়, তখন কোটলিন টাইপ ইনফারেন্সের উপর নির্ভর করে, অথবা আপনি কোন টাইপ আশা করবেন তা নির্ধারণ করে দিতে পারেন। জাভা থেকে আসা কোনো রেফারেন্সের সঠিক নালিবিলিটি স্টেট নিশ্চিত করার সেরা উপায় হলো আপনার জাভা কোডে নালিবিলিটি অ্যানোটেশন (যেমন, @Nullable ) ব্যবহার করা। কোটলিন কম্পাইলার এই রেফারেন্সগুলোকে প্ল্যাটফর্ম টাইপ হিসেবে নয়, বরং প্রকৃত নালযোগ্য বা অ-নালযোগ্য টাইপ হিসেবে উপস্থাপন করবে।

প্রয়োজন অনুযায়ী জাভা জেটপ্যাক এপিআই-গুলোকে @Nullable বা @NonNull দিয়ে টীকাযুক্ত করা হয়েছে এবং অ্যান্ড্রয়েড ১১ এসডিকে- তেও একই ধরনের পদ্ধতি অনুসরণ করা হয়েছে। এই এসডিকে থেকে আসা যে টাইপগুলো কোটলিনে ব্যবহৃত হয়, সেগুলোকে সঠিক নালযোগ্য বা নন-নালযোগ্য টাইপ হিসেবে উপস্থাপন করা হবে।

কোটলিনের টাইপ সিস্টেম NullPointerException ক্র্যাশ উল্লেখযোগ্যভাবে কমিয়ে দেয়। উদাহরণস্বরূপ, গুগল হোম অ্যাপটি যে বছর নতুন ফিচার ডেভেলপমেন্ট কোটলিনে স্থানান্তরিত করেছিল, সেই বছরে নাল পয়েন্টার এক্সেপশনের কারণে হওয়া ক্র্যাশ ৩০% কমে গিয়েছিল।