করুটিন হল কনকারেন্সি ডিজাইন প্যাটার্ন যা আপনি অ্যাসিঙ্ক্রোনাসভাবে এক্সিকিউট করা কোডকে সহজ করতে Android-এ ব্যবহার করতে পারেন। Kotlin-এর 1.3 ভার্সনে করুটিন যোগ করা হয়েছে এবং এটি অন্যান্য ভাষা থেকে নেওয়া প্রতিষ্ঠিত কনসেপ্টের উপর ভিত্তি করে তৈরি হয়েছে।
Android-এ, কোরাউটিন দীর্ঘক্ষণ ধরে চলতে থাকা টাস্ক ম্যানেজ করতে সাহায্য করে, যা অন্যথায় মূল থ্রেড ব্লক করতে পারে এবং আপনার অ্যাপকে অনুপযুক্ত করে তুলতে পারে। করুটিন ব্যবহার করেন এমন ৫০%-এর বেশি পেশাদার ডেভেলপাররা জানিয়েছেন যে তাদের প্রোডাক্টিভিটি বেড়েছে। এইসব সমস্যার সমাধান করতে আপনি কীভাবে Kotlin কোরাউটিন ব্যবহার করতে পারবেন তা এই বিষয়ে বর্ণনা করা হয়েছে, এর ফলে আপনি আরও পরিচ্ছন্ন ও সংক্ষিপ্ত অ্যাপ কোড লিখতে পারবেন।
ফিচার
Android-এ অ্যাসিঙ্ক্রোনাস প্রোগ্রামিংয়ের জন্য আমাদের সাজেস্ট করা সমাধান হল কোরুটিন। উল্লেখযোগ্য ফিচারের মধ্যে এগুলি অন্তর্ভুক্ত:
- লাইটওয়েট: আপনি একটি থ্রেডে অনেক করুটিন রান করাতে পারবেন, কারণ এতে সাসপেনশন কাজ করে, যা করুটিন রান করা থ্রেডকে ব্লক করে না। সাসপেন্ড করলে ব্লক করার চেয়ে বেশি মেমরি সেভ হয় এবং এর ফলে একসাথে অনেক অপারেশন করা যায়।
- কম মেমরি লিক: স্কোপের মধ্যে অপারেশন রান করাতে স্ট্রাকচার্ড কনকারেন্সি ব্যবহার করুন।
- বিল্ট-ইন বাতিলকরণ সংক্রান্ত সহায়তা: চলমান কোরাউটিন হায়ারার্কির মাধ্যমে বাতিলকরণ অটোমেটিক প্রোপাগেট করা হয়।
- Jetpack ইন্টিগ্রেশন: অনেক Jetpack লাইব্রেরিতে এক্সটেনশন থাকে যা সম্পূর্ণ কোরুটিন সহায়তা প্রদান করে। কিছু লাইব্রেরিও তাদের নিজস্ব করুটিন স্কোপ প্রদান করে যা আপনি স্ট্রাকচার্ড কনকারেন্সির জন্য ব্যবহার করতে পারেন।
উদাহরণ ওভারভিউ
অ্যাপ আর্কিটেকচার সংক্রান্ত নির্দেশিকা অনুযায়ী, এই টপিকে উল্লেখ করা উদাহরণগুলি নেটওয়ার্ক অনুরোধ করে এবং ফলাফলটি মূল থ্রেডে রিটার্ন করে, যেখানে অ্যাপটি ব্যবহারকারীকে ফলাফল দেখাতে পারে।
নির্দিষ্টভাবে, ViewModel
আর্কিটেকচার কম্পোনেন্ট, নেটওয়ার্ক অনুরোধ ট্রিগার করার জন্য
প্রধান থ্রেডে রিপোজিটরি লেয়ারকে কল করে। এই গাইড বিভিন্ন সমাধানের মাধ্যমে পুনরাবৃত্তি করে
যা মূল থ্রেড আনব্লক রাখতে কোরাউটিন ব্যবহার করে।
ViewModel-এ KTX এক্সটেনশনের একটি সেট অন্তর্ভুক্ত থাকে যা সরাসরি কোরাউটিনের
সাথে কাজ করে। এইসব এক্সটেনশন হল
lifecycle-viewmodel-ktx লাইব্রেরি এবং সেগুলি
এই নির্দেশিকায় ব্যবহার করা হয়েছে।
ডিপেন্ডেন্সি সংক্রান্ত তথ্য
আপনার Android প্রোজেক্টে কোরাউটিন ব্যবহার করতে, আপনার অ্যাপের build.gradle ফাইলে
নিচের ডিপেন্ডেন্সি যোগ করুন:
গ্রোভি
dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9' }
কোটলিন
dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9") }
ব্যাকগ্রাউন্ড থ্রেডে এক্সিকিউট করা
মূল থ্রেডে নেটওয়ার্ক রিকোয়েস্ট করলে, উত্তর না পাওয়া পর্যন্ত এটি অপেক্ষা করে বা ব্লক হয়ে যায়।
থ্রেডটি ব্লক করা থাকার কারণে, OS onDraw() কল করতে
পারছে না, এর ফলে আপনার অ্যাপ ফ্রিজ হয়ে যাচ্ছে এবং সম্ভাব্য
ভাবে 'অ্যাপ্লিকেশন উত্তর দিচ্ছে না' (ANR) ডায়ালগ দেখাচ্ছে। ব্যবহারকারীর আরও ভালো
অভিজ্ঞতার জন্য, আসুন ব্যাকগ্রাউন্ড থ্রেডে এই অপারেশনটি রান করি।
প্রথমে, আমাদের Repository ক্লাসটি দেখে নেওয়া যাক এবং এটি কীভাবে
নেটওয়ার্ক অনুরোধ করছে তা দেখা যাক:
sealed class Result<out R> { data class Success<out T>(val data: T) : Result<T>() data class Error(val exception: Exception) : Result<Nothing>() } private const val loginUrl = "https://example.com/login" class LoginRepository(private val responseParser: LoginResponseParser) { // Function that makes the network request, blocking the current thread fun makeLoginRequest( jsonBody: String ): Result<LoginResponse> { val url = URL(loginUrl) (url.openConnection() as? HttpURLConnection)?.run { requestMethod = "POST" setRequestProperty("Content-Type", "application/json; utf-8") setRequestProperty("Accept", "application/json") doOutput = true outputStream.write(jsonBody.toByteArray()) return Result.Success(responseParser.parse(inputStream)) } return Result.Error(Exception("Cannot open HttpURLConnection")) } }
makeLoginRequest হল সিঙ্ক্রোনাস এবং কলিং থ্রেড ব্লক করে। নেটওয়ার্ক
অনুরোধের উত্তর মডেল করতে, আমাদের নিজস্ব Result ক্লাস আছে।
ব্যবহারকারী কোনও বোতামে ক্লিক করলে, ViewModel নেটওয়ার্ক সংক্রান্ত অনুরোধ ট্রিগার করে, যেমন
এই ক্ষেত্রে:
class LoginViewModel( private val loginRepository: LoginRepository ) : ViewModel() { fun login(username: String, token: String) { val jsonBody = "{ username: \"$username\", token: \"$token\"}" loginRepository.makeLoginRequest(jsonBody) } }
আগের কোডে, নেটওয়ার্ক সংক্রান্ত অনুরোধ করার সময় LoginViewModel UI থ্রেডকে ব্লক করছে।
প্রধান থ্রেড থেকে এক্সিকিউশন
সরানোর সবচেয়ে সহজ সমাধান হল একটি নতুন কোরাউটিন তৈরি করা এবং I/O থ্রেডে নেটওয়ার্ক
অনুরোধ এক্সিকিউট করা:
class LoginViewModel( private val loginRepository: LoginRepository ) : ViewModel() { fun login(username: String, token: String) { // Create a new coroutine to move the execution off the UI thread viewModelScope.launch(Dispatchers.IO) { val jsonBody = "{ username: \"$username\", token: \"$token\"}" loginRepository.makeLoginRequest(jsonBody) } } }
login ফাংশনে কোরাউটিন কোড বিশ্লেষণ করা যাক:
viewModelScopeহল একটি পূর্বনির্ধারিতCoroutineScopeযাViewModelKTX এক্সটেনশনের সাথে অন্তর্ভুক্ত থাকে। মনে রাখবেন, সব কোরাউটিনকে অবশ্যই একটি স্কোপের মধ্যে রান করতে হবে।CoroutineScopeএকটি বা একাধিক সম্পর্কিত কোরাউটিন ম্যানেজ করে।launchহল এমন একটি ফাংশন যা কোরাউটিন তৈরি করে এবং সংশ্লিষ্ট ডিসপ্যাচারকে এর ফাংশন বডির এক্সিকিউশন ডিসপ্যাচ করে।Dispatchers.IOথেকে বোঝা যায় যে এই কোরাউটিনটি I/O অপারেশনের জন্য রিজার্ভ করা থ্রেডে এক্সিকিউট করা উচিত।
login ফাংশনটি নিম্নলিখিতভাবে এক্সিকিউট করা হয়:
- অ্যাপটি মূল থ্রেডে
Viewলেয়ার থেকেloginফাংশন কল করে। launchএকটি নতুন কোরাউটিন তৈরি করে এবং নেটওয়ার্ক অনুরোধটি I/O অপারেশনের জন্য সংরক্ষিত থ্রেডে স্বাধীনভাবে করা হয়।- করুটিন চলাকালীন,
loginফাংশন এক্সিকিউট করা চালিয়ে যায় এবং নেটওয়ার্ক অনুরোধ সম্পূর্ণ হওয়ার আগেই রিটার্ন করে। মনে রাখবেন যে সহজ করার জন্য, নেটওয়ার্কের উত্তরটি এখন উপেক্ষা করা হয়েছে।
এই কোরাউটিন viewModelScope দিয়ে শুরু করা হয়েছে বলে, এটি ViewModel-এর স্কোপের মধ্যে
এক্সিকিউট করা হয়। ব্যবহারকারী স্ক্রিন থেকে সরে গেলে ViewModel ধ্বংস হয়ে যায়, তাই viewModelScope অটোমেটিক
বাতিল হয়ে যায় এবং সব রানিং কোরাউটিনও বাতিল হয়ে যায়।
আগের উদাহরণে একটি সমস্যা হল যে
makeLoginRequest কল করা যেকোনও কিছুকে এক্সিকিউশনকে স্পষ্টভাবে
মূল থ্রেড থেকে সরিয়ে নেওয়ার কথা মনে রাখতে হবে। আমাদের জন্য এই সমস্যার সমাধান করতে, কীভাবে Repository পরিবর্তন করতে পারি তা
দেখা যাক।
থ্রেড-সেফটির জন্য কোরাউটিন ব্যবহার করা
কোনও ফাংশন মূল থ্রেড-সেফ হলে, সেটি মূল থ্রেডে UI আপডেট ব্লক করে না।
makeLoginRequest ফাংশনটি মূল থ্রেড-সেফ নয়, কারণ মূল থ্রেড থেকে
makeLoginRequest কল করলে UI ব্লক হয়ে যায়। অন্য থ্রেডে কোরাউটিনের এক্সিকিউশন
সরিয়ে নিয়ে যেতে, কোরাউটিন লাইব্রেরি থেকে
withContext() ফাংশন ব্যবহার করুন:
class LoginRepository( // ... ) { // ... suspend fun makeLoginRequest( jsonBody: String ): Result<LoginResponse> { // Move the execution of the coroutine to the I/O dispatcher return withContext(Dispatchers.IO) { // Blocking network request code } } }
withContext(Dispatchers.IO) কোরাউটিনের এক্সিকিউশনকে একটি
I/O থ্রেডে সরিয়ে দেয়, এর ফলে আমাদের কলিং ফাংশনটি মেইন-সেফ হয়ে যায় এবং প্রয়োজনমতো UI-কে
আপডেট করতে পারে।
makeLoginRequest-এ suspend কীওয়ার্ডও আছে। এই কীওয়ার্ড
হল কোটলিনের এমন একটি উপায় যার মাধ্যমে কোনও ফাংশনকে কোরাউটিনের মধ্যে থেকে কল করার জন্য বাধ্য করা হয়।
নিচের উদাহরণে, কোরাউটিনটি LoginViewModel-এ তৈরি করা হয়েছে।
makeLoginRequest মূল থ্রেড থেকে এক্সিকিউশন সরিয়ে দেয় বলে, login ফাংশনের মধ্যে থাকা কোরাউটিন
এখন মূল থ্রেডে এক্সিকিউট করা যেতে পারে:
class LoginViewModel( private val loginRepository: LoginRepository ) : ViewModel() { fun login(username: String, token: String) { // Create a new coroutine on the UI thread viewModelScope.launch { val jsonBody = "{ username: \"$username\", token: \"$token\"}" // Make the network call and suspend execution until it finishes val result = loginRepository.makeLoginRequest(jsonBody) // Display result of the network request to the user when (result) { is Result.Success<LoginResponse> -> { /* Happy path */ } else -> { /* Show error in UI */ } } } } }
মনে রাখবেন, এখানে এখনও কোরাউটিনের প্রয়োজন আছে, কারণ makeLoginRequest হল
একটি suspend ফাংশন এবং সব suspend ফাংশন অবশ্যই কোরাউটিনে
এক্সিকিউট করতে হবে।
এই কোডটি আগের login উদাহরণ থেকে কয়েকটি উপায়ে আলাদা:
launch-এDispatchers.IOপ্যারামিটার ব্যবহার করা যায় না। আপনিlaunch-এ কোনওDispatcherপাস না করলে,viewModelScopeথেকে লঞ্চ করা যেকোনও কোরাউটিন মূল থ্রেডে রান করে।- নেটওয়ার্ক অনুরোধের ফলাফল এখন সাকসেস বা ব্যর্থতার UI দেখানোর জন্য ম্যানেজ করা হয়।
লগ-ইন ফাংশন এখন এইভাবে এক্সিকিউট হয়:
- অ্যাপটি মূল থ্রেডে
Viewলেয়ার থেকেlogin()ফাংশন কল করে। launchমূল থ্রেডে একটি নতুন কোরাউটিন তৈরি করে এবং কোরাউটিনটি এক্সিকিউট করা শুরু করে।- করুটিনের মধ্যে,
loginRepository.makeLoginRequest()now সাসপেন্ড করার কলটিwithContextblock inmakeLoginRequest()রান করা শেষ না হওয়া পর্যন্ত করুটিনের পরবর্তী এক্সিকিউশন সাসপেন্ড করে। withContextব্লক শেষ হয়ে গেলে, নেটওয়ার্ক অনুরোধের ফলাফল সহlogin()-এর কোরাউটিন প্রধান থ্রেডে এক্সিকিউশন আবার শুরু হয়।
ব্যতিক্রম ম্যানেজ করা
Repository লেয়ার থেকে পাওয়া ব্যতিক্রম হ্যান্ডেল করতে, Kotlin-এর
বিল্ট-ইন ব্যতিক্রম সংক্রান্ত সহায়তা ব্যবহার করুন।
নিচের উদাহরণে, আমরা একটি try-catch ব্লক ব্যবহার করেছি:
class LoginViewModel( private val loginRepository: LoginRepository ) : ViewModel() { fun login(username: String, token: String) { viewModelScope.launch { val jsonBody = "{ username: \"$username\", token: \"$token\"}" val result = try { loginRepository.makeLoginRequest(jsonBody) } catch (e: Exception) { Result.Error(Exception("Network request failed")) } when (result) { is Result.Success<LoginResponse> -> { /* Happy path */ } else -> { /* Show error in UI */ } } } } }
এই উদাহরণে, makeLoginRequest()
কলের মাধ্যমে তৈরি হওয়া যেকোনও অপ্রত্যাশিত ব্যতিক্রমকে UI-তে একটি সমস্যা হিসেবে ম্যানেজ করা হয়।
অতিরিক্ত কোরাউটিন রিসোর্স
Android-এ কোরাউটিন সম্পর্কে আরও বিস্তারিত জানতে, Kotlin কোরাউটিনের মাধ্যমে অ্যাপের পারফর্ম্যান্স উন্নত করুন দেখুন।
আরও কোরাউটিন রিসোর্সের জন্য, নিম্নলিখিত লিঙ্ক দেখুন: