تعرف على Kotlin coroutines : الجزء الثالث

ها قد عدنا مرة أخرى، حاول تفتكر سريعًا احنا شرحنا ايه في الجزء الأول و التاني عشان فيه جزء كبير هنا مترتب عليه، في الجزء دا هنشرح حاجة مهمة جدًا وهي async و await وايه الفرق بينهم وبين الطريقة العادية Launch.

لو رجعت على الجزء الأول هتعرف أن Launch بتبدأ coroutines متفرقين وبترجع Job، بس في أغلب الحالات في التطبيقات اليومية الللي بنعملها بنكون عايزين نعمل Task بياخد وقت عشان نرجع منه قيمة، يعني مثال على كدا لما تعمل Network request عشان تحمل صورة، دا يعتبر Heavy task وهياخد وقت كبير وعايزينه أول ما يخلص يعملنا return بالصورة سواء كانت Bitmap او Drawable حسب الLogic اللي أنت عامله في التطبيق.

async :

هنا السحر بيجي، async بتشتغل نفس شغل Launch بالظبط وهي أنها بتبدأ Coroutines بس الفرق بقا أنها بعد ما بتخلص الCoroutines بترجعلك قيمة اللي هي في الأغلب الحاجة اللي انت كنت بتعملها Process زي مثال الصورة، أو انك تجيب داتا من Retrofit مثلاً

طيب ازاي هنستخدم async، الموضوع سهل جدًا وتقريبًا نفس Launch، شوف المثال البسيط اللي تحت وهنبدأ نشرحه

val result : Deferred<String> = CoroutineScope(IO).async{
                fakeHeavyWork()
                "A String return value"
            }

هنلاحظ هنا كام حاجة مختلة، أولها أننا حددنا قيمة من نوع Deferred من نوع String، يعني ايه Deferred؟، هي Return Value عامة بتاعت async، عشان تقول لCoroutine أن الValue ديه جاية أو هتستخدم في Coroutine تانية، وبنعملها Wrap اللي هي String اللي فوق عشان نقول النوع الاساسي اللي احنا عايزينه بقا من Coroutine ديه، يعني بالاختصار أنك لو عايز تستخدم async الReturn value لازم يكون Deferred، ويكون بين <> النوع الاساسي اللي انت عايز ترجعه

لاحظ نقطة كمان أن مكتبناس Return بالقيمة، لأن هي lambada، اللي انت بتكتب بس القيمة اللي هترجع على طول، ممكن تعمل بحث سريع عنها، لو مش فاهم شوف المثال اللي تحت:

    private lateinit var  IntResult : Deferred<Int>

    private lateinit var  bitmapResult : Deferred<Bitmap>
ش
    private lateinit var  UserResult : Deferred<User>

التلت أمثلة اللي فوق كل واحد منهم هيستخدم في بدأ Coroutine، هيتعمل عليه process بياخد وقت كبير وفي الاخر هيرجع Value من نوع wrap، يعني المثال الأول هترجع Int، المثال التاني هيرجع Bitmap، حتى انت ممكن تعمل Model وتخليه يرجع القيمة زي مثلا User يكون جواه الUsername والباسورد مثلاً

await :

كدا احنا عارفنا ازاي بنعمل Coroutine بإستخدام async، دلوقتي جه الوقت عشان نستخدم القيمة اللي هتطلع من الCoroutine ودا هيكون بإستخدام await اللي هي Method موجودة جوا Deferred وبكدا هترجع القيمة اللي أنت كنت عاملها Wrap، شوف المثال اللي جي:

 private fun initCoroutine(){
        CoroutineScope(IO).launch {

            val result: Deferred<Bitmap> = CoroutineScope(IO).async {
                getBitmapFromNetwork()
            }

            setProfilePic(result.await())
        }
    }

    private suspend fun getBitmapFromNetwork() : Bitmap{
        //Network request to get the Bitmap
        //process the Bitmap
        return bitmap
    }

    private  fun setProfilePic(bitmap: Bitmap){
        withContext(Main){
        imageView.setImageBitmap(bitmap)
       }        
    }

دا مثال بسيط جدًا للتوضيح فقط، احنا هنا كأننا بنجيب صورة من Network وبنحطها في ImageView، وطبعًا مادام Network يبقى أكيد هياخد وقت فأحنا هنعمل CoroutineScope وجواها هنستدعي Suspend function تعمل الProcess وترجع الBimap هتكون محفوظة في Deffered اللي اسمه result وبعدين نستدعي “setProfilePic” ونمرر الBitmap اللي جبناها عن طريق نستخدم await، اللي بتحول Deferred للنوع اللي احنا عايزينه واللي عملنلها Wrap.

لو عايز تتعامل مع Coroutine أنها ترجع القيمة الاساسية من غير Deffered ممكن تعمل الآتي :

  val result: Bitmap = CoroutineScope(IO).async {
                getBitmapFromNetwork()
            }.await()

أحنا زونا هنا await في الأخر، وشيلنا Deferred عشان زي ما قولنا فوق أن await برتجع القيمة الاساسية، وبقا كدا نوع result هو Bitmap مش Deferred، بس حط في ذهنك أنك كدا هتوقف الparent coroutine لحد ما تخلص ويجيب الBitmap، ودا بيكون كويس لو الparent job فيه اتنين Jobs، التانية فيها بتعتمد على أول، يعني لازم الأول تخلص عشان التانية تبدأ تتعمل، او أن التانية بتستخدم الناتج بتاع الjob الأول.

Suspend function ‘await’ should be called only from a coroutine or another suspend function

دا الايرور اللي هيظهرك في Android Studio لما تحاول تستخدم await من مكان تاني غير Coroutine أو Suspend function، لأنها Asynchronous task، لو رجعت على الجزء الأول والتاني هتفهم أحنا بنقول ايه، فخلي بالك من النقطة ديه.

كدا خلصنا شرح الasync وawait المرة ديه، ولحد كبير أنت ممكن تبدأ تستخدمها في مشروعاتك، الجزء الجي هنركز على Cancellation و Exception، والجزء اللي بعده هيكون عن Kotlin Flow، لو فيه أي سؤال ممكن تحطه في التعلقيات هنا أو على الجروب.

الكاتب: Mohamed Saber

Pharmacist, Android developer and UI/UX enthusiast

(2) تعليقات

  1. انت ليه فى ميثود initCoroutine حطيت CoroutineScope(IO).async جوا CoroutineScope(IO).launch ؟ هو ينفع اعمل الميثود دى CoroutineScope(IO).async وبعدها انادى setProfilePic ولا لازم فى ميثود واحده

اترك ردا