Kotlin coroutines একটি API প্রদান করে যা আপনাকে অ্যাসিঙ্ক্রোনাস কোড লিখতে সক্ষম করে। Kotlin coroutines-এর সাহায্যে, আপনি একটি CoroutineScope
সংজ্ঞায়িত করতে পারেন, যা আপনাকে কখন আপনার coroutines চালানো উচিত তা পরিচালনা করতে সাহায্য করে। প্রতিটি অ্যাসিঙ্ক্রোনাস অপারেশন একটি নির্দিষ্ট সুযোগের মধ্যে চলে।
লাইফসাইকেল-সচেতন উপাদানগুলি LiveData
সহ একটি আন্তঃঅপারেবিলিটি স্তর সহ আপনার অ্যাপে লজিক্যাল স্কোপের জন্য কোরোটিনগুলির জন্য প্রথম-শ্রেণীর সহায়তা প্রদান করে। এই বিষয় ব্যাখ্যা করে যে কীভাবে জীবনচক্র-সচেতন উপাদানগুলির সাথে কার্যকরভাবে কোরোটিনগুলি ব্যবহার করতে হয়।
KTX নির্ভরতা যোগ করুন
এই বিষয়ে বর্ণিত অন্তর্নির্মিত করোটিন স্কোপগুলি প্রতিটি সংশ্লিষ্ট উপাদানের জন্য KTX এক্সটেনশনগুলিতে রয়েছে। এই সুযোগগুলি ব্যবহার করার সময় উপযুক্ত নির্ভরতা যোগ করতে ভুলবেন না।
-
ViewModelScope
এর জন্য,androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0
বা উচ্চতর ব্যবহার করুন। -
LifecycleScope
এর জন্য,androidx.lifecycle:lifecycle-runtime-ktx:2.4.0
বা উচ্চতর ব্যবহার করুন। -
liveData
এর জন্য,androidx.lifecycle:lifecycle-livedata-ktx:2.4.0
বা উচ্চতর ব্যবহার করুন।
জীবনচক্র-সচেতন করোটিন স্কোপ
জীবনচক্র-সচেতন উপাদানগুলি নিম্নলিখিত অন্তর্নির্মিত স্কোপগুলিকে সংজ্ঞায়িত করে যা আপনি আপনার অ্যাপে ব্যবহার করতে পারেন৷
মডেলস্কোপ দেখুন
আপনার অ্যাপে প্রতিটি ViewModel
জন্য একটি ViewModelScope
সংজ্ঞায়িত করা হয়েছে। ViewModel
সাফ করা হলে এই সুযোগে চালু করা যেকোন কোরোটিন স্বয়ংক্রিয়ভাবে বাতিল হয়ে যায়। Coroutines এখানে উপযোগী যখন আপনার কাজ থাকে যা শুধুমাত্র ViewModel
সক্রিয় থাকলেই করা দরকার। উদাহরণস্বরূপ, যদি আপনি একটি লেআউটের জন্য কিছু ডেটা গণনা করেন, তাহলে আপনার কাজটি ViewModel
এর জন্য স্কোপ করা উচিত যাতে যদি ViewModel
সাফ করা হয়, তাহলে সম্পদ গ্রহণ এড়াতে কাজটি স্বয়ংক্রিয়ভাবে বাতিল হয়ে যায়।
আপনি ViewModel-এর viewModelScope
প্রপার্টির মাধ্যমে একটি ViewModel
এর CoroutineScope
অ্যাক্সেস করতে পারেন, যেমনটি নিম্নলিখিত উদাহরণে দেখানো হয়েছে:
class MyViewModel: ViewModel() {
init {
viewModelScope.launch {
// Coroutine that will be canceled when the ViewModel is cleared.
}
}
}
লাইফসাইকেলস্কোপ
প্রতিটি Lifecycle
অবজেক্টের জন্য একটি LifecycleScope
সংজ্ঞায়িত করা হয়। Lifecycle
ধ্বংস হয়ে গেলে এই সুযোগে চালু করা যেকোন কোরুটিন বাতিল হয়ে যায়। আপনি lifecycle.coroutineScope
বা lifecycleOwner.lifecycleScope
বৈশিষ্ট্যের মাধ্যমে Lifecycle
CoroutineScope
অ্যাক্সেস করতে পারেন।
নিচের উদাহরণটি দেখায় কিভাবে lifecycleOwner.lifecycleScope
ব্যবহার করে অসিঙ্ক্রোনাসভাবে প্রি-কম্পিউটেড টেক্সট তৈরি করতে হয়:
class MyFragment: Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycleScope.launch {
val params = TextViewCompat.getTextMetricsParams(textView)
val precomputedText = withContext(Dispatchers.Default) {
PrecomputedTextCompat.create(longTextContent, params)
}
TextViewCompat.setPrecomputedText(textView, precomputedText)
}
}
}
পুনঃসূচনাযোগ্য লাইফসাইকেল-সচেতন কোরোটিন
যদিও Lifecycle
DESTROYED
হয়ে গেলে lifecycleScope
স্বয়ংক্রিয়ভাবে দীর্ঘ-চলমান ক্রিয়াকলাপগুলিকে বাতিল করার একটি সঠিক উপায় প্রদান করে, আপনার অন্যান্য ক্ষেত্রে হতে পারে যেখানে আপনি একটি কোড ব্লক কার্যকর করতে চান যখন Lifecycle
একটি নির্দিষ্ট অবস্থায় থাকে এবং যখন এটি বাতিল হয় অন্য রাজ্যে আছে। উদাহরণস্বরূপ, Lifecycle
STARTED
হলে আপনি একটি প্রবাহ সংগ্রহ করতে চাইতে পারেন এবং এটি STOPPED
গেলে সংগ্রহটি বাতিল করতে পারেন৷ এই পদ্ধতিটি শুধুমাত্র তখনই প্রবাহ নির্গমন প্রক্রিয়া করে যখন UI স্ক্রিনে দৃশ্যমান হয়, সম্পদ সংরক্ষণ করে এবং সম্ভাব্যভাবে অ্যাপ ক্র্যাশ এড়ানো যায়।
এই ক্ষেত্রে, Lifecycle
এবং LifecycleOwner
সাসপেন্ড repeatOnLifecycle
API প্রদান করে যা ঠিক সেই কাজটি করে। নিম্নলিখিত উদাহরণে একটি কোড ব্লক রয়েছে যা প্রতিবার সংশ্লিষ্ট Lifecycle
কমপক্ষে STARTED
অবস্থায় থাকে এবং Lifecycle
STOPPED
হয়ে গেলে বাতিল হয়:
class MyFragment : Fragment() {
val viewModel: MyViewModel by viewModel()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Create a new coroutine in the lifecycleScope
viewLifecycleOwner.lifecycleScope.launch {
// repeatOnLifecycle launches the block in a new coroutine every time the
// lifecycle is in the STARTED state (or above) and cancels it when it's STOPPED.
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
// Trigger the flow and start listening for values.
// This happens when lifecycle is STARTED and stops
// collecting when the lifecycle is STOPPED
viewModel.someDataFlow.collect {
// Process item
}
}
}
}
}
জীবনচক্র-সচেতন প্রবাহ সংগ্রহ
যদি আপনাকে শুধুমাত্র একটি একক প্রবাহে জীবনচক্র-সচেতন সংগ্রহ সম্পাদন করতে হয়, তাহলে আপনি আপনার কোডকে সহজ করতে Flow.flowWithLifecycle()
পদ্ধতি ব্যবহার করতে পারেন:
viewLifecycleOwner.lifecycleScope.launch {
exampleProvider.exampleFlow()
.flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.STARTED)
.collect {
// Process the value.
}
}
যাইহোক, যদি আপনাকে সমান্তরালভাবে একাধিক প্রবাহের উপর জীবনচক্র-সচেতন সংগ্রহ সম্পাদন করতে হয়, তাহলে আপনাকে অবশ্যই প্রতিটি প্রবাহকে বিভিন্ন কোরোটিনে সংগ্রহ করতে হবে। সেই ক্ষেত্রে, repeatOnLifecycle()
সরাসরি ব্যবহার করা আরও কার্যকর:
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
// Because collect is a suspend function, if you want to
// collect multiple flows in parallel, you need to do so in
// different coroutines.
launch {
flow1.collect { /* Process the value. */ }
}
launch {
flow2.collect { /* Process the value. */ }
}
}
}
লাইফসাইকেল-সচেতন coroutines স্থগিত
যদিও CoroutineScope
স্বয়ংক্রিয়ভাবে দীর্ঘ-চলমান ক্রিয়াকলাপগুলিকে বাতিল করার একটি সঠিক উপায় সরবরাহ করে, আপনার কাছে এমন অন্যান্য ক্ষেত্রে থাকতে পারে যেখানে আপনি একটি কোড ব্লক কার্যকর করা স্থগিত করতে চান যদি না Lifecycle
একটি নির্দিষ্ট অবস্থায় থাকে। উদাহরণস্বরূপ, একটি FragmentTransaction
চালানোর জন্য, আপনাকে অবশ্যই অপেক্ষা করতে হবে যতক্ষণ না Lifecycle
অন্তত STARTED
হয়। এই ক্ষেত্রে, Lifecycle
অতিরিক্ত পদ্ধতি প্রদান করে: lifecycle.whenCreated
, lifecycle.whenStarted
, এবং lifecycle.whenResumed
। Lifecycle
কমপক্ষে কাঙ্খিত অবস্থায় না থাকলে এই ব্লকগুলির মধ্যে যেকোনও কোরোটিন চালানো স্থগিত করা হয়।
নীচের উদাহরণে একটি কোড ব্লক রয়েছে যা শুধুমাত্র তখনই চলে যখন সংশ্লিষ্ট Lifecycle
কমপক্ষে STARTED
অবস্থায় থাকে:
class MyFragment: Fragment {
init { // Notice that we can safely launch in the constructor of the Fragment.
lifecycleScope.launch {
whenStarted {
// The block inside will run only when Lifecycle is at least STARTED.
// It will start executing when fragment is started and
// can call other suspend methods.
loadingView.visibility = View.VISIBLE
val canAccess = withContext(Dispatchers.IO) {
checkUserAccess()
}
// When checkUserAccess returns, the next line is automatically
// suspended if the Lifecycle is not *at least* STARTED.
// We could safely run fragment transactions because we know the
// code won't run unless the lifecycle is at least STARTED.
loadingView.visibility = View.GONE
if (canAccess == false) {
findNavController().popBackStack()
} else {
showContent()
}
}
// This line runs only after the whenStarted block above has completed.
}
}
}
কোন একটি when
পদ্ধতির মাধ্যমে করোটিন সক্রিয় থাকাকালীন Lifecycle
ধ্বংস হয়ে গেলে, করোটিনটি স্বয়ংক্রিয়ভাবে বাতিল হয়ে যায়। নীচের উদাহরণে, Lifecycle
অবস্থা DESTROYED
হয়ে গেলে finally
ব্লকটি চলে:
class MyFragment: Fragment {
init {
lifecycleScope.launchWhenStarted {
try {
// Call some suspend functions.
} finally {
// This line might execute after Lifecycle is DESTROYED.
if (lifecycle.state >= STARTED) {
// Here, since we've checked, it is safe to run any
// Fragment transactions.
}
}
}
}
}
লাইভডেটা দিয়ে কোরোটিন ব্যবহার করুন
LiveData
ব্যবহার করার সময়, আপনাকে অ্যাসিঙ্ক্রোনাসভাবে মান গণনা করতে হতে পারে। উদাহরণস্বরূপ, আপনি একজন ব্যবহারকারীর পছন্দগুলি পুনরুদ্ধার করতে এবং আপনার UI এ তাদের পরিবেশন করতে চাইতে পারেন৷ এই ক্ষেত্রে, আপনি একটি suspend
ফাংশন কল করতে liveData
বিল্ডার ফাংশন ব্যবহার করতে পারেন, ফলাফলটিকে একটি LiveData
অবজেক্ট হিসাবে পরিবেশন করতে পারেন।
নীচের উদাহরণে, loadUser()
হল একটি সাসপেন্ড ফাংশন যা অন্যত্র ঘোষণা করা হয়েছে। loadUser()
অ্যাসিঙ্ক্রোনাস কল করতে liveData
বিল্ডার ফাংশন ব্যবহার করুন এবং তারপর ফলাফল নির্গত করতে emit()
ব্যবহার করুন:
val user: LiveData<User> = liveData {
val data = database.loadUser() // loadUser is a suspend function.
emit(data)
}
liveData
বিল্ডিং ব্লক কোরোটিন এবং LiveData
এর মধ্যে একটি স্ট্রাকচার্ড কনকারেন্সি হিসেবে কাজ করে। LiveData
সক্রিয় হয়ে গেলে কোড ব্লক কার্যকর করা শুরু করে এবং LiveData
নিষ্ক্রিয় হয়ে গেলে কনফিগারযোগ্য সময়সীমার পরে স্বয়ংক্রিয়ভাবে বাতিল হয়ে যায়। এটি সম্পূর্ণ হওয়ার আগে বাতিল করা হলে, LiveData
আবার সক্রিয় হলে এটি পুনরায় চালু করা হয়। এটি পূর্ববর্তী রানে সফলভাবে সম্পন্ন হলে, এটি পুনরায় চালু হয় না। মনে রাখবেন যে এটি শুধুমাত্র স্বয়ংক্রিয়ভাবে বাতিল হলেই পুনরায় চালু হবে। যদি ব্লকটি অন্য কোন কারণে বাতিল করা হয় (যেমন একটি CancellationException
নিক্ষেপ করা), এটি পুনরায় চালু করা হয় না ।
আপনি ব্লক থেকে একাধিক মান নির্গত করতে পারেন। প্রতিটি emit()
কল মূল থ্রেডে LiveData
মান সেট না হওয়া পর্যন্ত ব্লকের কার্য সম্পাদন স্থগিত করে।
val user: LiveData<Result> = liveData {
emit(Result.loading())
try {
emit(Result.success(fetchUser()))
} catch(ioException: Exception) {
emit(Result.error(ioException))
}
}
আপনি Transformations
সাথে liveData
একত্রিত করতে পারেন, যেমনটি নিম্নলিখিত উদাহরণে দেখানো হয়েছে:
class MyViewModel: ViewModel() {
private val userId: LiveData<String> = MutableLiveData()
val user = userId.switchMap { id ->
liveData(context = viewModelScope.coroutineContext + Dispatchers.IO) {
emit(database.loadUserById(id))
}
}
}
আপনি যখনই একটি নতুন মান নির্গত করতে চান তখনই emitSource()
ফাংশন কল করে একটি LiveData
থেকে একাধিক মান নির্গত করতে পারেন। মনে রাখবেন যে emit()
বা emitSource()
এর প্রতিটি কল পূর্বে যোগ করা উৎসকে সরিয়ে দেয়।
class UserDao: Dao {
@Query("SELECT * FROM User WHERE id = :id")
fun getUser(id: String): LiveData<User>
}
class MyRepository {
fun getUser(id: String) = liveData<User> {
val disposable = emitSource(
userDao.getUser(id).map {
Result.loading(it)
}
)
try {
val user = webservice.fetchUser(id)
// Stop the previous emission to avoid dispatching the updated user
// as `loading`.
disposable.dispose()
// Update the database.
userDao.insert(user)
// Re-establish the emission with success type.
emitSource(
userDao.getUser(id).map {
Result.success(it)
}
)
} catch(exception: IOException) {
// Any call to `emit` disposes the previous one automatically so we don't
// need to dispose it here as we didn't get an updated value.
emitSource(
userDao.getUser(id).map {
Result.error(exception, it)
}
)
}
}
}
আরও কোরোটিন-সম্পর্কিত তথ্যের জন্য, নিম্নলিখিত লিঙ্কগুলি দেখুন:
- Kotlin coroutines দিয়ে অ্যাপের পারফরম্যান্স উন্নত করুন
- Coroutines ওভারভিউ
- CoroutineWorker মধ্যে থ্রেডিং
অতিরিক্ত সম্পদ
জীবনচক্র-সচেতন উপাদানগুলির সাথে কোরোটিনগুলি ব্যবহার সম্পর্কে আরও জানতে, নিম্নলিখিত অতিরিক্ত সংস্থানগুলির সাথে পরামর্শ করুন৷
নমুনা
ব্লগ
- অ্যান্ড্রয়েডের কোরাটিন: অ্যাপ্লিকেশন প্যাটার্ন
- অ্যান্ড্রয়েডে সহজ কোরোটিন: ভিউমডেলস্কোপ
- কোরোটিনে পরপর দুটি লাইভডেটা নির্গমন পরীক্ষা করা হচ্ছে
আপনার জন্য প্রস্তাবিত
- দ্রষ্টব্য: জাভাস্ক্রিপ্ট বন্ধ থাকলে লিঙ্ক টেক্সট প্রদর্শিত হয়
- লাইভডেটা ওভারভিউ
- লাইফসাইকেল-সচেতন উপাদানগুলির সাথে লাইফসাইকেল পরিচালনা করা
- পৃষ্ঠাযুক্ত ডেটা লোড এবং প্রদর্শন করুন