অ্যান্ড্রয়েডে কোটলিন কোরোটিন

একটি কোরাউটিন হল একটি কনকারেন্সি ডিজাইন প্যাটার্ন যা আপনি অ্যাসিঙ্ক্রোনাসভাবে কার্যকর করা কোডকে সহজ করতে Android এ ব্যবহার করতে পারেন। Coroutines সংস্করণ 1.3 এ Kotlin এ যোগ করা হয়েছে এবং অন্যান্য ভাষার প্রতিষ্ঠিত ধারণার উপর ভিত্তি করে তৈরি করা হয়েছে।

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

বৈশিষ্ট্য

Coroutines হল Android এ অ্যাসিঙ্ক্রোনাস প্রোগ্রামিংয়ের জন্য আমাদের প্রস্তাবিত সমাধান। উল্লেখযোগ্য বৈশিষ্ট্যগুলির মধ্যে নিম্নলিখিতগুলি অন্তর্ভুক্ত রয়েছে:

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

উদাহরণ ওভারভিউ

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

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

ViewModel KTX এক্সটেনশনের একটি সেট রয়েছে যা সরাসরি কোরোটিনের সাথে কাজ করে। এই এক্সটেনশনগুলি হল lifecycle-viewmodel-ktx লাইব্রেরি এবং এই গাইডে ব্যবহার করা হয়েছে৷

নির্ভরতা তথ্য

আপনার অ্যান্ড্রয়েড প্রোজেক্টে কোরোটিন ব্যবহার করতে, আপনার অ্যাপের 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>()
}

class LoginRepository(private val responseParser: LoginResponseParser) {
    private const val loginUrl = "https://example.com/login"

    // 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 ফাংশনে coroutines কোড বিচ্ছিন্ন করা যাক:

  • viewModelScope হল একটি পূর্বনির্ধারিত CoroutineScope যা ViewModel KTX এক্সটেনশনের সাথে অন্তর্ভুক্ত। মনে রাখবেন যে সমস্ত কোরোটিন অবশ্যই একটি সুযোগে চলবে। একটি CoroutineScope এক বা একাধিক সম্পর্কিত coroutines পরিচালনা করে।
  • launch হল একটি ফাংশন যা একটি করুটিন তৈরি করে এবং এর ফাংশন বডির এক্সিকিউশনকে সংশ্লিষ্ট প্রেরককে প্রেরণ করে।
  • Dispatchers.IO নির্দেশ করে যে I/O অপারেশনের জন্য সংরক্ষিত একটি থ্রেডে এই করুটিনটি কার্যকর করা উচিত।

login ফাংশনটি নিম্নরূপ সঞ্চালিত হয়:

  • অ্যাপটি মূল থ্রেডের View লেয়ার থেকে login ফাংশনকে কল করে।
  • launch একটি নতুন কোরোটিন তৈরি করে, এবং নেটওয়ার্ক অনুরোধটি I/O অপারেশনের জন্য সংরক্ষিত একটি থ্রেডে স্বাধীনভাবে করা হয়।
  • করুটিন চলাকালীন, login ফাংশনটি কার্যকর করা চালিয়ে যায় এবং রিটার্ন করে, সম্ভবত নেটওয়ার্ক অনুরোধ শেষ হওয়ার আগে। মনে রাখবেন যে সরলতার জন্য, নেটওয়ার্ক প্রতিক্রিয়া আপাতত উপেক্ষা করা হয়েছে।

যেহেতু এই coroutineটি viewModelScope দিয়ে শুরু হয়েছে, তাই এটি ViewModel এর সুযোগে কার্যকর করা হয়। ব্যবহারকারী স্ক্রীন থেকে দূরে নেভিগেট করার কারণে যদি ViewModel টি ধ্বংস হয়ে যায়, তাহলে viewModelScope স্বয়ংক্রিয়ভাবে বাতিল হয়ে যাবে এবং চলমান সমস্ত কোরোটিনও বাতিল হয়ে যাবে।

পূর্ববর্তী উদাহরণের সাথে একটি সমস্যা হল যে makeLoginRequest কল করা যেকোন কিছুকে স্পষ্টভাবে মূল থ্রেড থেকে এক্সিকিউশন সরানোর জন্য মনে রাখতে হবে। আসুন দেখি কিভাবে আমরা Repository পরিবর্তন করতে পারি আমাদের জন্য এই সমস্যার সমাধান করতে।

প্রধান নিরাপত্তার জন্য coroutines ব্যবহার করুন

আমরা একটি ফাংশনকে প্রধান-নিরাপদ বিবেচনা করি যখন এটি মূল থ্রেডে UI আপডেটগুলিকে ব্লক করে না। makeLoginRequest ফাংশন প্রধান-নিরাপদ নয়, কারণ মেইন থ্রেড থেকে makeLoginRequest কল করা UI ব্লক করে। একটি ভিন্ন থ্রেডে একটি coroutine কার্যকরী স্থানান্তর করতে coroutines লাইব্রেরি থেকে 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 থ্রেডে coroutine কার্যকর করে, আমাদের কলিং ফাংশনকে প্রধান-নিরাপদ করে এবং UI কে প্রয়োজন অনুযায়ী আপডেট করতে সক্ষম করে।

makeLoginRequest এছাড়াও suspend কীওয়ার্ড দিয়ে চিহ্নিত করা হয়েছে। এই কীওয়ার্ডটি হল কোটলিনের একটি কোরোটিনের মধ্যে থেকে কল করার জন্য একটি ফাংশন কার্যকর করার উপায়।

নিম্নলিখিত উদাহরণে, LoginViewModel এ coroutine তৈরি করা হয়েছে। যেহেতু makeLoginRequest এক্সিকিউশনকে মূল থ্রেড থেকে সরিয়ে দেয়, login ফাংশনের coroutine এখন মূল থ্রেডে কার্যকর করা যেতে পারে:

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
            }
        }
    }
}

মনে রাখবেন যে coroutine এখনও এখানে প্রয়োজন, যেহেতু makeLoginRequest একটি suspend ফাংশন, এবং সমস্ত suspend ফাংশন অবশ্যই একটি coroutine-এ কার্যকর করা উচিত।

এই কোডটি আগের login উদাহরণ থেকে কয়েকটি উপায়ে আলাদা:

  • launch একটি Dispatchers.IO প্যারামিটার নেয় না। আপনি যখন launch জন্য একটি Dispatcher পাস করেন না, তখন viewModelScope থেকে লঞ্চ করা যেকোন কোরোটিন মূল থ্রেডে চলে।
  • নেটওয়ার্ক অনুরোধের ফলাফল এখন সাফল্য বা ব্যর্থতার UI প্রদর্শন করতে পরিচালনা করা হয়।

লগইন ফাংশন এখন নিম্নরূপ সঞ্চালিত হয়:

  • অ্যাপটি মূল থ্রেডের View লেয়ার থেকে login() ফাংশনটিকে কল করে।
  • launch মূল থ্রেডে একটি নতুন coroutine তৈরি করে, এবং coroutine কার্যকর করা শুরু করে।
  • coroutine-এর মধ্যে, loginRepository.makeLoginRequest() এ করা কল এখন coroutine-এর আরও এক্সিকিউশন স্থগিত করে যতক্ষণ না makeLoginRequest() -এ withContext ব্লক চালু না হয়।
  • একবার withContext ব্লক শেষ হয়ে গেলে, login() -এ coroutine নেটওয়ার্ক অনুরোধের ফলাফলের সাথে মূল থ্রেডে পুনরায় এক্সিকিউশন শুরু করে।

ব্যতিক্রম হ্যান্ডলিং

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-তে একটি ত্রুটি হিসাবে পরিচালনা করা হয়।

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

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

আরও কোরোটিন সংস্থানগুলির জন্য, নিম্নলিখিত লিঙ্কগুলি দেখুন: