রচনার মধ্যে চিন্তা

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

ঘোষণামূলক প্রোগ্রামিং দৃষ্টান্ত

ঐতিহাসিকভাবে, একটি অ্যান্ড্রয়েড ভিউ হায়ারার্কিকে UI উইজেটগুলির একটি ট্রি হিসাবে উপস্থাপন করা হয়। ব্যবহারকারীর ইন্টারঅ্যাকশনের মতো বিভিন্ন কারণে অ্যাপের অবস্থা পরিবর্তিত হলে, বর্তমান ডেটা প্রদর্শনের জন্য UI হায়ারার্কি আপডেট করার প্রয়োজন হয়। UI আপডেট করার সবচেয়ে সাধারণ উপায় হলো findViewById() মতো ফাংশন ব্যবহার করে ট্রি-টি নেভিগেট করা এবং button.setText(String) , container.addChild(View) , বা img.setImageBitmap(Bitmap) এর মতো মেথড কল করে নোড পরিবর্তন করা। এই মেথডগুলো উইজেটের অভ্যন্তরীণ অবস্থা পরিবর্তন করে।

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

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

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

একটি কম্পোজেবল ফাংশনের উদাহরণ

Compose ব্যবহার করে, আপনি একগুচ্ছ কম্পোজেবল ফাংশন সংজ্ঞায়িত করার মাধ্যমে আপনার ইউজার ইন্টারফেস তৈরি করতে পারেন, যেগুলো ডেটা গ্রহণ করে এবং UI এলিমেন্ট তৈরি করে। এর একটি উদাহরণ হলো Greeting উইজেট, যা একটি String গ্রহণ করে এবং একটি Text উইজেট তৈরি করে, যা একটি শুভেচ্ছা বার্তা প্রদর্শন করে।

একটি ফোন, যেখানে "Hello World" লেখাটি দেখা যাচ্ছে, এবং সেই UI তৈরি করার কম্পোজেবল ফাংশনের কোড।
চিত্র ১. একটি কম্পোজেবল ফাংশন, যা ডেটা গ্রহণ করে এবং তা ব্যবহার করে স্ক্রিনে একটি টেক্সট উইজেট রেন্ডার করে।

এই ফাংশনটি সম্পর্কে কয়েকটি উল্লেখযোগ্য বিষয় হলো:

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

    • একই আর্গুমেন্ট দিয়ে একাধিকবার কল করা হলেও ফাংশনটি একইভাবে কাজ করে, এবং এটি গ্লোবাল ভেরিয়েবল বা random() ফাংশনের মতো অন্য কোনো মান ব্যবহার করে না।
    • ফাংশনটি প্রোপার্টি বা গ্লোবাল ভেরিয়েবল পরিবর্তনের মতো কোনো পার্শ্ব-প্রতিক্রিয়া ছাড়াই UI বর্ণনা করে।

    সাধারণভাবে, পুনর্গঠন (Recomposition) অংশে আলোচিত কারণগুলোর জন্য, সকল সংযোজনযোগ্য (composable) ফাংশন অবশ্যই এই বৈশিষ্ট্যগুলো মাথায় রেখে লিখতে হবে।

ঘোষণামূলক দৃষ্টান্ত পরিবর্তন

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

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

কম্পোজ UI-তে উচ্চ-স্তরের অবজেক্ট থেকে তাদের চাইল্ড অবজেক্ট পর্যন্ত ডেটা প্রবাহের চিত্র।
চিত্র ২। অ্যাপ লজিক শীর্ষ-স্তরের কম্পোজেবল ফাংশনে ডেটা সরবরাহ করে। সেই ফাংশনটি অন্যান্য কম্পোজেবলকে কল করার মাধ্যমে UI বর্ণনা করতে ডেটা ব্যবহার করে, এবং উপযুক্ত ডেটা সেই কম্পোজেবলগুলোতে ও ক্রমানুসারে নিচের দিকে প্রেরণ করে।

যখন ব্যবহারকারী UI-এর সাথে ইন্টারঅ্যাক্ট করে, তখন UI, onClick এর মতো ইভেন্ট তৈরি করে। এই ইভেন্টগুলো অ্যাপের লজিককে অবহিত করে, যা তখন অ্যাপের স্টেট পরিবর্তন করতে পারে। যখন স্টেট পরিবর্তিত হয়, তখন নতুন ডেটা দিয়ে কম্পোজেবল ফাংশনগুলোকে আবার কল করা হয়। এর ফলে UI এলিমেন্টগুলো পুনরায় আঁকা হয়—এই প্রক্রিয়াকে রিকম্পোজিশন বলা হয়।

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

গতিশীল বিষয়বস্তু

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

@Composable
fun Greeting(names: List<String>) {
    for (name in names) {
        Text("Hello $name")
    }
}

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

পুনর্গঠন

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

উদাহরণস্বরূপ, এই কম্পোজেবল ফাংশনটি বিবেচনা করুন যা একটি বাটন প্রদর্শন করে:

@Composable
fun ClickCounter(clicks: Int, onClick: () -> Unit) {
    Button(onClick = onClick) {
        Text("I've been clicked $clicks times")
    }
}

প্রতিবার বাটনটি ক্লিক করা হলে, কলার clicks এর মান আপডেট করে। নতুন মানটি দেখানোর জন্য `Compose` আবার Text ফাংশনসহ ল্যাম্বডাটিকে কল করে; এই প্রক্রিয়াটিকে `recomposition` বলা হয়। যেসব ফাংশন এই মানের উপর নির্ভর করে না, সেগুলোকে `recomposed` করা হয় না।

যেমনটি আমরা আলোচনা করেছি, সম্পূর্ণ UI ট্রি পুনর্গঠন করা গণনাগতভাবে ব্যয়বহুল হতে পারে, যা কম্পিউটিং শক্তি এবং ব্যাটারির আয়ু খরচ করে। Compose তার এই বুদ্ধিমান পুনর্গঠন পদ্ধতির মাধ্যমে এই সমস্যার সমাধান করে।

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

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

  • একটি শেয়ার্ড অবজেক্টের প্রপার্টিতে লেখা
  • ViewModel এ একটি অবজার্ভেবল আপডেট করা
  • শেয়ার করা পছন্দগুলি আপডেট করা হচ্ছে

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

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

@Composable
fun SharedPrefsToggle(
    text: String,
    value: Boolean,
    onValueChanged: (Boolean) -> Unit
) {
    Row {
        Text(text)
        Checkbox(checked = value, onCheckedChange = onValueChanged)
    }
}

এই নথিতে Compose ব্যবহার করার সময় কয়েকটি বিষয় সম্পর্কে আলোচনা করা হয়েছে, যেগুলোর ব্যাপারে সচেতন থাকা প্রয়োজন:

  • রিকম্পোজিশন যত বেশি সম্ভব কম্পোজেবল ফাংশন এবং ল্যাম্বডা এড়িয়ে যায়।
  • পুনর্গঠনটি আশাব্যঞ্জক এবং তা বাতিলও করা হতে পারে।
  • একটি কম্পোজেবল ফাংশন বেশ ঘন ঘন চালানো হতে পারে, এমনকি একটি অ্যানিমেশনের প্রতিটি ফ্রেমেও।
  • কম্পোজেবল ফাংশনগুলো সমান্তরালভাবে চলতে পারে।
  • কম্পোজেবল ফাংশনগুলো যেকোনো ক্রমে কার্যকর হতে পারে।

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

পুনর্গঠন যতটা সম্ভব এড়িয়ে যায়

যখন আপনার UI-এর কোনো অংশ ত্রুটিপূর্ণ হয়, তখন Compose শুধুমাত্র সেই অংশগুলোকেই পুনর্গঠন করার যথাসাধ্য চেষ্টা করে যেগুলো আপডেট করা প্রয়োজন। এর মানে হলো, এটি UI ট্রি-তে উপরের বা নিচের কোনো কম্পোজেবল এক্সিকিউট না করেই শুধুমাত্র একটি Button কম্পোজেবল পুনরায় চালানো এড়িয়ে যেতে পারে।

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

/**
 * Display a list of names the user can click with a header
 */
@Composable
fun NamePicker(
    header: String,
    names: List<String>,
    onNameClicked: (String) -> Unit
) {
    Column {
        // this will recompose when [header] changes, but not when [names] changes
        Text(header, style = MaterialTheme.typography.bodyLarge)
        HorizontalDivider()

        // LazyColumn is the Compose version of a RecyclerView.
        // The lambda passed to items() is similar to a RecyclerView.ViewHolder.
        LazyColumn {
            items(names) { name ->
                // When an item's [name] updates, the adapter for that item
                // will recompose. This will not recompose when [header] changes
                NamePickerItem(name, onNameClicked)
            }
        }
    }
}

/**
 * Display a single name the user can click.
 */
@Composable
private fun NamePickerItem(name: String, onClicked: (String) -> Unit) {
    Text(name, Modifier.clickable(onClick = { onClicked(name) }))
}

একটি রিকম্পোজিশনের সময় এই স্কোপগুলোর প্রত্যেকটিই একমাত্র এক্সিকিউট হওয়ার মতো বিষয় হতে পারে। header পরিবর্তিত হলে Compose তার কোনো প্যারেন্টকে এক্সিকিউট না করেই সরাসরি Column ল্যাম্বডাতে চলে যেতে পারে। এবং Column এক্সিকিউট করার সময়, names অপরিবর্তিত থাকলে Compose LazyColumn এর আইটেমগুলো এড়িয়ে যাওয়ার সিদ্ধান্ত নিতে পারে।

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

পুনর্গঠন আশাব্যঞ্জক

যখনই Compose মনে করে যে কোনো কম্পোজেবলের প্যারামিটার পরিবর্তিত হতে পারে, তখনই রিকম্পোজিশন শুরু হয়। রিকম্পোজিশন একটি আশাবাদী প্রক্রিয়া, যার অর্থ হলো Compose আশা করে যে প্যারামিটারগুলো আবার পরিবর্তিত হওয়ার আগেই এটি শেষ হয়ে যাবে। যদি রিকম্পোজিশন শেষ হওয়ার আগেই কোনো প্যারামিটার পরিবর্তিত হয়ে যায়, তাহলে Compose রিকম্পোজিশনটি বাতিল করে নতুন প্যারামিটার দিয়ে আবার শুরু করতে পারে।

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

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

কম্পোজেবল ফাংশনগুলো বেশ ঘন ঘন চলতে পারে।

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

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

যদি কোনো কম্পোজেবল ফাংশনের ডেটার প্রয়োজন হয়, তবে সেই ডেটার জন্য প্যারামিটার নির্ধারণ করুন। এরপর আপনি ব্যয়বহুল কাজটি কম্পোজিশনের বাইরে অন্য একটি থ্রেডে সরিয়ে নিতে পারেন এবং mutableStateOf বা LiveData ব্যবহার করে প্রাপ্ত মানটিকে প্যারামিটার হিসেবে কম্পোজেবল ফাংশনে পাঠাতে পারেন।

কম্পোজেবল ফাংশনগুলো সমান্তরালভাবে চলতে পারে।

Compose কম্পোজেবল ফাংশনগুলোকে সমান্তরালভাবে চালানোর মাধ্যমে রিকম্পোজিশন অপ্টিমাইজ করতে পারে। এর ফলে Compose একাধিক কোরের সুবিধা নিতে পারবে এবং স্ক্রিনে প্রদর্শিত নয় এমন কম্পোজেবল ফাংশনগুলোকে কম প্রায়োরিটিতে চালাতে পারবে।

এই অপ্টিমাইজেশনের ফলে একটি কম্পোজেবল ফাংশন ব্যাকগ্রাউন্ড থ্রেডের একটি পুলে এক্সিকিউট হতে পারে। যদি একটি কম্পোজেবল ফাংশন কোনো ViewModel এর ফাংশনকে কল করে, তাহলে Compose একই সময়ে একাধিক থ্রেড থেকে সেই ফাংশনটিকে কল করতে পারে।

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

যখন একটি কম্পোজেবল ফাংশন কল করা হয়, তখন সেই কলটি কলারের থ্রেড থেকে ভিন্ন কোনো থ্রেডে ঘটতে পারে। এর মানে হলো, কম্পোজেবল ল্যাম্বডার ভেতরের ভেরিয়েবল পরিবর্তনকারী কোড পরিহার করা উচিত—কারণ এই ধরনের কোড থ্রেড-সেফ নয় এবং এটি কম্পোজেবল ল্যাম্বডার একটি অগ্রহণযোগ্য সাইড-ইফেক্ট।

এখানে একটি কম্পোজেবলের উদাহরণ দেওয়া হলো যা একটি তালিকা এবং তার সংখ্যা প্রদর্শন করে:

@Composable
fun ListComposable(myList: List<String>) {
    Row(horizontalArrangement = Arrangement.SpaceBetween) {
        Column {
            for (item in myList) {
                Text("Item: $item")
            }
        }
        Text("Count: ${myList.size}")
    }
}

এই কোডটি সাইড-ইফেক্ট মুক্ত এবং ইনপুট লিস্টকে UI-তে রূপান্তর করে। একটি ছোট লিস্ট দেখানোর জন্য এটি একটি চমৎকার কোড। তবে, যদি ফাংশনটি কোনো লোকাল ভেরিয়েবলে লেখে, তাহলে এই কোডটি থ্রেড-সেফ বা সঠিক হবে না।

@Composable
fun ListWithBug(myList: List<String>) {
    var items = 0

    Row(horizontalArrangement = Arrangement.SpaceBetween) {
        Column {
            for (item in myList) {
                Card {
                    Text("Item: $item")
                    items++ // Avoid! Side-effect of the column recomposing.
                }
            }
        }
        Text("Count: $items")
    }
}

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

কম্পোজেবল ফাংশনগুলো যেকোনো ক্রমে কার্যকর হতে পারে।

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

উদাহরণস্বরূপ, ধরুন আপনার কাছে একটি ট্যাব লেআউটে তিনটি স্ক্রিন আঁকার জন্য এইরকম কোড আছে:

@Composable
fun ButtonRow() {
    MyFancyNavigation {
        StartScreen()
        MiddleScreen()
        EndScreen()
    }
}

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

আরও জানুন

Compose-এ কীভাবে চিন্তা করতে হয় এবং কম্পোজেবল ফাংশন সম্পর্কে আরও জানতে, নিম্নলিখিত অতিরিক্ত রিসোর্সগুলো দেখুন।

ভিডিও

{% হুবহু %} {% endverbatim %} {% হুবহু %} {% endverbatim %}