Kotlin中的協(xié)程 - suspend

前言

Kotlin是一種在Java虛擬機(jī)上運(yùn)行的靜態(tài)類型編程語言势腮,被稱之為Android世界的Swift丹壕,在GoogleI/O2017中抵赢,Google宣布Kotlin成為Android官方開發(fā)語言

delay

delay是一個(gè)頂級函數(shù)粗井,由于它被suspend修飾,所以只能用在協(xié)程或者被其他suspend函數(shù)修飾肄梨,它的功能為

將當(dāng)前協(xié)程延遲一個(gè)給定時(shí)間,但是不會(huì)阻塞當(dāng)前線程 并且它也是可以被取消的

public suspend fun delay(timeMillis: Long) {
    if (timeMillis <= 0) return // don't delay
    return suspendCancellableCoroutine sc@ { cont: CancellableContinuation<Unit> ->
        cont.context.delay.scheduleResumeAfterDelay(timeMillis, cont)
    }
}

我們再看下另一個(gè)會(huì)延遲的函數(shù)Thread.sleep()它會(huì)導(dǎo)致 當(dāng)前線程進(jìn)行休眠

    /**
     * Causes the currently executing thread to sleep (temporarily cease
     * execution) for the specified number of milliseconds plus the specified
     * number of nanoseconds, subject to the precision and accuracy of system
     * timers and schedulers. The thread does not lose ownership of any
     * monitors.
     */
    public static void sleep(long millis, int nanos)

在協(xié)程中使用它們膘格,會(huì)有不同的效果峭范,如果我們的協(xié)程都在同一個(gè)線程

fun test() {
    MainScope().launch {
        Log.e("Mike","Coroutine1 start")
        delay(2000)
        Log.e("Mike","Coroutine1 end")
    }

    MainScope().launch {
        Log.e("Mike","Coroutine2 start")
        delay(2000)
        Log.e("Mike","Coroutine2 end")
    }
}

上述的代碼在使用delay時(shí)打印财松,當(dāng)前線程沒有阻塞會(huì)執(zhí)行Coroutine2

Coroutine1 start
Coroutine2 start
//兩秒后
Coroutine1 end
Coroutine2 end

上述的代碼在使用Thread.sleep()時(shí)打印

Coroutine1 start
//兩秒后
Coroutine1 end
Coroutine2 start
//兩秒后
Coroutine2 end

很容易看到這兩種用法的區(qū)別瘪贱,并且當(dāng)我們在協(xié)程中時(shí)如果使用了阻塞線程的Thread.sleep()也會(huì)有警告提示Inappropriate blocking method call 提示你使用了不適當(dāng)?shù)淖枞椒ǎ驗(yàn)榫€程阻塞會(huì)導(dǎo)致其他協(xié)程無法執(zhí)行辆毡,會(huì)影響其他協(xié)程菜秦,delay表示的是非阻塞調(diào)用,不會(huì)阻塞當(dāng)前線程

非阻塞式掛起

我們很容易理解阻塞與非阻塞的區(qū)別舶掖,從行為上來講球昨,就是是否擋住了你這條線程的后續(xù)執(zhí)行,如果擋住了就是阻塞眨攘,沒有擋住就是非阻塞主慰,那么掛起是什么,掛起的行為其實(shí)就是切換了線程的工作

    MainScope().launch {
        delay(2000)
    }

    MainScope().launch {
        delay(2000)
    }

在上述例子中鲫售,第一個(gè)協(xié)程創(chuàng)建完成之后就會(huì)被掛起共螺,主線程的執(zhí)行由當(dāng)前協(xié)程切換到了下一個(gè)協(xié)程,當(dāng)掛起兩秒之后再將主線程切換回了第一個(gè)協(xié)程繼續(xù)工作情竹,掛起其實(shí)也就是一種協(xié)程的暫停行為藐不,不會(huì)線程中的其他單元

suspend

suspend是協(xié)程中很重的關(guān)鍵字,它用來修飾函數(shù)秦效,表示此函數(shù)是一個(gè)會(huì)掛起的函數(shù)雏蛮,并且 掛起函數(shù)只有在協(xié)程中使用或者被另一個(gè)掛起函數(shù)調(diào)用,可以暫停和進(jìn)行恢復(fù)阱州,什么情況下需要用到掛起函數(shù)

  • 線程切換挑秉,掛起本身是線程切換不同的協(xié)程去工作,所以當(dāng)需要進(jìn)行線程切換時(shí)可以使用掛起函數(shù)
  • 延時(shí)苔货,暫停往往代表在等待一些結(jié)果犀概,當(dāng)我們在等待一些返回結(jié)果時(shí),協(xié)程可以通過掛起的方式等待蒲赂,而不阻塞線程

suspend只是對函數(shù)的一個(gè)標(biāo)識(shí)別阱冶,它不像inline,refied等關(guān)鍵字一樣會(huì)對代碼造成影響,而是提醒使用者這是一個(gè)掛起函數(shù)滥嘴,具體的掛起業(yè)務(wù)還是需要函數(shù)內(nèi)部自己實(shí)現(xiàn)

withContext

withContext是一個(gè)掛起函數(shù)木蹬,表明它只能在協(xié)程或者其他suspend函數(shù)調(diào)用

/**
 * Calls the specified suspending block with a given coroutine context, suspends until it completes, and returns
 * the result.
 *
 * The resulting context for the [block] is derived by merging the current [coroutineContext] with the
 * specified [context] using `coroutineContext + context` (see [CoroutineContext.plus]).
 * This suspending function is cancellable. It immediately checks for cancellation of
 * the resulting context and throws [CancellationException] if it is not [active][CoroutineContext.isActive].
 *
 * This function uses dispatcher from the new context, shifting execution of the [block] into the
 * different thread if a new dispatcher is specified, and back to the original dispatcher
 * when it completes. Note that the result of `withContext` invocation is
 * dispatched into the original context in a cancellable way, which means that if the original [coroutineContext],
 * in which `withContext` was invoked, is cancelled by the time its dispatcher starts to execute the code,
 * it discards the result of `withContext` and throws [CancellationException].
 */
public suspend fun <T> withContext

需要傳入一個(gè)suspending 代碼塊,并且基于合并后的Context執(zhí)行環(huán)境,并且可以被取消镊叁,會(huì)返回代碼塊的執(zhí)行結(jié)果尘颓,在suspending 代碼塊執(zhí)行完畢之后有切換回來

fun test() {
    MainScope().launch {
        Log.e("Mike", "Coroutine start")
        val result = withContext(Dispatchers.IO) {
            delay(2000)
            "resposne data"
        }
        Log.e("Mike", "Coroutine end $result")
    }
}
打印結(jié)果
Coroutine start 主線程
兩秒后
Coroutine end resposne data 主線程

可以提取出suspend函數(shù),這樣我們的代碼看起來是同步單線程執(zhí)行的晦譬,但是實(shí)際卻在不同的線程

fun test() {
    MainScope().launch {
        Log.e("Mike", "Coroutine start") //主線程
        val result = getData() //getData在IO線程
        Log.e("Mike", "Coroutine end $result") //主線程
    }
}

suspend fun getData() = withContext(Dispatchers.IO) {
    delay(2000)
    "resposne data"
}

suspend使用案例

文件讀取

private val scope = CoroutineScope(Dispatchers.Main)

scope.launch {
    Toast.makeText(this@MainActivity, "start...", Toast.LENGTH_SHORT).show()
    val result = readFile()
    Toast.makeText(this@MainActivity, "end..." + result, Toast.LENGTH_LONG).show()
}

suspend fun readFile() = withContext(Dispatchers.IO) {
    val inp = assets.open("a.txt")
    val isr = InputStreamReader(inp)
    isr.readText().also {
        inp.close()
        isr.close()
    }
}

網(wǎng)絡(luò)請求

引入Okhttp

implementation("com.squareup.okhttp3:okhttp:4.9.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.9.0")

封裝OkhttpClient

object OkHttpManager {

    fun getOkhttpClient() = OkHttpClient.Builder()
        .addNetworkInterceptor(HttpLoggingInterceptor().apply {
            level = HttpLoggingInterceptor.Level.BODY
        })
        .build()
}

封裝Sevice

class RequestService(val okhttpClient: OkHttpClient) {

    suspend fun getInfo() = withContext(Dispatchers.IO) {
        val url = "https://www.baidu.com".toHttpUrl().newBuilder().build()
        val request = Request.Builder().url(url).build()
        okhttpClient.newCall(request).execute().body?.string()
    }
}

發(fā)起調(diào)用

scope.launch {
    Toast.makeText(this@MainActivity, "start...", Toast.LENGTH_SHORT).show()
    val result = RequestService(OkHttpManager.getOkhttpClient()).getInfo()
    Toast.makeText(this@MainActivity, "end..." + result, Toast.LENGTH_LONG).show()
}

歡迎關(guān)注Mike的簡書

Android 知識(shí)整理

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末疤苹,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子敛腌,更是在濱河造成了極大的恐慌卧土,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件像樊,死亡現(xiàn)場離奇詭異尤莺,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)生棍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進(jìn)店門颤霎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人涂滴,你說我怎么就攤上這事友酱。” “怎么了柔纵?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵缔杉,是天一觀的道長。 經(jīng)常有香客問我首量,道長壮吩,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任加缘,我火速辦了婚禮鸭叙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘拣宏。我一直安慰自己沈贝,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布勋乾。 她就那樣靜靜地躺著宋下,像睡著了一般。 火紅的嫁衣襯著肌膚如雪辑莫。 梳的紋絲不亂的頭發(fā)上学歧,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天,我揣著相機(jī)與錄音各吨,去河邊找鬼枝笨。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的横浑。 我是一名探鬼主播剔桨,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼徙融!你這毒婦竟也來了洒缀?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤欺冀,失蹤者是張志新(化名)和其女友劉穎树绩,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體脚猾,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡葱峡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了龙助。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,625評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蛛芥,死狀恐怖提鸟,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情仅淑,我是刑警寧澤称勋,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站涯竟,受9級特大地震影響赡鲜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜庐船,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一银酬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧筐钟,春花似錦揩瞪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至壹将,卻和暖如春嗤攻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背诽俯。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工妇菱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓恶耽,卻偏偏與公主長得像密任,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子偷俭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,492評論 2 348

推薦閱讀更多精彩內(nèi)容