اخیراً کاتلین یک رویکرد پیشرفته و کارآمد از همزمانی را در Coroutines معرفی کرد، که میتواند در اندروید از آن برای ساده کردن کارهای async استفاده کرد. در حقیقت این رویکرد در مقایسه با سایر رویکردهای اندروید سادهتر، جامعتر و قویتر است. هدف از این مقاله معرفی و بحث در مورد مفاهیم اساسی Coroutines در کاتلین برای توسعهدهندگان اندروید است.
نگاه اجمالی
برنامه نویسی ناهمزمان ابزاری برای برنامه نویسی موازی است، که در آن واحد کار جدا از thread اصی اپلیکیشن اجرا میشود و thread که آن را فراخوانی کرده را از اتمام، عدم موفقیت و یا پیشرفت مطلع میکند. بنابراین میتوان آن را به عنوان یک مسئله اساسی در برنامههای پیشرفته اندرویدی در نظر گرفت زیرا به عنوان یک توسعهدهنده اندرید باید کارهای گران و سنگین را از Ui thread دور کنیم. این بدان معنی است که شما میتوانید برخی از کارها را در پسزمینه بدون توقف Ui مدیریت کنید و از تجربه کاربری بد جلوگیری کنید. اندروید از برخی از راهحلهای سنتی برنامه نویسی async مانند thread و AsyncTask پشتیبانی میکند، با این حال این مشکل هنوز به طور کامل حل نشده است. thread و AsyncTask به راحتی میتوانند باعث نشت حافظه و سربار اضافی شوند، و همچنین مشکل CallBack Hell یکی از پیامدهای اصلی در برنامه نویسی سنتی است.
اخیراً گوگل علاوه بر اینها برخی از بهترین شیوهها و بازخوردها را برای همزمانی مورد استفاده قرار داده است که توسط توسعهدهندگان اندروید استفاده میشود. در واقع رویکرد همزمانی مدرن در اندروید توسط گوگل در سه دسته طبقهبندی میشود: کوروتینها(محاسبات قابل تعلیق)، RxKotlin یا RxJava(شامل زمانبندها، ناظرها و دادههای قابل مشاهده)، و LiveData (دادههای قابل مشاهده). ابتدا در باره مورد اول باید بگویم که Coroutines در کاتلین رویکرد جدیدی از همزمانی را معرفی میکند، که میتواند در اندروید برای سادهسازی کارهای async مورد استفاده قرار گیرد. همچنین توسعهدهندگان اعلام کردهاند که این روش میتواند بهترین راهحل در اندروید باشد. مورد دوم که Rxjava یا RxKotlin است یکی از مکانیزمهای کارآمد در اندروید برای همزمانی و همچنین برنامه نویسی ناهمزمان است، اما برای شناخت و استفاده از آن به طور موثر توسط توسعهدهندگان زمان زیادی طول میکشد. در مورد مورد سوم توسعهدهندگان ذکر کردهاند که livedata میتواند یک رویکرد مفید برای همزمانی در اندروید باشد، اما آنها یک راهحل کامل برای این مشکل میخواهند.
سرانجام بهترین راهحل از دیدگاه گوگل باید از سه اصل اصلی پیروی کند، و آنها معتقدند که Coroutines در کاتلین این ایدهها را به شرح زیر ارائه میدهد:
- سادگی: باید یک راهحل آسان برای یادگیری باشد
- جامع: باید راهحلی باشد که کلیه موارد و راهحلها را در بر بگیرد
- قدرتمند: باید دارای بخشهای تحریک کننده داخلی باشد
آشنایی با coroutine در کاتلین
اساساً کاتلین از مفهوم سبک برنامه نویسی (continuation-passing(CPS استفاده میکند. این روش برنامه نویسی شامل ارسال جریان کنترل برنامه به عنوان آرگومان به تابع است. به این آرگومان در کاتلین continuation گفته میشود. در یک کلام continuation شبیه callback است، اما به طور کلی بیشتر در سطح سیستم. علاوه براین coroutinesها را میتوان به حالت تعبیق درآورد و دوباره از سرگرفت. این یعنی که میتوانید یک کار طولانی مدت را به تدریج انجام دهید. بنابراین شما میتوانید هر چند بار مکث کنید و وقتی میخواهید دوباره از آن استفاده کنید آن را از سربگیرید. نکته مهم این است که استفاده از تعداد زیادی coroutines باعث ایجاد سربار حافظه برای برنامه شما نمیشود.برای اطلاعات بیشتر، اگر یک coroutines در کاتلین به حالت تعلیق درآید، پشته فعلی در کاتلین کپی میشود و برای مصارف بعدی ذخیره میشود. هنگامی که از سرگرفتن آن را شروع میکنید پشته از جایی که در آن ذخیره شد است کپی میشود و دوباره شروع به کار میکند. همچنین در صورت تعلیق تمام coroutinesها، thread اصلی برای کارهای عادی آزاد خواهد بود. علاوه براین main-safety یکی دیگر از ویژگیهای coroutines در کاتلین است. به طور خلاصه این یعنی که توابع قابل تعلیق هنگام صدا زدن در در thread اصلی بیخطر هستند. آنها همیشه باید اجازه دهند که هر thread بتواند آنها را صدا بزنند بدون اینکه بدانند چه کاریی میخواهند انجام دهند.
Coroutines = Co + Routines
Co یعنی همکاری و Routine یعنی توابع، که این یعنی وقتی توابع با هم همکاری میکنند، آنها را Coroutine مینامیم.
توابع قابل تعلیق
توابع قابل تعلیق، اجرای coroutine فعلی را بدون مسدود کردن thread فعلی به تعلیق در میآورد. همچنین به جای بازگشت یک مقدار، میداند که صدازننده در کدام context آنرا معلق میکند. بنابراین با استفاده از این ویژگی میتواند در وقت مناسب از سرگرفته شود. این بیشتر به بهینهسازی حافظه، cpu و چند وظیفگی کمک میکند. علیرغم مسدود کردن thread، توابع تعلیق ارزانتر هستند زیرا نیازی به تعویض context ندارند. با اضافه کردن کلید واژه suspend به یک تابع میتوان آن را قابل تعلیق کرد. توابع قابل تعلیق فقط میتوانند از یک coroutine و یا یک تابع قابل تعلیق دیگر صدا زده شوند.
suspend fun sampleSuspend(){
println("Kotlin Coroutines")
}
Coroutine Builder
در اصل نمیتوان توابع قابل تعلیق را از توابع معمولی فراخوانی کرد. بنابراین کتابخانه مجموعهای از توابع را برای شروع Coroutine از آنها فراهم کرده است. تعداد زیادی coroutine builder توسط کاتلین ارائه شده است، از جمله:
- Launch: این یک coroutine جدید ایجاد میکند. که فقط شلیک و فراموش میکند، و برای پاسخ منتظر نمیماند. اگر یک استثناء ناخواسته رخ دهد، این باعث میشود جریان برنامه دچار اختلال شود
- Async: این نوع شلیک میکند و برای پاسخ منتظر میماند
- runBlocking: این مشابه launch است به جز اینکه در داخل runBlocking همه چیز در داخل همان coroutine خواهد بود
- run: این یک coroutine پایه است
- CoroutineScope: این به تعریف چرخه عمر coroutine کمک میکند. که میتواند در تمام اپلیکیشن باشد و یا محدود به یک اکتیویتی باشد
- Dispatchers: این thread poolها را برای راهاندازی coroutine تعریف میکند
همانطور که در بالا مشخص است دو راهکار برای شروع coroutine در کاتلین با کاربردهای مختلف وجود دارد:
- Launch builder: برای جواب منتظر نمیماند
- Async builder: یک coroutine جدید شروع میکند، و به شما امکان میدهد نتیجهای را برگردانید(نمونهای از Deffered<T> را برگردانید)، با تابع قابل تعلیق به نام await.
برای مثال:
suspend fun sampleSuspend() {
println("Kotlin Coroutines")
}
fun main(args: Array<String>) = runBlocking {
println("Start")
val x = launch(CommonPool) {
delay(2000)
sampleSuspend()
println("launch")
}
println("Finish")
x.join() //The sampleSuspend method does not run because the function just
only fires and forgets. We should use the join function, which waits for the completion.
}
خروجی کدهای فوق عبارت است از: start، finish، kotlin coroutine و launch که به ترتیب و به صورت عمودی چاپ نمیشوند.
تفاوت بین join و await این است که join منتظر تکمیل launch است. و Await منتظر برگشت نتیجه است.
Dispatchers
تمام coroutineها در کاتلین باید در dispatcher اجرا شوند حتی اگر در thread اصلی کار کنند. Coroutineها میتوانند خود را به حالت تعلیق درآورند و dispatcher عنصری است که میداند چگونه آنها را از سرگیری کند. میتوانید از disoatcherها در شرایط زیر استفاده کنید:
- Dispatchers.Default: کارهای فشرده CPU، مانند مرتب کردن لیستهای بزرگ، انجام محاسبات پیچیده.
- Dispatchers.IO: کارهای شبکه یا خواندن و نوشتن در پروندهها(هرگونه ورودی یا خروجی)
- Dispatchers.Main: dispatchers پیشنهاد شده برای رویدادهای مربوط به Ui مانند نشان دادن لیت در recyclerView، بروزرسانی view، و غیره.
برای مثال:
suspend fun fetchUser(): User {
return GlobalScope.async(Dispatchers.IO) {
// make network call
// return user
}.await()
}
دامنهها در Coroutine
دامنهها در coroutine بسیار مفید هستند، زیرا ما به محض نابودی اکتیویتی باید کارهای پسزمینه را لغو کنیم. Coroutineها در کاتلین باید در یک عنصر به نام CoroutineScope اجرا شوند. CoroutineScope، Coroutine شما را ردیابی میکند حتی Coroutineهایی که به حالت تعلیق درآمدهاند. به عبارت دیگر CoroutineScope فقط اطمینان حاصل میکند که آنها را ردیابی میکنید، و تمام coroutineهایی که در آن شروع شده است را لغو میکند.
هنگامی که ما نیاز به استفاده از دامنه سراسری داریم، که این دامنه برای کل اپلیکیشن است نه دامنه اکتیویتی، میتوانیم از GlobalScope استفاده کنیم.
withContext
withContext روشی دیگر برای نوشتن async است زمانی که نیاز به نوشتن await() نداریم. برای مثال:
suspend fun fetchUser(): User {
return withContext(Dispatchers.IO) {
// make network call
// return user
}
}
در نتیجه coroutine در کاتلین یک چارچوب بسیار کارآمد و کامل برای مدیریت همزمانی با رویکردی ساده، جامع و قوی است. در این مقاله برخی از مفاهیم اساسی Coroutineها که برای توسعهدهندگان اندروید در نظر گرفته شده است را بررسی کردیم.
دیدگاه و پرسش
در حال دریافت نظرات از سرور، لطفا منتظر بمانید
در حال دریافت نظرات از سرور، لطفا منتظر بمانید