คำนำ
Kotlin Coroutines เป็น API แบบอะซิงโครนัสใหม่ที่เปิดตัวโดย Kotlin ไม่ใช่ทางออกที่ดีที่สุดสำหรับปัญหาทั้งหมด แต่หวังว่าในหลาย ๆ กรณีมันจะทำให้สิ่งต่าง ๆ ง่ายขึ้นเล็กน้อย ที่นี่ฉันจะแสดงแผนการใช้งานเฉพาะของห้องสมุดนี้ใน Android ฉันจะไม่พูดด้านล่างมากนักลองมาดูการแนะนำรายละเอียดด้วยกัน
แนะนำ coroutines
// เพิ่มรหัสต่อไปนี้ในโหนด Android ในไฟล์ build.gradle ของแอปพลิเคชัน kotlin {experimental {coroutines 'enable'}} // เพิ่มสองบรรทัดต่อไปนี้ในการใช้งานการพึ่งพา "org.jetbrains.kotlinx: kotlinx-coroutines-core: 020ตัวอย่าง coroutines แรก
โดยปกติเราจะโหลดภาพลงใน ImageView และงานโหลดแบบอะซิงโครนัสมีดังนี้:
Fun Loadbitmapfrommediastore (ImageId: int, imagesbaseuri: uri): บิตแมป {val uri = uri.withappendedpath (imagesbaseuri, imageId.toString ()) ส่งคืน mediastore.images.media.getBitMap (ContentResolver, uri)}}}วิธีนี้จะต้องดำเนินการในเธรดพื้นหลังเนื่องจากเป็นของการดำเนินการ IO ซึ่งหมายความว่าเรามีวิธีแก้ปัญหามากมายในการเริ่มงานพื้นหลังและเมื่อวิธีการส่งคืนบิตแมปเราต้องแสดงใน ImageView ทันที
ImageView.SetImageBitMap (บิตแมป)
บรรทัดของรหัสนี้จะต้องดำเนินการในเธรดหลักมิฉะนั้นจะผิดพลาด
หากรหัสสามบรรทัดข้างต้นถูกเขียนเข้าด้วยกันโปรแกรมจะติดหรือขัดข้องซึ่งขึ้นอยู่กับการเลือกเธรดที่เหมาะสม ต่อไปลองมาดูกันว่า coroutines โดยใช้ Kotlin แก้ปัญหานี้ได้อย่างไร:
Val Job = Launch (พื้นหลัง) {val uri = uri.withappendedPath (imagesbaseuri, imageId.toString ()) val bitmap = mediastore.images.media.getbitmap (ContentResolver, Launch (UI) {imageView.SetImageBitMapสิ่งที่สำคัญที่สุดที่นี่คือการเปิดตัว () และพื้นหลังพารามิเตอร์และ UI เปิดตัว () หมายถึงการสร้างและเริ่มต้น coroutine พารามิเตอร์พื้นหลัง coroutinecontext ใช้เพื่อให้แน่ใจว่าการดำเนินการในเธรดพื้นหลังเพื่อให้แน่ใจว่าแอปพลิเคชันจะไม่ติดหรือขัดข้อง คุณสามารถประกาศ coroutinecontext ตามที่แสดงด้านล่าง
พื้นหลังวาลภายใน = newFixedThreadPoolContext (2, "BG")
สิ่งนี้สร้างบริบทใหม่และใช้สองเธรดปกติเมื่อดำเนินงาน
ถัดไปเปิดตัว (UI) ซึ่งจะกระตุ้น coroutine อื่นซึ่งจะดำเนินการบน Android
เธรดหลัก
ยกเลิกได้
ความท้าทายต่อไปคือการจัดการกับสิ่งต่าง ๆ ที่เกี่ยวข้องกับวัฏจักรการประกาศกิจกรรม เมื่อคุณโหลดงานและออกจากกิจกรรมก่อนที่จะดำเนินการเสร็จสิ้นมันจะทำให้เกิดความผิดพลาดเมื่อโทรหา imageView.setImageBitmap(bitmap) ดังนั้นเราต้องยกเลิกงานก่อนออกจากกิจกรรม ที่นี่เราใช้ค่าส่งคืนของวิธีการเปิดใช้งาน () เมื่อกิจกรรมเรียกวิธีการบนสต็อปเราจำเป็นต้องใช้งานเพื่อยกเลิกงาน
Job.cancel ()
มันเหมือนกับการโทรออกเมื่อคุณใช้ RXJAVA และการเรียกใช้ฟังก์ชั่นยกเลิกเมื่อคุณใช้ ASYNCTASK
Lifecycleobserver
ส่วนประกอบสถาปัตยกรรม Android ให้นักพัฒนา Android ที่มีห้องสมุดที่ทรงพลังมากมายซึ่งหนึ่งในนั้นคือ Lifecycle API มันให้วิธีง่ายๆในการฟังวงจรชีวิตของกิจกรรมและชิ้นส่วนแบบเรียลไทม์ มากำหนดรหัสเพื่อใช้กับ coroutines
คลาส coroutinelifecycleListener (val referred: deferred <*>): lifecyclebserver {@onlifecycleevent (lifecycle.event.on_destroy) fun cancelcoroutine () {if (!เราสร้างฟังก์ชั่นส่วนขยาย LifeCycledowner:
fun <t> lifecycleowner.load (loader: () -> t): รอการไกล <t> {val deferred = async (บริบท = พื้นหลัง, start = coroutinestart.lazy) {loader ()} lifecycle.addobserver มีสิ่งใหม่ ๆ มากเกินไปในวิธีนี้และฉันจะอธิบายพวกเขาทีละคน:
ตอนนี้เราสามารถเรียก load() ในกิจกรรมหรือชิ้นส่วนและเข้าถึงสมาชิกวงจรชีวิตจากฟังก์ชั่นนั้นและเพิ่ม coroutinelifecycleListener ของเราเป็นผู้สังเกตการณ์
วิธีการโหลดต้องการตัวโหลดเป็นพารามิเตอร์การส่งคืนประเภททั่วไป T ในวิธีการโหลดเราเรียกฟังก์ชัน coroutine creator async () ซึ่งจะใช้บริบท coroutine พื้นหลังเพื่อดำเนินการงานในเธรดพื้นหลัง โปรดทราบว่าวิธีนี้มีพารามิเตอร์อื่น start = coroutinestart.lazy ซึ่งหมายความว่า coroutine จะไม่ถูกดำเนินการทันทีจนกว่าจะมีการเรียก
จากนั้น Coroutine จะส่งคืนวัตถุ Defered<T> ไปให้กับผู้โทรซึ่งคล้ายกับงานก่อนหน้าของเรา แต่ก็สามารถมีค่าล่าช้าเช่น JavaScript Promise หรือ Future <T> ใน Java API ปกติและดียิ่งกว่านั้นมันมีวิธีการรอคอย
ต่อไปเราจะกำหนดฟังก์ชันส่วนขยายอื่น then() คราวนี้เรากำหนดไว้ข้างต้น Deferen<T> ซึ่งเป็นประเภทที่ส่งคืนโดยวิธีการโหลดของเราด้านบน นอกจากนี้ยังใช้แลมบ์ดาเป็นพารามิเตอร์ชื่อบล็อกซึ่งใช้วัตถุประเภทเดียวของ Type T เป็นพารามิเตอร์
infix fun <t> deferred <t> .then (block: (t) -> หน่วย): งาน {return launch (context = ui) {block ([email protected] ())}} ฟังก์ชั่นนี้จะสร้าง coroutine อื่นโดยใช้ฟังก์ชัน launch() ซึ่งจะทำงานบนเธรดหลักในครั้งนี้ แลมบ์ดา (ชื่อบล็อก) ผ่านไปยัง coroutine นี้ใช้ค่าของวัตถุรอการตัดบัญชีที่เสร็จสมบูรณ์เป็นพารามิเตอร์ เราเรียกว่า await() เพื่อระงับการดำเนินการของ coroutine นี้จนกว่าวัตถุรอการตัดบัญชีจะส่งคืนค่า
นี่คือที่ที่ coroutine น่าประทับใจมาก การโทรไป await() จะทำในเธรดหลัก แต่ไม่ได้บล็อกการดำเนินการเพิ่มเติมของเธรดนั้น มันจะหยุดการดำเนินการของฟังก์ชั่นชั่วคราวจนกว่าจะพร้อมเมื่อมันกลับมาทำงานต่อและส่งผ่านค่าล่าช้าไปยังแลมบ์ดา เมื่อ coroutine ถูกระงับเธรดหลักสามารถดำเนินการต่อไปได้ ฟังก์ชั่นการรอคอยเป็นแนวคิดหลักใน coroutine สิ่งที่สร้างสิ่งที่น่าอัศจรรย์ทั้งหมด
ผู้สังเกตการณ์ Lifecycle ที่เพิ่มเข้ามาในฟังก์ชั่น load() จะยกเลิก coroutine แรกหลังจากเรียก onDestroy() ในกิจกรรมของเรา สิ่งนี้จะทำให้ coroutine ที่สองถูกยกเลิกป้องกันไม่ให้ block() ถูกเรียก
Kotlin Coroutine DSL
ตอนนี้เรามีฟังก์ชั่นส่วนขยายสองฟังก์
โหลด {loadbitmapfrommediastore (imageId, imagesbaseuri)} จากนั้น {imageView.SetImageBitMap (มัน)} ในรหัสข้างต้นเราผ่านวิธี Lambda ไปยังฟังก์ชันโหลดซึ่งเรียกใช้เมธอด loadbitmapfrommediastore ซึ่งจะต้องดำเนินการบนเธรดพื้นหลังจนกว่าวิธีการส่งคืนบิตแมปและค่าคืนของวิธีการโหลดจะ Deferred<Bitmap>
เป็นฟังก์ชันส่วนขยายวิธีการ then() ใช้การประกาศ Infix แม้ว่าวิธีการโหลดจะส่งคืน Deferred<Bitmap> แต่จะถูกส่งผ่านไปยังวิธีการนั้นเป็นค่าส่งคืนบิตแมปดังนั้นเราจึงสามารถเรียก imageView.setImageBitmap(it) โดยตรงในวิธีนี้
รหัสข้างต้นสามารถใช้สำหรับการโทรแบบอะซิงโครนัสใด ๆ ที่จำเป็นต้องเกิดขึ้นในเธรดพื้นหลังและที่ควรส่งคืนค่าส่งคืนไปยังเธรดหลักเช่นในตัวอย่างข้างต้น มันไม่ได้โทรหลายครั้งตามที่ Rxjava ทำ แต่อ่านง่ายกว่าและอาจครอบคลุมกรณีที่พบบ่อยที่สุด ตอนนี้คุณสามารถทำสิ่งนี้ได้อย่างปลอดภัยโดยไม่ต้องกังวลเกี่ยวกับการทำให้เกิดการรั่วไหลของบริบทหรือการประมวลผลเธรดในการโทรทุกครั้ง
โหลด {restapi.fetchData (Query)} จากนั้น {adapter.display (มัน)}จากนั้น () และ load () วิธีการเป็นเพียงส่วนเล็ก ๆ ของภูเขาน้ำแข็งของห้องสมุดใหม่นี้ แต่ฉันหวังว่าสิ่งที่คล้ายกันจะปรากฏในไลบรารี Android ที่ใช้ Kotlin ในอนาคตเมื่อเวอร์ชัน coroutine มาถึงเวอร์ชันที่เสถียร ก่อนหน้านี้คุณสามารถใช้หรือแก้ไขรหัสด้านบนหรือตรวจสอบ Anko Coroutines ฉันยังเปิดตัวเวอร์ชันที่สมบูรณ์มากขึ้นใน GitHub (https://github.com/erikhellman/kotlinasyncWithCoroutines (ดาวน์โหลดท้องถิ่น))
สรุป
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่าเนื้อหาของบทความนี้จะมีค่าอ้างอิงบางอย่างสำหรับการศึกษาหรือที่ทำงานของทุกคน หากคุณมีคำถามใด ๆ คุณสามารถฝากข้อความไว้เพื่อสื่อสาร ขอบคุณสำหรับการสนับสนุน Wulin.com