سلام بچه ها امیدوارم که حالتون خوب باشه.خب توی این مقاله میخوایم که هر آن چیزی که به عنوان یک AndroidDeveloper در مورد کوروتین ها باید بدانید را با هم بررسی کنیم.
خب اول از همه اینکه آقا این کوروتین اصلا چی هست؟🤔
قبل از شروع اینو بگم که پیشنیاز این مقاله آشنایی کافی با مفاهیم تردینگ میباشد.
خب همه ما در حین توسعه اپلیکیشنمون یک سری تسک هایی داریم که اصطلاحا بهشون میگن LongTask. خب یعنی چی؟یعنی اینکه این تسک ها برای اجرا و کامل شدن زمان زیادی لازم دارند که اگه اونا رو روی ترد اصلی یا یوآی ترد اجرا کنیم اپلیکیشن ما اصطلاحا فریز میشه یا lock میشه و خب این تجربه کاربری بدی برای اپلیکیشن ما خواهد بود.
خب راه حل چیه؟
راه حل های قدیمی و سنتی بدین صورت بود که شما در اندروید میتوانستید با ایجاد یک بکگراند ترد این عملیات را از ترد اصلی جدا کنید.خب این راه حل خیلی جالب به نظر نمیرسه چرا که مدیریت دستی ترد ها کاری زمانبر و پیچیده خواهد بود و علاوه بر اینکه خوانایی کد پایین میاد (مشکل callback hell) و هم اینکه ممکن هست که با مشکل MemoryLeak برخورد کنیم چرا که هر ترد حافظه ی جداگانه خود را دارد و مدیریت دستی این موارد واقعا کاری پیچیده خواهد بود.
خب بریم توضیحات بالا رو با مثال توضیح بدیم:
فرض کنید ما یک ApiCall برای گرفتن اطلاعات یوزر از سرور داریم .حالا اگه کد زیر را برای این منظور به صورت زیر بنویسیم:
fun fetchAndShowUser() {
val user = fetchUser()
showUser(user)
}
fun fetchUser(): User {
// make network call
// return user
}
fun showUser(user: User) {
// show user
}
خب اگه این کد را اجرا کنیم ، ما یک exception از نوع NetworkOnMainThreadException دریافت خواهیم کرد و این یعنی اینکه ما اجازه استفاده از این درخواست را روی MainThread نداریم .خب راه حل چیه؟استفاده از Callback .حالا کد زیر را در نظر بگیرید:
fun fetchAndShowUser() {
fetchUser { user ->
showUser(user)
}
}
fun fetchUser(callback: (User) -> Unit)) {
// make network call on background thread to get user
// callback with user
callback(user)
}
fun showUser(user: User) {
// show user
}
خب اوکی مشکل ما حل شد و دیگه ما NetworkOnMainThreadException دریافت نخواهیم کرد.اما اگه ما چندتا درخواست تودرتو داشته باشیم چی میشه؟حالا کد زیر را در نظر بگیر:
fun fetchData() {
fetchA { a ->
fetchB(a) { b ->
fetchC(b) { c ->
// do something with c
}
}
}
}
خب به این نوع پیادهسازی، که توابع به صورت متوالی یکدیگر را فراخوانی میکنند و از نتیجه هرکدام برای فراخوانی تابع بعدی استفاده میکنند، به عنوان "Callback Hell" شناخته میشود. در این روش، به دلیل استفاده از بازخوانی های متعدد و تودرتویی، کد ممکن است به شکل ناخوانا و سخت به نظر بیاید. و حالا فکر کنید اگر این درخواست ها بیشتر و بیشتر بشه چی میشه و چه و دردسرهایی خواهیم داشت.تا اینجا افتاد دیگه؟
خب حالا بریم دنبال راه حل های بهتر.
خب برای خلاص شدن از این مشکلات میتونیم بیایم از ابزار قدرتمند RX استفاده کنیم.به صورت زیر :
fetchUser()
.subscribeOn(Schedulers.io())
.observerOn(AndroidSchedulers.mainThread())
.subscribe { user ->
showUser(user)
}
fun fetchUser(): Single<User> {
// make network call
// emit user
}
fun showUser(user: User) {
// show user
}
خب اوکی الان با RX تونستیم مشکل callback hell رو هم حل کنیم.(ما توی این مقاله در همین حد از RX توضیح میدیم و توضیحات بیشتر در این مبحث نمیگنجه).
خب حالا با وجود اینکه RX راه حلی عالی بود و همچنان هم هست اما خب بعضی مقالات به منحنی یادگیری RX اشاره کردن.یعنی اینکه یادگیری RX زمانبر تر هست و هم اینکه خب حجم کدنویسی Rx برای هندل کردن کدهای Async بیشتر خواهد بود.خب تا همینجا کافیه .
حالا بریم برای بحث شیرین coroutine ها .😍
کوروتین یک ابزار یا فریم ورک یا یک الگوی برنامهنویسی است که امکان اجرای همروند و ناهمگام(asynchronous ) را فراهم میکند. این الگو به برنامهنویسان امکان میدهد تا به صورت سادهتر با عملیاتهای همروند کار کنند و از منابع سیستم به بهترین شکل استفاده کنند.
کوروتین ها را اصطلاحا تردهای سبک وزن میگن یعنی اینکه شما میتونید چندین کرورتین را در یک ترد داشته باشید.(نگران نباش بهت قول میدم در پست بعد کامل برات توضیح بدم یعنی چی)
نکته مهم دیگه اینه که استفاده از کوروتین ها خیلی ساده ست یعنی اینکه شما همانطور که کد های sequential (کدهای sequential به صورت متوالی و پشت سر هم اجرا میشوند، به این معنی که هر دستور باید منتظر اجرای دستور قبلی باشد تا بتواند اجرا شود. )کد های خود را مینویسید به همان صورت هم میتوانید کدهای Async خود را به کمک کوروتین ها بنویسید که از نمای بصری ساده تر و خوانایی کد بالاتر خواهد بود و این از زیبایی های کوروتین هاست😊.به عنوان مثال:
نمونه کد زیر را در نظر بگیرید
fun fetchAndShowUser() {
GlobalScope.launch(Dispatchers.Main) {
val user = fetchUser() // fetch on IO thread
showUser(user) // back on UI thread
}
}
suspend fun fetchUser(): User {
return withContext(Dispatchers.IO) {
// make network call on IO thread
// return user
}
}
fun showUser(user: User) {
// show user
}
تیکه کد بالا یک نمونه ساده از استفاده و پیاده سازی کوروتین هاست.میبینید به چه سادگی و زیایی تونستیم درخواست شبکه رو روی یک IOThread اجرا کنیم و نتیجه رو نشون بدیم.😍😍
خب اصلا نگران نباشید در مورد کدهای بالا و کلمات کلیدی آن در مقاله بعدی توضیح خواهیم داد. تا اینجا که متوجه شدیم هدف از استقاده کوروتین ها چیه.
شیرجه ای عمیق تر درون کوروتین ها<br/>