آموزش کوروتین ها در کامپوز و اندروید


در این مطلب به بررسی استفاده از کوروتینها در Jetpack Compose میپردازیم و نکات کلیدی برای استفادهی صحیح از آنها را بیان میکنیم.
Jetpack Compose به عنوان کتابخانهی مدرن رابط کاربری اندروید، طراحی UI را ساده و واکنشی کرده است. اما در دنیای توسعهی اندروید، تقریباً همیشه با کارهای ناهمزمان (Asynchronous) سر و کار داریم؛ مانند فراخوانی API، خواندن و نوشتن دیتابیس، یا انجام محاسبات سنگین. در اینجا کوروتینها (Coroutines) وارد میشوند تا مدیریت این عملیاتها را سادهتر و امنتر کنند.
قبل از شروع اگر درمورد این کوروتین ها اطلاعاتی ندارید ، میتونید به عنوان آموزش مقدماتی اینجا رو مطالعه کنید
1. چرا از کوروتینها استفاده میکنیم؟
قبل از معرفی کاربرد در Compose، اجازه دهید به مزایای کوروتینها نگاه کنیم:
- کاهش پیچیدگی کد: بدون کوروتینها معمولاً از Callbacks یا RxJava برای مدیریت عملیاتهای ناهمزمان استفاده میکنیم که کد را پیچیده و سختخوان میکند.
- خوانایی بهتر: با کوروتینها، کد ناهمزمان شبیه کد همزمان میشود و استفاده از `suspend` و `async/await` خوانایی را افزایش میدهد.
- مدیریت بهتر منابع: کوروتینها به شما امکان میدهند عملیاتهای طولانی را بدون مسدود کردن Thread اصلی انجام دهید و از ترکیدن Threadها جلوگیری کنید .😁
- همکاری با Lifecycle: با ترکیب `ViewModel` و `CoroutineScope` میتوان عملیاتهای ناهمزمان را با چرخه عمر Activity یا Composable هماهنگ کرد.
2. ساختار پایهی کوروتین در Compose
در Compose، ما معمولاً از `LaunchedEffect` و `rememberCoroutineScope` برای مدیریت کوروتینها استفاده میکنیم.
2.1 LaunchedEffect
`LaunchedEffect` یک Composable است که به محض وارد شدن به Composition، یک Coroutine را راهاندازی میکند و به صورت خودکار با ترک Composable لغو میشود.
@Composable
fun GreetingScreen(viewModel: MyViewModel) {
val greeting by viewModel.greeting.collectAsState()
LaunchedEffect(Unit) {
viewModel.loadGreeting()
}
Text(text = greeting)
}
- `LaunchedEffect(Unit)` یعنی این کوروتین یک بار اجرا شود.
- وقتی Composable از Composition خارج شود، کوروتین متوقف میشود و دیگر نیازی به لغو دستی نیست.
2.2 rememberCoroutineScope
گاهی میخواهیم یک کوروتین را با یک رویداد کاربر راهاندازی کنیم، مثل کلیک روی دکمه:
@Composable
fun LoadDataButton(viewModel: MyViewModel) {
val scope = rememberCoroutineScope()
Button(onClick = {
scope.launch {
viewModel.loadData()
}
}) {
Text("Load Data")
}
}
- `rememberCoroutineScope()` یک `CoroutineScope` متصل به Composition برمیگرداند.
- با ترک Composable، تمام کوروتینهای این scope لغو میشوند.
3. استفاده از ViewModel و StateFlow
در Compose، بهتر است منطق برنامه یا تصمیم گیری درمورد نمایش داده (Business Logic) در ViewModel باشد و UI فقط State را مصرف کند یا بهتر بگوییم نمایش میدهد . این کار باعث جدا شدن واضح UI و دادهها میشود.
class MyViewModel: ViewModel() {
private val _greeting = MutableStateFlow("Loading...")
val greeting: StateFlow<String> = _greeting
fun loadGreeting() {
viewModelScope.launch {
delay(2000) // شبیهسازی عملیات طولانی
_greeting.value = "Hello from Coroutine!"
}
}
}
- `viewModelScope` یک CoroutineScope است که با ViewModel زندگی میکند و وقتی ViewModel نابود شود، کوروتینها لغو میشوند.
- استفاده از `StateFlow` یا `LiveData` باعث میشود Compose به طور خودکار UI را بروزرسانی کند.
4. مدیریت چند عملیات همزمان
کوروتینها اجازه میدهند عملیاتهای همزمان با `async` و `await` اجرا شوند:
fun loadMultipleData() {
viewModelScope.launch {
val data1Deferred = async { fetchData1() }
val data2Deferred = async { fetchData2() }
val result1 = data1Deferred.await()
val result2 = data2Deferred.await()
// حالا میتوانیم StateFlow را بروزرسانی کنیم
}
}
- `async` عملیات را همزمان اجرا میکند.
- `await` نتیجه را برمیگرداند و تا پایان عملیات صبر میکند.
5. Handling Exceptions
در Compose و ViewModel باید خطاهای کوروتینها را مدیریت کنیم. `try/catch` یا `CoroutineExceptionHandler` گزینههای معمول هستند.
viewModelScope.launch {
try {
val data = fetchDataFromApi()
_state.value = data
} catch (e: IOException) {
_state.value = "Error loading data"
}
}
- مدیریت خطا در ViewModel باعث میشود UI بتواند پیغام مناسب نمایش دهد.
6. ترکیب با Compose State
در Compose، ما از State برای بروزرسانی UI استفاده میکنیم. ترکیب با کوروتین باعث میشود عملیات ناهمزمان بدون مسدود کردن Thread اصلی انجام شود.
@Composable
fun DataScreen(viewModel: MyViewModel) {
val data by viewModel.data.collectAsState(initial = emptyList())
LaunchedEffect(Unit) {
viewModel.loadData()
}
LazyColumn {
items(data) { item ->
Text(item)
}
}
}
- `collectAsState` باعث میشود هر تغییر در StateFlow یا LiveData باعث رفرش UI شود. این الگو بسیار رایج و استاندارد در Compose است.
7. بهترین روشها (Best Practices)
1. همیشه از ViewModel برای عملیات طولانی استفاده کنید: این کار باعث مدیریت بهتر چرخه عمر میشود.
2. از LaunchedEffect برای Side Effect ها استفاده کنید: برای مثال، فراخوانی API هنگام شروع Composable.
3. از rememberCoroutineScope برای رویدادهای کاربر استفاده کنید: مثل کلیک، Scroll یا Swipe.
4. از StateFlow یا LiveData برای State نگهداری استفاده کنید: تا UI خودکار بروزرسانی شود.
5. مدیریت خطا را فراموش نکنید: هیچ چیز بدتر از کرش اپلیکیشن هنگام قطع اینترنت نیست!
6. از Dispatcher مناسب استفاده کنید: برای مثال، `Dispatchers.IO` برای عملیات شبکه یا دیتابیس، `Dispatchers.Main` برای بروزرسانی UI.
نتیجهگیری
استفاده از کوروتینها در Jetpack Compose باعث سادهتر شدن مدیریت عملیات ناهمزمان، خوانایی بیشتر کد و کاهش مشکلات مرتبط با چرخه عمر میشود. با ترکیب ViewModel، StateFlow و LaunchedEffect یا rememberCoroutineScope میتوان اپلیکیشنهایی نوین، واکنشی و مقیاسپذیر نوشت.
با رعایت بهترین روشها، میتوان از خطاهای رایج مثل مسدود کردن UI، نشت حافظه و کرش هنگام تغییر چرخه عمر جلوگیری کرد. در نهایت، ترکیب:
Compose + Coroutines + ViewModel به توسعهدهنده این امکان را میدهد که به راحتی تجربه کاربری روان و عملکرد بالا ارائه دهد.
در اخر ، امیدوارم که این که این مطلب دید تازه ای از استفاده کوروتین ها به شما بده از کد زدن لذت ببرید 🌸❤️







