,

آموزش استفاده از Channel در کاتلین برای مدیریت رویدادهای سراسری (Global Events)

آموزش استفاده از Channel در کاتلین برای مدیریت رویدادهای سراسری (Global Events)

تا حالا شده توی برنامه‌نویسی اندروید یا کاتلین مولتی‌پلتفرم (KMP) به این مشکل بخورید که بخواید یه پیغام یا دیالوگ رو از هرجای اپلیکیشن نشون بدید؟

مثلاً تو بازی، قلبت تموم می‌شه و باید پیام “بازی تموم شد” بیاد، فرقی هم نمی‌کنه تو کدوم صفحه‌ای باشی! یا توی یه اپلیکیشن حسابداری، اعتبارت تموم می‌شه و باید یه دیالوگ بیاد که بگه “لطفاً اشتراک بخر”.

کپی کردن کد دیالوگ توی همه صفحه‌ها کار درستی نیست! اینجا Channelهای کاتلین میان وسط و کل ماجرا رو نجات میدن!

چنل در کاتلین

چنل (Channel) دقیقاً چیه؟

چنل‌ها توی کاتلین کوروتین (Kotlin Coroutines) یه چیزی شبیه لوله‌های پستی قدیمی هستن! شما یه پیام رو از یه کوروتین (مثلاً ViewModel) می‌فرستی تو این لوله، و اون سرش تو یه کوروتین دیگه (مثلاً MainActivity) پیامت رو تحویل می‌گیری. کارشون خیلی ساده است: ارتباط امن و منظم بین بخش‌های مختلف برنامه.

یه مرور سریع بر انواع Channel

چنل‌ها مدل‌های مختلفی دارن، ولی کاربردی‌ترین‌هاشون اینان:

  • Rendezvous (پیش‌فرض): فرستنده و گیرنده باید همزمان آماده باشن. اگه نباشن، پیام ممکنه بره هوا!
  • Buffered (پُر کاربردترین!): یه صف انتظار کوچیک داره (مثلاً ۶۴ تا پیام). اگه گیرنده سرش شلوغ باشه، پیام‌ها موقتاً توی صف می‌مونن. ما از همین استفاده می‌کنیم.
  • Conflated: تنبله! همیشه فقط آخرین پیامی که اومده رو نگه می‌داره و بقیه رو پرت می‌کنه بیرون.

پیاده‌سازی گام‌به‌گام

برای اینکه کارمون تمیز و حرفه‌ای باشه، از Hilt (سیستم تزریق وابستگی) استفاده می‌کنیم تا مطمئن بشیم فقط “یک” چنل توی کل اپلیکیشن داریم.

۱. تعریف رویدادها (اتفاقاتی که می‌افتند)

اول یه Sealed Class می‌سازیم که لیست تمام رویدادهای احتمالی رو نگه داره. اینجوری نوع پیام‌ها مشخصه.

Kotlin

// AppEvent.kt
sealed class AppEvent {
    // برای نشون دادن یه Toast یا Snackbar ساده
    data class MessageEvent(val message: String) : AppEvent()
    
    // برای نشون دادن یه دیالوگ کامل
    data class DialogEvent(val title: String, val description: String) : AppEvent()
    
    // اگه خواستید یه رویداد دیگه اضافه کنید، جاش اینجاست!
}

۲. تنظیمات Hilt برای Channel (فقط یک‌بار)

ما توی ماژول Hilt، چنلمون رو به صورت Singleton (تک‌ساز) تعریف می‌کنیم تا همه جای برنامه بهش دسترسی داشته باشن.

// AppModule.kt
@Module
@InstallIn(SingletonComponent::class)
object AppModule {

    @Provides
    @Singleton
    fun provideAppChannel(): Channel<AppEvent> {
        // از BUFFERED استفاده می‌کنیم تا مطمئن بشیم پیام‌ها رو از دست نمیدیم.
        return Channel(capacity = Channel.BUFFERED)
    }
}

۳. ارسال پیام از ViewModel (فرستنده)

حالا توی هر ViewModel که هستید، کافیه چنل رو تزریق کنید و با متد send پیامتون رو بفرستید. یادتون باشه send یه تابع suspend هست و باید داخل یه Coroutine Scope اجرا بشه.

// LoginViewModel.kt
@HiltViewModel
class LoginViewModel @Inject constructor(
    private val appChannel: Channel<AppEvent> // تزریق شد، آماده برای ارسال
) : ViewModel() {

    fun onLoginClicked() {
        viewModelScope.launch {
            // ... عملیات لاگین انجام شد و همه چی اوکیه
            
            // حالا پیام رو به سمت UI می‌فرستیم:
            appChannel.send(AppEvent.MessageEvent("وارد شدی! خوش اومدی."))
            
            // یا مثلاً یک دیالوگ:
            // appChannel.send(AppEvent.DialogEvent("توجه!", "اعتبار شما به پایان رسیده است."))
        }
    }
}

۴. دریافت و نمایش پیام در UI (گیرنده)

توی MainActivity (یا کامپوزبل اصلی برنامه‌تون) باید به این لوله‌ی پستی گوش بدید.

نکته فنی: چنل رو به صورت Flow دریافت می‌کنیم تا بتونیم با collect کردن، جریان پیام‌ها رو بگیریم. این کار توی LaunchedEffect انجام میشه که با چرخه حیات (Lifecycle) کامپوز همراه باشه.

// MainActivity.kt (یا AppComposable اصلی)
@AndroidEntryPoint
class MainActivity : ComponentActivity() {

    @Inject
    lateinit var appChannel: Channel<AppEvent> // همون چنل سینگلتون

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val context = LocalContext.current
            
            // گوش دادن به چنل توی یک LaunchedEffect
            LaunchedEffect(key1 = true) {
                // تبدیل چنل به Flow و جمع‌آوری رویدادها
                appChannel.receiveAsFlow().collect { event ->
                    when (event) {
                        is AppEvent.MessageEvent -> {
                            // اگه پیام ساده بود، یه Toast نشون بده
                            Toast.makeText(context, event.message, Toast.LENGTH_LONG).show()
                        }
                        is AppEvent.DialogEvent -> {
                            // اگه دیالوگ بود، مثلاً اینجا کد نمایش دیالوگ رو می‌ذاریم
                            Log.d("AppEvent", "باید دیالوگ: ${event.title} نمایش داده شود.")
                        }
                    }
                }
            }

            // اینجا UI اصلی برنامه اجرا می‌شه (مثلاً LoginScreen)
            MyApplicationTheme {
                LoginScreen()
            }
        }
    }
}

جمع‌بندی

با این ساختار، ViewModel شما دیگه هیچ‌وقت نگران نیست که پیامش چطوری و کجا قراره نمایش داده بشه. فقط کافیه پیامش رو بندازه توی چنل، و UI (که همیشه بیدار و منتظره!) اونو می‌گیره و نمایش میده. این یعنی یک معماری تمیزتر، جداشدگی بهتر و کد قابل نگهداری‌تر.

Comments

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *