Skip to main content Link Menu Expand (external link) Document Search Copy Copied

6- کروتینز

قسمت 22

ما برای اینکه بتونیم کارهارو منظم و هماهنگ شده با هم انجام بدیم یکسری روش ها داریم که Synchronous و asynchronous جزء هموناست.

My image

Synchronous : سنکرون یعنی بصورت همزمانی، یعنی اگه چندین کار داشته باشیم، مثلاً کاربر همزمان هم دانلود کنه و هم بیاد یه بخشی رو پر کنه و … ، وقتی از Synchronous استفاده میکنیم میاد کارهای مختلف و وظایف مختلف رو بصورت پشت سر هم انجام میده، یعنی تا زمانی که وظیفه یا کار شماره یک تموم نشده باشه کار شماره دو شروع نمیشه و تا زمانی که کار شماره دو تموم نشه کار شماره سه شروع نمیشه و همینطور این چرخه ادامه پیدا میکنه.

همه ی اینا یه جوری بهم وابسته و به این میگن Synchronous سنکرون یعنی همه ی کارهایی که داره رو، پشت سر هم انجام میده.

Synchronous دو حالت داره:

1- Single thread یا تک نخی، 2- multi thread یا چند نخی

کارهای مختلف توی cpu توی نخ ها یا thread تردهای مختلف انجام میشه بخاطر اینکه کارهای ما سریعتر اتفاق بیفته، مثلاً توی یه خیابون که یه لاین بیشتر نداره بریم و ماشین جلوی ما فقط 50 تا بره و ما عجله داشته باشیم و بخوایم با سرعت بیشتر بریم، 100% با ماشین جلوی برخورد میکنیم و تا ماشین جلوی از این مسیر خارج نشه ما نمیتونیم با سرعت به مسیرمون ادامه بدیم و کلی معطل میشیم و این حالت شبیه به تک نخی هست و برای اینکه این اتفاق توی cpu نیفته، سازنده ها اومدن نخ ها یا thread تردهای مختلفی رو در نظر گرفتن، که cpu میاد کارهای مختلف رو، روی نخ ها یا thread های مختلف انجام میده، مثلاً اگه یه ماشین بخواد تو جاده با 50 تا سرعت بره و ما تو مسیر تندرو با 100 تا سرعت بریم و هیچ تداخلی نداریم و کارهای مربوط به خودمونو انجام میدیم.

My image

اگه Synchronous سنکرون به حالت تک نخی باشه باید همه ی کارها بصورت متوالی و پشت سر هم انجام بشه.

My image

 ولی اگه بصورت چند نخی باشه(Synchronous سنکرون همونه وفرقی با حالت تک نخی نداره) فقط اومده وظیفه شماره 1 رو در نخ شماره یک اجرا کرده و تا زمانی که 1 شروع نشه 2 شروع نمیشه و تا زمانی که 2 شروع نشه 3 شروع نمیشه و اینجا Synchronous سنکرون همون حالت پشت سرهم رو داره ولی با این تفاوت که بجای اینکه بیاد کل فشارو بندازه روی یک thread ، اومده کارها و وظایف مختلفو روی تردهای مختلف پیاده سازی کرده که بتونه فشار سیستم رو یکمی کمتر کنه.

بخاطر همین تردها توی cpu ساخته شدن که کارهای مختلف توی تردهای مختلف صورت بگیره و فشار کمتری بیاد.

My image

Asynchronous آسنکرون: که حالت بهتری نسبت به synchronous سنکرون هست.

My image

Asynchronous آسنکرون در حالت تک نخی یا Single Thread اومده همون وظایف قبلی رو در سلول های کوچیکتر تقسیم کرده یعنی اومده گفته یه قسمت از وظیفه ی 1 رو انجام بده بعد یه قسمت از 2 و یه قسمت از 3 و بعد دوباره گفته چند قسمت از 1 رو انجام بده و این وظایف رو بصورت نامنظم تقسیم بندی کرده که قابل تشخیص نیست اول کدوم وظیفه انجام بشه به اینجور کد زدن و عملیات میگن” ناهمزمانی “ یعنی ما میایم کدهارو بصورت ناهمزمانی انجام میدیم که این ها باهم اتفاق بیفتن و وابسته بهم نباشن که مثلاً اگه 1 اتفاق نیفتاد سراغ 2 نره و بعد 3 و … ، به اینصورت هست که از هرکدوم یه قسمت رو انجام میده تا بلاخره تموم بشه.

My image

Asynchronous آسنکرون در حالت چند نخی یا Multi Thread که همون وظایف اول و دوم و سوم رو دارن ولی با این تفاوت که دارن همزمان صورت میگیرن و هرکدوم بصورت همزمان دارن وظیفه خودشونو انجام میدن، حالا ممکن هرکدوم با سرعت های متفاوتی بیان کارهارو انجام بدن مثلاً 3 اول تموم بشه بعد وظیفه 1 و بعد 2، و بین خود نخ ها هیچ تفاوتی از نظر سرعت وجود نداره که مثلاً 1 از 3 کندتر باشه و فقط به بخش های مختلفی تقسیم شده که کارهای ما سریعتر اتفاق بیفته.

چرا به Multi Threading یا چند نخی نیاز داریم: بخاطر اینکه کارهامون سریعتر اتفاق بیفتن و همه ی فشار روی یه ترد نباشه که به سخت افزار فشار بیشتری بیاد و همه کارها بهم وابسته و پشت سرهم نباشن و انجام سریع تر عملیات ها ما باید از multi treading استفاده کنیم.

انواع Thread تردها یا نخ ها در اندروید: 1- main thread یا ui thread یا ترد اصلی 2- background thread یا worker thread

Ui thread : ui thread وظیفه ی هندل کردن چیزهایی رو به عهده داره که مربوط میشه به رابط کاربری، هر تغییر کوچیکی که ما انجام میدیم که کاربر یک چیزی رو ببینه میشه ui thread ، از نشون دادن toast گرفته تا نشون دادن یک متن در text view یا نمایش در recycler view و … ، هرکاری که کاربر داره با ui انجام میده، میشه ترد ui ، ترد main یا ترد اصلی.

Cpu یه دونه ترد ui یا main داره و چندین ترد background میتونه داشته باشه.

background thread یا worker thread : ترد background یا worker در واقع نخ های جانبی ما که کارهای مختلف داره روی اون اتفاق میفته، مثلاً کاربر میره دانلود میکنه و همزمان که دانلود میکنه میتونه کارهای دیگه ایی هم انجام بده.

یکی از دلایل استفاده از ترد background بخاطر اینه که بتونیم کارهایی که طولانین و زمان برن و ممکن فشار بیشتری روی اپلیکیشن در همون لحظه بوجود بیارن که باعث فریز شدن ui بشه میایم توی تردهای background و worker ازشون استفاده میکنیم، مثلاً موقع دانلود یه فایل 5 گیگی که اگه تو ترد ui باشه و باتوجه به سرعت اینترنت چندین ساعت طول بکشه، باعث فریز شدن اپلیکیشن میشه و هنگ میکنه وکاربر نمیتونه کار دیگه ایی انجام بده و کاربر باید چندین ساعت معطل بمونه تا دانلود تموم بشه، بخاطر همین از ترد background استفاده میکنیم.

ما از ترد background یا worker میتونیم برای دانلود کردن فایل، ارتباط با اینترنت، کار با database ها، نمایش عکس هایی که از اینترنت میان و … که نیاز به عملیات های سنگین دارن استفاده میکنیم.

معایب استفاده نکردن از کتابخونه ها در multi treading : اگه بخوایم multi treading رو تو اندروید بصورت پیشفرض و خام کنترلش کنیم به بدترین شکل ممکن اتفاق میفته، یعنی بقدری پیچیده اس که بخوایم وضعیت های مختلفش رو کنترل کنیم، واقعاً برامون سخت میشه و باید تک تک وضعیت ها، تک تک حالت ها و تک تک چیزهای مختلف رو باید تحت کنترل خودمون در بیاریم که اگه این اتفاق نیفته اپلیکیشن کرش میکنه یا مموری کم میکنه و دچار خطای مموری لیک میشه یا دچار یه سری error های خاص (ANR) میشه و اگه بخوایم بصورت دستی cpu رو کنترل کنیم خیلی حالت های بدی رو بوجود میاره.

بخاطر همین کتابخونه های مختلفی برای کنترل multi treading اومدن که معروفترین اون ها rx و coroutines هست و کارباهاشون راحتتر.

My image

Rx vs Coroutines : Rx در مقابل Coroutines :

از نظر امکانات: Rx فوق العاده خوب و بزرگ و پر از امکانات ولی Coroutines امکاناتش کمتر و محدود تر هست.

زمان یادگیری: rx زمان یادگیریش فوق العاده زیاد مثلاً 6 تا 9 ماه طول میکشه آدم مستر rx بشه و خیلی سنگین هست ولی coroutines یادیگیریش فوق العاده راحت.

تقاضای کار در لول های بالاتر: در rx تقاضای کار در لول های بالاتر کمتر هست چون دولوپرهایی که تسط کامل و واقعی به rx داشته باشن خیلی کم هست ولی چون coroutines خیلی راحت هست بخاطر همین تقاضای کارش در لول بالا زیاد هست.

نحوه کارکرد: rx نحوه کارکردش خیلی سنگین و موارد و متدهای خیلی زیاد و سنگینی داره و build کردنش هم زمانبر هست ولی coroutines فوق العاده سبک هست.

روند اجرای اپلیکیشن: وقتی اپلیکیشن اجرا میشه میاد و توی ترد اصلی(main - ui) اجرا میشه.

زمانی که یه اپلیکیشنی رو اجرا میکنیم و اگه برنامه نویس به نحوه مدیریت تردها توجه نکنه، همه چیز و حتی عملیات های سنگین توی ترد اصلی اتفاق میفته مثل کلیک کردن، نمایش اطلاعات مختلف، محاسبات سبک و سنگین، دانلود فیلم و … .

زمانی که بخوایم اطلاعاتی رو از سرور بگیریم مثلاً بریم به صفحه پروفایل و برای گرفتن اطلاعات مثلاً از 5 ثانیه تا 1 دقیقه طول بکشه و به اندازه ی 1 دقیقه اپلیکیشن قفل میشه چون توی تردهای background یا worker اجرا نشده و این عملیات تو ترد اصلی اتفاق افتاده، حالا اگه بخوایم دانلود کنیم و به نیم ساعت زمان نیاز باشه به اندازه نیم ساعت اپلیکیشن قفل میشه و نمیشه کار دیگه ایی انجام داد، بخاطر اینکه همچین اتفاقی نیفته و همه چیز قفل نشه یک خطای معروفی در اندروید اتفاق میفته به اسم “ ANR “ یا Application Not Responding که یعنی اون عملیاتی که میخوایم انجام بدیم تو ترد ui انقد سنگین شده که cpu جواب نمیده و باعث میشه اپلیکیشن کرش کنه و کاربرو میندازه بیرون.

و در اندروید های جدید مثل 11 و 12 و حتی کمی هم اندروید 10 اگه این اتفاق زیاد تکرار بشه خود اندروید یه سری امتیازات منفی برای اپلیکیشن در نظر میگیره و به کاربر هشدار میده که این اپلیکیشن ممکن برات ضرر داشته باشه.

My image

راه حل این مشکل : استفاده از ترد پس زمینه (مثل thread background  یا thread worker هردوتا یه ترد هستن با اسم های مختلف)

برای اجرای عملیات های سنگین اونارو در تردهای مختلف در نظر میگیریم و اونهارو میفرستم روی اون تردها مثلاً بعد دانلود 5 گیگ که روی ترد background اتفاق افتاد میایم و نتیجه رو میدیم به ترد ui .

اگه برای مدیریت کردن تردها از کتابخونه ها استفاده نکنیم به یه error به اسم out of memory میخوریم که یعنی رم کم آورده و باعث کرش اپلیکیشن میشه.

My image

کروتین coroutine : کروتین یک کتابخونه ی بسیار سبک که به ما اجازه میده که بتونیم تردهای مختلف رو، یا یکسری کارهارو روی تردها انجام بدیم.

کروتین ترد نیست بلکه کار مدیریت کردن تردهارو برای ما راحتتر میکنه.

کارهایی که ما توی کروتین ها انجام میدیم میتونن بصورت موازی باهم انجام بگیرین و هرکدوم بیان کارهای خودشونو انجام بدن و هیچکدوم باهم تداخلی و دخالتی نسبت بهم نداشته باشن و یا بصورت پشت سرهم انجام بشن مثلاً بعد وظیفه 1 وظیفه 2 و بعد 3 و همینطور چرخه ادامه داشته باشه و کارها انجام بشه.

My image

ما هرچندتا ترد که بخوایم میتونیم داشته باشیم و هرچندتا کروتین روی اون تردها.

مثلاً طبق عکس یه ترد background thread 1 داریم که کروتین 1 داره ویدئو دانلود میکنه، کروتین 2 داره با اینترنت ارتباط برقرار میکنه برای کار دیگه ایی مثلاً دانلود موزیک و کروتین 3 داره همزمان که اطلاعات رو میگیره اون هارو توی دیتابیس ذخیره میکنه.

ما طبق عکس یه ترد background داریم که توی این ترد کارهای مختلفو روی کروتین های مختلف داریم انجام میدیم.

خلاصه توضیحات کروتین ها : کروتین ها ترد نیستن، بلکه ابزاری هستن که به ما اجازه ی مدیریت بهتر تردهارو میدن و توی تردهای مختلف ما میتونیم کروتین های مختلف و بیش از یک کروتین داشته باشیم و ازش استفاده کنیم.

قسمت 23

یک سری از اصطلاحات کروتین:

My image

1- coroutine scope

معنی خود اسکوپ میشه یعنی میاد یه جا و محدوده ایی که باید کروتین اجرا بشه رو برامون مشخص میکنه و معنی کلی اسکوپ میشه مشخص کردن محدوده یا جایی.

Coroutine scope : کروتین اسکوپ به ما میگه این کروتین که داریم در چه محدوده ایی میتونه مورد استفاده قرار بگیره و کدهامونو بزنیم.

بصورت کلی ما 3 نوع coroutine scope داریم:

My image

1-  coroutine scope 2- global scope 3- main scope

1-      Coroutine scope : در واقع یه کروتین جدید برای ما درست میکنه و از نظر تعداد ساخت کروتین هم، محدودیت ساخت نداریم مثلاً یه دونه واسه ویدئو، یه دونه واسه دانلود، یه دونه واسه دیتابیس و … .

Coroutine scope تقریباً حالت پیشفرض و default داره و زیاد مورد استفاده قرار میگیره.

2-      global scope : یعنی میتونیم تو کل اپلیکیشنمون ازش استفاده کنیم و چیزی که تو scope یا محدوده کل اپلیکیشن باشه یه خورده کارو سختتر میکنه و نباید همینطوری والکی ازش استفاده کنیم.

3-     Main scope : main scope اسکوپی هست که مستقیم کارشو روی ترد انجام میده، توی coroutine scope و global scope ما میتونیم بگیم روی کدوم ترد کارو انجام بده ولی در main scope مسقتیم روی ترد ui یا اصلی داره کار میکنه و دیگه روی چیزهای دیگه نمیتونه کارکنه.

ما به جز این 3 تا اسکوپ یه سری اسکوپ های دیگه هم داریم که نیاز به یکسری کتابخونه ها داره:

My image

Lifecycle scope و view model scope .

View model scope : اسکوپی هست که در غالب view model میشه ازش استفاده کرد.

Lifecycle scope : خیلی فوق العاده و کابردی هست و با توجه به چرخه حیات فرگمنت یا اکتیویتی که داریم ازش استفاده میکنیم میاد و فقط توی خود اون چرخه ی حیات مورد استفاده قرار میگیره مثلاً، اگه بخوایم توی اکتیویتی دیگه این coroutine کار نکنه اگه از بقیه ی scope ها بجز lifecycle scope استفاده کنیم باید بیایم و اون job یا کارو بصورت دستی کنسلش کنیم و اگه یادمون بره که کنسل کنیم، همینطور اون پشت و توی cpu داره کار میکنه ولی اگه از lifecycle scope استفاده کنیم و حتی یادمون هم بره کنسلش کنیم و stop کنیم مشکلی نیست و نیازی نیست، چون lifecycle که از بین رفت اتوماتیک coroutine مون هم از بین میره.

My image

انواع context های کروتین:

Context : context در coroutine همون انتخاب کننده ی ترد ماست در واقع اینکه میگیم روی کدوم ترد کار کنه یعنی داریم context هاشو مشخص میکنیم.

کروتین 4 نوع context داره که 3 تا بیشتر مورد استفاده قرار نمیگیره.

از بین این 3 تا io و main خیلی مورد استفاده قرار میگیرن بیشتر از 95% و 5% default مورد استفاده قرار میگیره.

Default برای حالت هایی که محاسبات خیلی سنگین داریم میتونیم استفاده کنیم و میاد یه سری تردهایی رو درست میکنه که اون محاسبات سنگین روبرامون انجام میده.

Io برای اجرا کردن کدمون روی ترد پس زمینه یا background هست، مثلاً دانلود اطلاعات ازسرور، دانلود ویدئو، ذخیره کردن دیتابیس و … که میخوایم توی ترد background یا worker مون اجرا بشه.

Main هم میاد اون کدی که داریم رو تو ترد main یا اصلی برای ما اجرا میکنه.

My image

انواع builder ها: builder ها در واقع نحوه ی اجرا شونده coroutine رو برای ما مشخص میکنن که این coroutine که داریم به چه شکلی میخواد اجرا بشه که 3 حالت داره:

1- launch 2- async 3- run blocking

Launch : موقعی استفاده میشه که ما میخوایم بصورت متوالی و پشت سرهم اینارو انجام بدیم مثلاً اول 1 رو انجام بده بعد 2 و بعد 3 و … و درنهایت که همه ی کارها انجام شد به کاربر اطلاع بده که تموم شده.

به launch اصطلاحاً fire to forget هم میگن، یعنی اینکه به محض اینکه اونجا کارش تموم شد و آماده شد و اونو بلافاصله برای ما برمیگردونه و اگه عملیات های دیگه ایی داشته باشیم منتظر اون ها نمیمونه و اونو برای ما میفرسته تا بتونیم ازش استفاده کنیم.

Async : برای عملیات های همزمانی هست مثلاً اگه 3 تا کار داشته باشیم بجای اینکه بگیم 1 بعد 2 و بعد 3 میتونیم هر3تارو همزمان اجرا کنیم و بعد تموم شدنش هم به کاربر اطلاع بدیم که عملیات تموم شد.

از async یه استفاده ی دیگه هم میشه، مثلاً زمانی که یه متد بازگشتی داریم و میخوایم یکسری اطلاعات رو برگردونیم و بواسطه ی اون اطلاعات بگیم یک کارهای دیگه ایی رو هم انجام بدیم، مثلاً یه api زدیم که میخواد لیست کاربرارو به ما نشون بده و میخوایم اون لیست کاربرا رو برگردونیم و تو متد بازگشتی ازش استفاده کنیم که برای اینکار میتونیم از async استفاده کنیم و async یه متدی داره به نام await که توسط متد await چیزهایی که برگشت داده میشن رو میتونیم بگیریم و ازشون استفاده کنیم.

Run blocking : باعث میشه که ترد اصلی یا ترد main ما بلاک بشه، بعضی وقتها نیاز که یکسری عملیات هارو انجام بدیم که بواسطه این عملیات کاربر نتونه بره یکسری کارهای دیگه تو ui انجام بده و میخوایم سریع و یه لحظه ui رو بلاک کنیم و این عملیات انجام بشه و بعد کاربر بتونه کارهاشو انجام بده که اینجا میتونیم از run blocking ها استفاده کنیم.

My image

نوشتن متدهای خاص: زمانی که توی کروتین داریم کار میکنیم و میخوایم از یکسری از متدها استفاده کنیم باید یکسری از شرایط رو هم در نظر بگیریم، حالا چه متدهایی که خودمون داریم بصورت custom مینویسیم یا از متدهای خود کروتین بخوایم استفاده کنیم، برای اینکه بتونیم این متدهارو درست کنیم باید بهشون suspend اضافه کنیم و اول متد بنویسیم suspend .

زمانی که متدمون suspend باشه، یعنی حالت به تعویق انداختن اون متد رو به ما میده، یعنی بتونیم این متدرو مثلاً pause کنیم بتونیم stop کنیم و بعد ادامه بدیم یا بخوایم به حالت دیگه اجراش کنه، مثلاً  در تعویق انداختن اجرای یک متد که بگیم مثلاً بعد 2 ثانیه مکث بیاد یه کارو انجام بده.

متدهایی که suspend هستن چه متدهایی که خودمون نوشتیم یا متدهای خود کروتین که برنامه نویساش اونو نوشتن باید در متدهای suspend دیگه ایی یا در خود کروتین ها مورد استفاده قرار بگیره.

یعنی اگه یه متد suspend نوشتیم نمیتونیم همینطوری ازش توی یه متد دیگه یا توی متد on create ازش استفاده کنیم، حتماً باید اونجایی که ازش استفاده میکنیم اون متد هم suspend باشه یا داخل خود کروتین ازش مستقیم استفاده کنیم.

My image

برای استفاده از coroutine نیاز به این 2تا کتابخونه داریم و باید این dependency هارو اضافه کنیم.

My image

این کتابخونه core اصلی coroutine هست.

My image

این کتابخونه که مخصوص android هست و به ما اجازه میده که از تردهای مربوط به android و یکسری آپشن هایی که coroutine برای android در نظر گرفته رو، بتونیم ازش استفاده کنیم.

قسمت 24

برای اینکه بتونیم از coroutine ها استفاده کنیم میایم و اول scope ش رو مشخص میکنیم و معمولاً بصورت general و روتینش چیزی که بیشتر استفاده میشه coroutine scope هست.

My image

Coroutine scope بعنوان ورودی از ما context میخواد و برای اینکه بتونیم بهش context یا اون تردی که میخوایم باهاش اجرا بشه رو بدیم 2 راه داریم:

My image

1- بیام و از dispatchers استفاده کنیم  که میتونیم مشخص کنیم روی کدوم ترد باشه مثلاً main, io, default .

My image

2- اگه بخوایم میتونیم مستقیماً و بدون Dispatchers هم اضافه اش کنیم ولی باید خودمون دستی بیایم و import کنیم و مواردش رو دستی اضافه کنیم، ولی هردوتا حالت یکی هست و هیچ فرقی نمیکنه و نتیجه یکی هست.

My image

حالا بعد اینکه coroutine scope رو مشخص کردیم که توی ترد io  و builder اونو با launch مشخص میکنیم و میگیم که اجرا بشه.

الان اومدیم روی یک تردی از background چون io هست و یک coroutine یی رو مشخص کردیم.

My image

اگه بخوایم میتونیم هرچندتا coroutine که خواستیم برای مدیریت تردها تعریف کنیم.

My image

برای استفاده ازlog یه متغییر به اسم tag درست میکنیم که مدام مجبور نشیم tag های مختلف رو توی logcat بزاریم.

My image

توی خود coroutine با استفاده از log e و با استفاده از کلاس thread که مربوط به کلاس های اندروید میشه و درواقع جزء کلاس های java بحساب میاد و با استفاده از current thread که میاد و thread جاری مارو بهمون میگه، که با name اسمش رو مشخص میکنیم.

 My image

وقتی اجرا میگیریم و توی logcat با استفاده از filter و محدود کردن توسط tag مون میاد و نشون میده که ترد worker یا io مون رو شناخت و ترد 2 ما بحساب میاد و خودش آورده روی ترد 2 انجام داده.

وقتی روی ترد main و default هم log میزاریم و خروجی میگیرم همون thread های مربوط به خودش رو میاره و توی log نشون میده.

ترد default و ترد io یکی هستن ولی بیشتر از thread default تو جاهایی که یکسری محاسبات سنگین داریم استفاده میشه.

میتونیم از متدهای عادی توی coroutine استفاده کنیم ولی از متدهای coroutine نمیتونیم داخل متدهایی دیگه یا متدی که ساختیم استفاده کنیم چون متدها یا function هایی که برای coroutine نوشته شده suspend هستن تا ما کنترل بیشتری روی اون function ها داشته باشیم البته همه ی متدهای coroutine suspend نیستن.

My image

یکی از suspend function هایی که توی خود کروتین هست function delay هست که میتونیم باهاش یه تاخیری بندازیم که براساس میلی ثانیه هست(هر 1000 میلی ثانیه میشه 1 ثانیه).

نمیتونیم و به ما اجازه نمیده که از delay استفاده کنیم چون داخل یه متد عادی(show my log) ازش استفاده کردیم.

My image

برای اینکه بتونیم از delay استفاده کنیم باید بیایم و متدمون رو suspend کنیم و باید قبل از fun ، بنویسیم suspend و کنار delay یه علامت بصورت فلش اضافه شد که نشانه ی suspend function هست.

My image

ما میتونیم بجای اینکه بیایم مستقیماً  کدها و دستورات رو داخل خود coroutine بنویسیم از طریق متد (show my log) هم در coroutine قرار بدیم و اجرا بگیریم و فرقی نداره و ننیجه همونه.

چون متد show my log از نوع suspend هست ما نمیتونیم خارج از کروتین یا suspend function ها ازش استفاده کنیم.

My image

 با استفاده از یه delay 3 ثانیه ایی اومدیم log یی که میخواست نمایش بده رو با 3 ثانیه تاخیر نمایش دادیم.

قسمت 25

Coroutine در حالت lunch پشت سرهم اتفاق میفته و در حالت async بصورت همزمان و باهم کارهارو انجام میده.

میخوایم ببینیم که اگه چندتا coroutine داشته باشیم خودش چطور بین تردها میاد و تقسیم میکنه:

My image

ما 4 تا coroutine داریم که 4 بار اجرا شده 2تا کروتین روی ترد io و 2تا روی ترد main و یه متد که میایم با log ، اسم تردمون رو نشون میدیم و اونو تو coroutine هامون call میکنیم.

نتیجه میشه برای ترد io اومده 2تا ترد درست کرده که خود cpu میاد تقسیم بندی میکنه که روی کدوم ترد کار کنه و برای main هم چون فقط 1 ترد main داریم اومده و از همون 1 ترد استفاده کرده و coroutine های مارو روی اون ها اجرا کرده.

ما بیش از 1 ترد worker یا background داریم و تعدادشون زیاده که البته بستگی به خود cpu داره که هرچی جدیدتر باشه و تکنولوژیش بهتر باشه تردهای بیشتری رو داره.

نحوه ی اجرای متدها توسط builder lunch در coroutine که چطور اونهارو بصورت پشت سرهم اجرا میکنه:

My image

2تا متد درست کردیم با مقدار بازگشتی از نوع string و براشون با delay 2 ثانیه و 3 ثانیه تاخیر درنظر گرفتیم، بعد این متدهارو در coroutine و داخل 2 تا متغییر تعریف کردیم و بعد اونهارو در log قرار دادیم.

وقتی اجرا میگیریم، 5 ثانیه طول میکشه تا توی logcat بیاد و log هارو نشون بده چون زمانی که از lunch استفاده میکنیم اول result1 اجرا میکنه و 2 ثانیه delay میخوره بعد تموم که شد میره سراغ result2 و 3 ثانیه delay میخوره که مجموع تاخیر میشه 5 ثانیه، وقتی بعد 5 ثانیه 2 تا متد ما انجام شده بعد میاد و نتیجه جفتشون رو برای ما اجرا میکنه.

ولی async اینطوری نیست و بعد 2 ثانیه میاد و متد اول رو اجرا میکنه و بعد 3 ثانیه متد دوم رو، Async به ما این قابلیتو میده که توسط متد await بتونیم چیزی که برگشت داده میشه رو بتونیم ازش استفاده کنیم مثلاً بخوایم لیست کاربران سایتمون رو بگیریم و تو اپلیکیشنمون نشون بدیم که توسط await میتونیم اینکارو انجام بدیم.

My image

با استفاده از متد measure time miller میتونیم مدت زمان انجام شدن یه کارو بدست بیاریم و کار انجام شده رو برحسب میلی ثانیه به ما میده و این متد جز lambda expression ها بحساب میاد.

با استفاده از time میایم و مدت زمان رو در logcat نشون میدیم

My image

 که همون 5 ثانیه که تاخیر ایجاد کرده بودیم رو نشون میده.

تست کردن حالت async که موازی باهم اجرا میکنه و پشت سرهم نیست: My image

وقتی میخوایم از async استفاده کنیم میایم و بجایی اینکه مستقیم متدهامونو داخل متغییر تعریف کنیم داخل async قرار میدیم که async هم جزء lambda expression هاست.

My image

وقتی میخوایم از result1 توی log استفاده کنیم به ما error میده چون log از ما string میگیره ولی result1 یک آبجکتی از async هست که با استفاده از await میتونیم از مقدار برگشتی متدهامون استفاده کنیم که بصورت string تعریف کرده بودیم و اوکی میشه.

برای duplicate کردن کدها از ctrl + D استفاده میکنیم.

My image

وقتی برنامه رو اجرا میکنیم میاد result1 رو بعد 2 ثانیه و result2 رو بعد 3ثانیه اجرا میکنه و log هارو نشون میده، در تایم هم مشخص در ساعت 01:12:00 هردوتا اجرا شدن ولی result1 ظرف 2 ثانیه و result2 1 ثانیه بعد result1 و یعنی ظرف 3 ثانیه اجرا و بعد نمایش داده شد نه 3 ثانیه بعد result1 .

نکته ایی که در استفاده از async ها باید توجه کرد این که، اجرای کدها پشت سرهم نیست و موازی هم هستن یعنی همزمان باهم انجام میشن.

My image

با استفاده از متغییر final result اومدیم و مجموعه زمانی که result1,2 اجرا میشن رو با استفاده از time محاسبه کردیم که بدرستی شده 3 ثانیه چون async همزمان هردوتا متدرو باهم اجرا میکنه و میبینه کدوم متد بیشتر طول کشیده تا اجرا بشه که متد do worker2 ما 3 ثانیه delay داشت بخاطر همین توی 3 ثانیه اومد و جواب مارو داد.

قسمت 26

Run blocking : باعث میشه که ترد ui ما برای هر مدت زمانی که خودمون در نظر بگیریم یا برای انجام یکسری از عملیاتی که ما مشخص کردیم، block بشه و اجازه نده ترد main کارشو انجام بده.

My image

وقتی میخوایم از run blocking استفاده کنیم میتونیم بصورت مستقیم ازش استفاده کنیم و نیاز نیست داخل scope قرارش بدیم.

My image

وقتی داخلشش یه delay 5 ثانیه ایی ایجاد میکنیم 5 ثانیه طول میکشه تا اپلیکیشن ما ساخته بشه.

مثلاً میتونیم درجاهایی که بعد انجام یکسری کارها نیاز اول اطلاعات بیاد بعد ترد ما ساخته بشه میتونیم ازش استفاده کنیم.

My image

2تا متد(do work1,2) رو ساختیم و روی هرکدوم 1 ثانیه delay قرار دادیم و اومدیم توی 2 تا coroutine و هرکدومو 3 بار صدا زدیم.

My image

نتیجه میشه هرکدوم رو با 1 ثانیه تاخیر میاره و اجرامیکنه و در زمانش هم مشخصه که 2 تای اول رو ثانیه 0 دوتای دوم روی ثانیه 1 و دوتای آخری هم روی ثانیه 02 اجرا میشن.

My image

اومدیم روی coroutine دوم و یه تاخیر 3 ثانیه یی ایجاد کردیم و موقعه خروجی میاد و متد do work1,2 رو همزمان اجرا میکنه و دوباره میاد و با تاخیر های 1 ثانیه ایی و دوبار پشت هم do work 1 و اجرا میکنه و بعد با تاخیر 3 ثانیه ایی میاد و 2بار متد do work2 رو بدرستی اجرا میکنه چون ما یه delay 3 ثانیه ای روی log دوم و سوم do work2 ایجاد کردیم و روی بقیه اعمال نشد و تاخیر هیچ تاثیری روی coroutine اول ما نداشت.

My image  

میایم داخل coroutine دوممون و از run blocking استفاده میکنیم و وقتی اجرا میکنیم، میاد و do work1,2 رو اجرا میگیره و بعد 3 ثانیه اپلیکیشن رو فریز میکنه و علاوه بر فریز اپلیکیشن میاد coroutine اولمون رو هم فریز میکنه و کلاً تردمون رو اشغال میکنه چون هردوتا coroutine ما روی ترد main هستن و ما فقط یک ترد main بیشتر نداریم و اگه 100 تا coroutine دیگه داشتیم میومد و کل coroutine هامونو فریز میکرد و ترد main مون رو block میکرد.

در ادامه اومد و 2 بار do work2 با تاخیر 1 ثانیه ایی اجرا کرد و بعد میاد و do work 1 رو دوبار اجرا میکنه چون توی coroutine اولمون 2 تا log 2و 3 اجرا نشده بودن و بعد در نهایت بعد مکث 3 ثانیه اومد ui رو به ما داد.

ما میتونیم custom thread یا یه ترد مخصوص بخودمون روی background و با نام دلخواه خودمون ایجاد کنیم و نمیشه روی ترد main اینکارو کرد چون یه ترد main بیشتر نداریم.

زمانی که ما یه ترد بخصوص برای خودمون درست میکنیم، باید حواسمون به کنسل کردن و مدیریت اون lifecycle کروتین رو هم به عهده بگیریم.

My image

با استفاده از new single thread context میتونیم یه ترد جدید ایجاد کنیم که از ما یه اسم میخواد که براش یه اسم دلخواه در نظر میگیریم.

My image

این ترد جدید ترد background هست ولی بعضی مواقع نیاز به یک ترد مخصوص به خودمون داریم، مثلاً بخوایم یک کار بخصوصی رو در یک ترد بخصوص داشته باشیم مثلاً در بین تردهای مختلفی که داریم نیاز به یه ترد داریم که بیاد و اطلاعات کاربران رو بگیره که میایم و یه ترد users درست میکنیم.

یه نکته در مورد زمانی که داریم از کروتین استفاده میکنیم که باعث کرش اپلیکیشن میشه: وقتی داریم یکسری اطلاعات رو از سرور میگیریم و یا داریم توی database ذخیره شون میکنیم، و بخاطر اینکه ترد ui فریز نشه اونو میندازیم روی ترد background و از طرف دیگه میخوایم زمانی که اطلاعات تکمیل شد یا از سرور گرفتیم یا از دیتابیس set کردیم به کاربر اطلاع بدیم که عملیات با موفقیت انجام شد و اطلاعات رو نشون بدیم ولی نکته اینه که، این اطلاعات روی ترد background هست و ترد background اجازه نداره که بیاد چیزهایی که توی ui هست رو آپدیت کنه، و باید از with context استفاده کرد.

My image

یه coroutine داریم که روی ترد io هست و مثلاً میخوایم اطلاعات کاربرو تو دیتابیس ذخیره کنیم و متد do work1 اطلاعات کاربرو ذخیره میکنه و بعد اتمام عملیات بیا و توی text view نشون بده، که برنامه کرش میکنه و error میده که ما فقط در ترد ui میتونیم view هامونو تغییر بدیم.

اگه تردمون رو از io به main تغییر بدیم اوکی میشه و error یی هم نمیده ولی کارمون درست نیست چون ما نمیدونم که این عملیات ذخیره سازی اطلاعات توی دیتابیس یا دریافت اطلاعات از سرور چه مدت زمانی نیاز داره و ممکن طولانی باشه و الکی ترد main مون رو اشغال کنیم و الکی درگیر کنیم و همه چیز رو از دسترس خارج کنیم.

My image

برای انجام این کار میایم و از with context استفاده میکنیم که context در واقع میاد و ترد مارو مشخص میکنه و ما با with context میایم و بصورت موقتی میایم و تردمون رو عوض میکنیم که بعد انجام کار مثلاً بیاد و تغییرات رو در ترد main به کاربر نشون بده.

برای مثال در اینجا اومد متن پیشفرض hello world رو بعد تاخیر یک ثانیه ایی که ایجاد کردیم به Done تغییر داد.

از with context میشه در async هم استفاده کرد وقتی چیزی که برای ما بازگشت داده میشه در async مهم نیست و نمیخوایم از مقدار بازگشتیش استفاده کنیم و تو این شرایط هم میتونیم از with context استفاده کنیم و خود coroutine هم پیشنهاد میده که وقتی که نمیخوایم از مقدار بازگشتی استفاده کنیم و نیاز نیست از await استفاده کنیم میتونیم بجای async از with context استفاده کنیم.

قسمت 27

استفاده از repeat یعنی مشخص کردن تعداد تکرار:

My image

داخل repeat میتونیم مشخص کنیم که کار مورد نظرمون رو چند بار تکرار کنه که repeat جزء lambda expression ها بحساب میاد.

اومدیم و متد do work1 رو با 3 بار تکرار و تاخیر 1 ثانیه ایی چاپ کردیم.

repeat زمانی کاربرد داره که میخوایم یک کار خاصی رو به یک تعدادی انجام بدیم تا ببینیم به نتیجه مورد نظر میرسیم یا نه.

استفاده از with time out or null که جزء lambda expression ها بحساب میاد و بعد یک زمانی میاد و کنسل میکنه.

My image

مثلاً یه کاری داریم که یه زمان طولانی داره این کارو انجام میده و ما میخوایم که بعد 3 ثانیه بیاد و این کارو کنسل کنه که میایم و زمان کنسل کردن رو بهش میدیم.

My image

میایم یه حلقه for مینویسیم که از 1000 تا 1100 رو بشماره و میایم به ازای هربار انجام دادن شمارش یک ثانیه تاخیر ایجاد میکنیم که ظرف 3 ثانیه میاد و از 1000 تا 1002 رو برای میشماره که 3 تا عدد رو در 3 ثانیه شمرده و چاپ کرده و بعد ادامه کارو کنسل کرده به درستی.

with time out or null زمانی کاربرد داره که میخوایم یک عملیات خیلی طولانی انجام بدیم ولی نمیخوایم از یک بازه ایی بیشتر این اتفاق بیفته و میایم محدودش میکنیم که مثلاً تو 3 یا 5 ثانیه چه مقدار از اطلاعات رو میتونه ذخیره کنه یا دریافت کنه و … .

قسمت28

ما توی thread ها یک حالتی داریم که یک تاخیری رو ایجاد میکنه که بهش میگن thread. Sleep

و ترد مارو با زمانی که بهش میدیم میخوابونه و stop ش میکنه.

My image

Thread. Sleep با delay که داخل coroutine ها قرار میدیم و استفاده میکنیم باهم فرق دارن.

چون اگه ما از coroutine و delay استفاده کنیم ui مارو کنسل و فریز نمیکنه ولی کدهای مارو بعد اون تاخیر اجرا میکنه ولی thread. Sleep هم میاد کدهای مارو و هم ui مارو بعد اون تاخیری که درش ایجاد کردیم فریز میکنه و بعد اجرا  جفتشو اجرا میکنه.

Job : تموم coroutine هایی که ما تا الان داشتیم ازش استفاه میکردیم و lunch میکردیم در واقع داشتیم یک job جدید، یک کار جدیدیی رو باهاش معرفی میکردیم و ازش استفاده میکردیم.

اگه وارد کدهای خود lunch بشیم میبینیم که داره از job ارث بری میکنه.

چرخه ی حیات job : My image

همون اول که میخوایم یه job یی رو lunch کنیم ساخته میشه بعد start میشه و شروع بکار میکنه و بعد job ما active یا فعال میشه زمانی که میخواد بره complete یا تکمیل بشه و کارشو انجام بده و بعد این که complete شد completing یا درحال انجام شدن برای ما اتفاق میفته و زمانی که کلاً تموم کرد مثلاً میگفتیم بعد 1 ثانیه بیا log نشون بده و زمانی که کلاً تموم کرد completed میشه.

ولی اگه با cancel یا fail روبرو بشه و شکست بخوره چه توی active یا completing باشه وارد cancelling میشه و میخواد cancel بشه و درحال کنسل شدن و بعد که cancel شد و تموم شد به ما اون cancelled شدن و تموم شدن رو میده.

و ما میتونیم تشخیص بدیم که job ما active شده یا completed شده یا cancelled شده و این 3 حالت رو به ما میده که بتونیم کنترلشون کنیم.

مثلاً میخوایم از صفحه ی a بریم به صفحه ی b و میخوایم چک کنیم که coroutine ما که تو صفحه ی a کار کردیم is activate هست یا نه که اگه active بود بیاد و کنسلش کنه چون نیازی به کروتینش نداریم چرا الکی داخل cpu کار کنه و تردهارو درگیر کنه.

My image

میایم و یه متغییر از نوع job تعریف میکنیم.

My image

که این job از نوع coroutine context هست.

My image

بعد میایم و کل coroutine که داریم رو میریزیم داخل job .

My image

میتونیم چک کنیم که job ما active شده یا cancel شده یا complete شده و از نوع Boolean هست و true, false برمیگردونه.

My image

یا حتی میتونیم بصورت دستی هم بیایم و cancel ش کنیم.

My image

 یا حتی میتونیم از حالت join هم استفاده کنیم.

My image

میایم و وضعیت های مختلف یک job رو چک میکنیم که بعد تاخیر 1ثانیه ایی که ایجاد کردیم وضعیت های job در حالت is active, is completed, is cancelled چون از نوع Boolean هست میایم و اونو به string تبدیل میکنیم.

وقتی اجرا میگیریم هر3 حالت رو باهم و در یک زمان اجرا میکنه و چک میکنه ،که در حالت is active true هست چون همون اول اجرا شده و داره کار میکنه و هنوز کامل نشده و در حالت های کامپلیت و کنسل روی false قرار داره، چون عملیات در جریان هست و cancel نشده false نشون میده، ولی complete رو هم false نشون میده بخاطر اینکه همون اول که اپلیکیشن اجرا شده job ما تموم نشده و تازه با یک ثانیه تاخیر میخواد log رو اجرا کنه بخاطر همین هنوز اپلیکیشن یا coroutine ما completed نشده و false نشون میده و یک ثانیه تاخیر log هم بخاطر اینکه delay داخل coroutine قرار داره ولی 3 حالت job بیرون بلاک coroutine هستن.

My image

log هارو داخل یه coroutine میزاریم و با تاخیر 2 ثانیه ایی اجرا میگیریم که اول میاد log داخل  job رو با 1ثانیه تاخیر چاپ میکنه و بعد میا و هر3تا حالت رو همزمان و با 2ثانیه تاخیر چک میکنه و چاپ میکنه که این دفعه حالت active job false چون بعد 1 ثانیه اومد و کارشو انجام داد و دیگه job active نیست وjob در حالت complete true هست چون کار انجام شده و حالت cancel job هم false چون کار انجام شده و کنسل نشده.

My image

میایم و بصورت دستی job رو cancel میکنیم چون همون اول که میاد میخواد وارد coroutine بشه و بعد 1ثانیه منتظر بمونه تا کارشو انجام بده بلافاصله cancel میشه.

چون job ما تکمیل شده و خودمون هم دستی cancel ش کردیم هردو رو true نشون میده ولی چون active رو غیر فعال کردیم و cancel کردیم میاد و false میشه.

My image

چون cancel جزء suspend function ها نیست میتونیم بیرون از کروتین هم ازشون استفاده کنیم و جزء abstract هاست.

قسمت 29

Join : از join میشه زمانی استفاده کرد که ما بخوایم یکسری کارهامون حتماً با اون ترتیبی که در نظر داریم انجام بگیرن و بعد از اینکه انجام شد و job ما تکمیل و completed شد، بیاد و یکسری کارهای دیگه رو انجام بده.

خیلی مواقع پیش میاد که ما میخوایم یه کاری توی coroutine انجام بشه و بعد، از نتیجه توی جای دیگه استفاه کنیم، که اگه از join استفاده نکنیم میبینیم وسط کار اطلاعات رفته ولی ناقص رفته.

اگه چندین coroutine تو درتو تعریف کنیم به coroutine اول میگیم parent و به coroutine های داخلی میگیم child .

My image

داخل coroutine parent مون یه coroutine child با job ساختیم که با repeat 3 بار تکرار کردیم و بهش تاخیر 1ثانیه ایی دادیم و داخل log یه متنی قرار دادیم و بعد داخل coroutine والد هم یه log دیگه قرار دادیم که وقتی اجرا میگیریم اول log coroutine parent اجرا میشه، چون log های child رو با تاخیر 1 ثانیه ایی در نظر گرفتیم، ولی ما میخواستیم اول job ما با موفقیت تکمیل بشه بعد بیاد done رو چاپ کنه، مثالش میشه میخوایم اطلاعات رو بگیریم و بعد تکمیل شدن بفرستیم ولی اینجا قبل تکمیل شدن اطلاعات و بصورت ناقص فرستاده که با موفقیت انجام شد، درصورتی که ناقص هست.

برای اینکه این اتفاق نیفته و اطلاعات کامل فرستاده بشه باید از join استفاده کنیم.

My image

وقتی از  join  استفاده میکنیم میاد اول job رو کلاً تموم میکنه بعد میره سراغ ادامه کار، join جزء suspend function هاست.

My image

وقتی به جای join از cancel استفاده میکینم، میاد اول یه delay میخوره بعد چاپ میکنه بعد میخواد 2 دومین بارو با 1ثانیه تاخیر چاپ کنه چون برای cancel هم 2ثانیه تاخیر درنظر گرفتیم میاد cancel میکنه و بعد done رو نشون میده.

My image

وقتی داخل job بیایم و delay رو بعد log قرار بدیم میاد 2بار چاپ میکنه و بعد cancel میکنه و done میشه.

My image

یه حالت دیگه ایی هست که میخوایم مطمئن بشیم کارمون تموم شده و بعد اینکه کارامون تموم شد بیاد و کلاً اون job رو برای ما cancel کنه که میتونیم از join و cancel هم استفاده کنیم به این صورت و درسته.

My image

 ولی روش حرفه ایی تر استفاده از cancel and join هست که هردوتارو باهم داره.

My image

اگه cancel children رو بزنیم میتونیم فقط coroutine های child رو cancel کنیم.

 My image

وقتی از cancel and join استفاده کنیم و تاخیر رو هم روی 4ثانیه بزاریم هم میاد job های مارو با 3تا تاخیر 1ثانیه ایی چاپ میکنه و کارو انجام میده و هم کلاً بعد تموم شدن کار میاد و job رو cancel میکنه و تردی توی cpu رو اشغال نمیکنه که باعث سنگین شدن اپلیکیشن بشه.

از delay دوم که داخل coroutine parent هست زمانی استفاده میشه که ما یه کار خیلی سنگینی داریم مثلاً میخوایم اطلاعات رو بریزیم توی database که میایم و زمانش رو، روی 2 دقیقه و برحسب میلی ثانیه قرار میدیم، یا برای گرفتن اطلاعات از سرور توسط رتروفیت میایم و زمان تاخیر رو، روی 1 دقیقه قرار میدیم و اگه میخواد خوندن api که بیشتر از 1 دقیقه طول بکشه بهتر که cancel بشه چون یا سرور یا کارمون مشکل داره، و اینطوری میشه زمانی delay رو بصورت تقریبی و حدودی بدست آورد.

قسمت 30

Lifecycle coroutine و activity :

سناریومون اینکه از activity a بریم به activity b با این شرایط که 2تا coroutine داریم که در coroutine اول هر یک ثانیه 1بار یه log یی رو چاپ کنه و در coroutine دوم بعد 3ثانیه تاخیر بریم به activity b .

زمانی که ما از activity a به b میریم باید عملیاتی که توی a کار میکنه باید stop بشه چون ما دیگه در activity a نیستیم و نباید cpu و منابع سخت افزاری ما درگیر صفحه ایی که درش قرار نداریم بشه. My image

برای استفاده از lifecycle ها در coroutine باید این 2تا dependency رو به gradle مون اضافه کنیم، که کتابخونه اول مربوط به view model میشه.

و از این کتابخونه ها استفاده میکنیم که lifecycle activity مون رو بصورت خودکار تحت کنترل خودش قرار بده و علاوه براین یه scope دیگه ایی هم داره که زمانی که این 2تا dependency رو اضافه میکنیم یه scope دیگه برای coroutine اضافه میشه که lifecycle coroutine و lifecycle activity رو خودش بصورت اتوماتیک هندل میکنه.

My image

یه coroutine روی ترد io داریم که داخلش یه حلقه while داریم که مقدارش true هست و یعنی یه حلقه بی نهایت هست.

و coroutine دوم رو، روی ترد main قرار میدیم که از activity a با یه intent به b بریم و چون تغییرات ظاهری داره روی ترد main قرار میدیم.

My image

از also استفاده میکنیم که حالت intent رو برای ما برگردونه.

وقتی برنامه رو اجرا میگیریم با اینکه در coroutine دوم ما بعد 3ثانیه گفتیم بره به activity b و حتی از finish هم استفاده کردیم ولی همچنان coroutine اول با تاخیر 1ثانیه ایی میاد و log میندازه درصورتی که باید 3تا log مینداخت ولی در یه چرخه بینهایت ادامه داره، با اونکه دیگه تو activity a نیستیم cpu همچنان درگیر و منابع سخت افزاری مارو داره استفاده میکنه که بعد یه مدت باعث هنگی و فریز شدن اپلیکیشن میشه.

My image

راه حل اینه که یا بیایم و coroutine اولمون رو در قالب job تعریف بکنیم و بعد بیایم و این job رو در lifecycle android و داخل on stop و یا on destroy بنویسیم که بهتر توی on stop بنویسیم، چون زمانی که کاربر یه دفعه ایی از اپ بیرون میاد، منابع سخت افزاری ما هم stop بشن و کار نکنن.

My image

ولی روش بهینه تری هم وجود داره چون ممکن ما فراموش کنیم که بیایم و coroutine هارو cancel کنیم و میایم از lifecycle scope یا scope یی که درگیر lifecycle هست استفاده میکنیم، یعنی تا زمانی که lifecycle این activity یا fragment ایی که داریم ازش استفاده میکنیم، در حال اجراست، coroutine مون اجرا میشه، ولی به محض اینکه lifecycle اون بسته میشه و از دست میره، میاد و بصورت اتوماتیک اون coroutine که داخلش از lifecycle scope استفاده کردیم رو stop میکنه.

My image

وقتی از lifecycle scope استفاده کردیم میاد و اتوماتیک بعد اون تاخیر و از بین رفتن lifecycle عملیات مارو خودش cancel میکنه و بدرستی میاد و فقط 3 بار log میندازه و عملیات متوقف میشه.