بررسی انواع Kotlin Flow

قبل از شروع بد نیست این قسمت را بخوانید (یعنی بخوانید کلا)

در قسمت قبلی Flow رو به صورت کلی معرفی کردیم :

در کاتلین نوع جدیدی رو به شکل استریمی (جریانی) از داده ها داریم که به اون Flow می‌گیم، Flow که به معنی شناور بودن هست در واقع شامل یک Emitter (ارسال کننده) و یک Collector (دریافت کننده) هست
https://gist.github.com/sasssass/847e1a3b4301474ee88c5b505f6e9f0d

دو نوع برای Flow داریم :

Cold stream Flow

در این نوع ارسال کننده فقط وقتی عملیات emit رو انجام میده که یک دریافت کننده بهش متصل شده باشه

Hot stream Flow

در این نوع ارسال کننده کاری نداره که دریافت کننده بهش وصله یا نه، در هر صورت عملیات emit رو انجام میده (مثلا شما اگه یه تایمر داشته باشی تایمره کاری نداره که تو داری نگاش میکنی یا نه، اون تایم خودشو حساب میکنه و جلو میره)

در واقع این دسته بندی برای Flow نیست بلکه کلا برای Observable هاست

نوع Cold یعنی Flow رو به صورت عادی استفاده کنیم (Flow) و نوع Hot زمانی اتفاق میفته که از SharedFlow یا StateFlow استفاده بشه .

یک نمونه از SharedFlow ببینیم و بعدش توضیحاتی در موردش بدیم :

https://gist.github.com/sasssass/5e0add356a6eed8ae0bc1df5f7f40742

خب در اینجا چند نکته هست، اول اینکه SharedFlow به صورت Hot Stream هست، اگه دقت کنید ما دو بار emit انجام می‌دیم ولی فقط دومی collect میشه، به خاطر اینکه emit اولی قبل از collect صورت گرفته و چون به حالت Hot هست به هر حال emit انجام میشه ولی چون دیرتر collect شده دریافت نمیشه ولی دومی چون بعد از collect بوده collect میشه، دومین مورد اینه که یک Job تعریف کردیم و اون رو بعد از collect کنسل کردیم، چون اگه این کار رو نکنیم collect به صورت مداوم انجام میشه و برنامه هیچوقت تموم نمیشه (اگه از Scope دیگه ای به جز runBlocking استفاده می‌کردیم قضیه فرق می‌کرد)، سومین نکته در کلاس SharedFlowHelper هست که یک نوع MutableSharedFlow رو به صورت Private تعریف کردیم و یک نوع SharedFlow رو به صورت Public بهش متصل کردیم، دلیل اینکار اینه که عملیات emit باید درون خود SharedFlowHelper با استفاده از MutableSharedFlow صورت بگیره و باقی کلاس نباید بتونن emit رو به صورت مستقیم انجام بدن، SharedFlow عادی هم قابلیت emit نداره و فقط دریاف می‌کنه (این best practice خود گوگل هست)

حالا یک نمونه از StateFlow بببینیم :

https://gist.github.com/sasssass/0a80a9c1827915c4678b5c0fc2dec19a

و نتیجه :

خب اینجا کمی وضعیت فرق کرد، تا حد زیادی شباهت به SharedFlow می‌بینیم اما چرا emit شماره یک هم چاپ شده؟ به کلمه دقت کنید State + Flow یعنی آخرین State همیشه درش باقی می‌مونه، بنابراین وقتی ما یک بار emit انجام بدیم و بعدش collect کنیم، آخرین مقداری که درون StateFlow وجود داشته collect میشه (برعکس SharedFlow) و یک نکته مهم هم اینکه StateFlow رو حتما باید مقدار دهی اولیه کنید، مثلا اینجا ما Null رو به عنوان مقدار اولیه قرار دادیم که باز هم به مفهوم State برمی‌گرده چون من باید یک State اولیه داشته باشم .

حالا سوال اینجاست که چه زمانی از StateFlow استفاده کنیم و چرا از SharedFlow استفاده نکنیم ؟

جوابش ساده است، هر وقتی که به مفهوم State نیاز دارید از StateFlow استفاده می‌کنیم، مثلا یکی از مواردش dataBinding در Android هست، به این عکس نگاه کنید :

یکی از best practice ها پاس دادن ViewModel به dataBinding هست، در اینجا ما این کار رو کردیم و بعد یک مدل دیتا به صورت زیر درون ViewModel تعریف کردیم :

که این مدل دیتا یک پارامتر fullName داره که به صورت String هست، ما در dataBinding حق استفاده از SharedFlow رو نداریم چون متریال هامون حالت State دارن پس باید از StateFlow استفاده کنیم (یه پروژه سمپل بسازید و کمی با این مفاهیم بازی کنید تا بهتر متوجه بشید) .

یک مقدار هم در مورد اپراتورهای Flow صحبت کنیم ، اول در مورد اپراتور های محاسباتی صحبت می‌کنیم که می‌تونید SimpleFlowOperators از این گیت ببینید .

در این کد کامنت گذاری کردم و مشخصه هر اپراتور چه کار میکنه اما بذارید در مورد فرق transform و map صحبت کنیم، وقتی map انجام می‌دیم صرفا نوع تایپمون رو عوض می‌کنیم اما تو transform ما دوباره داریم عملیات emit رو override می‌کنیم و می‌تونیم به جای یک بار چندین بار با چندین تایپ emit انجام بدیم، در مورد reduce هم که معنی لغویش به معنی کم کردنه این شکلیه که این اپراتور میاد مثل یک تابع بازگشتی مقادیر flow ی شما رو هی در یک لامبدا بهتون پاس میده، بدنه لامبدا دقیقا مثل یک تابع بازگشتیه، مقدار اولش مقادیر محاسبه شده قبلیه و مقدار اولش مقدار فعلی، این اپراتور برای اینه که شما بیای و کل چیزایی که قراره emit بشه رو مختصر در یک value داشته باشی.

یک سری اپراتور دیگه هم داریم که مربوط به عملیات collect میشن که می‌تونید CollectionFlowOperator رو توی این گیت ببینید.

برای این اپراتورها هم اونجا کامنت کذاشتم و result هر اپراتور رو هم می‌تونید ببینید تا کامل متوجه بشید داستانشون چیه اما فقط یک نکته ای هم اینجا بگم، وقتی از flatMap (حالا هر نوعش) استفاده می‌کنید کاری که می‌کنه اینه که هر آیتم در ردیف A رو در کل ردیف B ادغام میکنه پس نهایتا سایز مقادیر شما میشه n(A) x n(B) البته به جز flatMapLatest که اونم اگه زمان بندیش درست باشه باز همین اتفاق میفته، اما در zip و combine هدف این نیست بلکه معمولا سایز مقادیر میشه یا سایز A یا سایز B چون اونجا هدف ادغام کل با کل نیست، بلکه مثلا هی یه دونه از A میگیره با یه دونه از B و بعد ترکیبشون میکنه و بعد از اون هم A و هم B رو میبره جلو و میره سراغ دونه بعدی از A و دونه بعدی از B (البته نه لزوما که بازم با توجه به زمان بندی ممکنه مثلا 3 تا از A یهو skip بشه، توضیحات فایل کامله و ریزالتم مشخص شده و بعید بدونم مشکلی پیدا کنید)

از این لینک می‌تونید به یک پروژه کوچولو در گیت دسترسی پیدا کنید که همه مثال ها اونجا نوشته شده.

آدرس کانال تلگرامی ما : لینک

من رو در لینکدین ، اینستاگرام و یوتیوب دنبال کنید !!!

اگه دوست داشتید می‌تونید به صفحه Spotify و SoundCloud بنده هم برید و موسیقی های منو گوش بدید !!!