一些學習kotlin的知識小點倡勇。。

1. kotlin是init先執(zhí)行還是成員變量先執(zhí)行?

誰寫前面誰先執(zhí)行妻熊。

2. kotlin+databing 無法生成BR文件夸浅。

手動執(zhí)行該task試試。

3. SparseArray put 重復的id會怎么樣

val sparseArray = SparseArray<String>()
sparseArray.put(1, "one")
sparseArray.put(1, "two")

sparseArray 中鍵為 1 的值將為 "two"

4.生命感知組件協(xié)程范圍

ViewModelScope

在這個范圍內(nèi)啟動的協(xié)程會在viewmodel清除時自動取消扔役,可以避免消耗資源

    init {
        viewModelScope.launch {
            // Coroutine that will be canceled when the ViewModel is cleared.
        }
    }
}

LifecycleScope

每個 Lifecycle對象都定義了 LifecycleScope,在此范圍內(nèi)啟動的協(xié)程會在 Lifecycle 被銷毀時取消.可以通過lifecycle.coroutineScopelifecycleOwner.lifecycleScope屬性訪問

class MyFragment: Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        viewLifecycleOwner.lifecycleScope.launch {
            ...
        }
    }
}

5.協(xié)程

一帆喇、概念

協(xié)程可以使用阻塞的方式寫出非阻塞的代碼,解決并發(fā)常用的回調(diào)問題亿胸。

二坯钦、使用
GlobalScope.launch(Dispatchers.Main) {
    val res = getResult(2)
    mNumTv.text = res.toString()
}

分三部分:協(xié)程作用域(GlobalScope)、調(diào)度器(Dispatchers)侈玄、掛起函數(shù)

2.1 協(xié)程作用域

runBlocking:頂層函數(shù)婉刀,它和 coroutineScope 不一樣,它會阻塞當前線程來等待序仙,所以這個方法在業(yè)務中并不適用突颊;

fun main() = runBlocking {
        testFlow().collect {
            Log.i("mali_test", "main: $it")
        }
    }

GlobalScope:全局協(xié)程作用域,可以在整個應用的聲明周期中操作潘悼,且不能取消律秃,所以仍用的時候要慎重,可能會引起泄漏

lifecycleScope:生命周期組件作用域治唤,每個 Lifecycle對象都定義了 LifecycleScope,在此范圍內(nèi)啟動的協(xié)程會在 Lifecycle 被銷毀時取消.可以通過lifecycle.coroutineScopelifecycleOwner.lifecycleScope屬性訪問

 viewLifecycleOwner.lifecycleScope.launch {
            ...
        }

ViewModelScope:協(xié)程會在viewmodel清除時自動取消棒动,可以避免消耗資源

自定義作用域:

class MainActivity : AppCompatActivity() {  
  // 1. 創(chuàng)建一個 MainScope  
  val scope = MainScope()  
 
  override fun onCreate(savedInstanceState: Bundle?) {  
    super.onCreate(savedInstanceState)  
    setContentView(R.layout.activity_main)  
 
    // 2. 啟動協(xié)程  
    scope.launch(Dispatchers.Unconfined) {  
      val one = getResult(20)  
      val two = getResult(40)  
      mNumTv.text = (one + two).toString()  
    }  
  }  
 
  // 3. 銷毀的時候釋放  
  override fun onDestroy() {  
    super.onDestroy()  
 
    scope.cancel()  
  }  
 
  private suspend fun getResult(num: Int): Int {  
    delay(5000)  
    return num * num  
  }  
}  
2.2 調(diào)度器

作用:限制協(xié)程在什么線程執(zhí)行,主要的調(diào)度器有:

  • Dispatchers.Main:指定執(zhí)行的線程是主線程宾添,
  • Dispatchers.IO:指定執(zhí)行的線程是 IO 線程迁客;
  • Dispatchers.Default:默認的調(diào)度器,適合執(zhí)行 CPU 密集性的任務辞槐;
  • Dispatchers.Unconfined:非限制的調(diào)度器,指定的線程可能會隨著掛起的函數(shù)的發(fā)生變化粘室;
2.3 掛機函數(shù)
1691651194224.png

launch榄檬,啟動一個新的協(xié)程,返回一個job衔统,可以通過cancel取消這個協(xié)程

async鹿榜,創(chuàng)建一個協(xié)程,可以并發(fā)操作锦爵,調(diào)用await獲取返回的值

 scope.launch(Dispatchers.Unconfined) {  
      val one = async { getResult(20) }  
      val two = async { getResult(40) }  
      mNumTv.text = (one.await() + two.await()).toString()  
    } 

2.3.1 suspent 修飾掛起函數(shù)的關鍵字舱殿。提醒調(diào)用者這個方法耗時

2.3.2 使用 withContext 切換到指定的 IO 線程去進行網(wǎng)絡或者數(shù)據(jù)庫請求;

private suspend fun getResult(num: Int): Int {  
    return withContext(Dispatchers.IO) {  
        num * num  
    }
}

2.3.3 使用 delay 方法去等待某個事件险掀;

private suspend fun getResult(num: Int): Int {  
    delay(5000)  
    return num * num  
}
三沪袭、Flow

創(chuàng)建一個flow

 fun testFlow(): Flow<Int> {
        return flow {
            for (i in 1..10) {
                delay(100)
                Log.i(TAG, "testFlow emit: $i")
                if (i == 2) throw java.lang.IllegalArgumentException("this is a fake error.")
                emit(i)
            }
        }
    }
createBtn("testFlow") {
            viewLifecycleOwner.lifecycleScope.launch(Dispatchers.Default) {
                kotlinStarter.testFlow()
                    .flowOn(Dispatchers.IO)
                    .onCompletion {
                        logi(TAG, "onCompletion the flow value $it")
                    }
                    .catch { e ->
                        logi(TAG, "catch some error $e")
                    }
                    .collect {
                        logi(TAG, "collect the flow value $it")
                    }
            }
        }

輸出log:

1691653124441.png

結(jié)論:

flowOn(Dispatchers.IO) 可以設置flow發(fā)射端的線程,在熱流中是不生效的樟氢。

collect 訂閱端運行的線程是lifecycleScope指定的冈绊。

3.1 操作符
普通操作符
  • map:轉(zhuǎn)換操作符侠鳄,將 A 變成 B;
  • take:后面跟 Int 類型的參數(shù)死宣,表示接收多少個 emit 出的值伟恶;
  • filter:過濾操作符;
kotlinStarter.testFlow()
                    .flowOn(Dispatchers.IO)
                    .onCompletion {
                        logi(TAG, "onCompletion the flow value $it")
                    }
                    .catch { e ->
                        logi(TAG, "catch some error $e")
                    }
                    .take(2) //取前2個
                    .map {
                        it + 5 //將數(shù)據(jù)改成另外一個值毅该,會影響collect
                    }
                    .filter {
                        it % 2 == 0 //符合這個條件才會被collect
                    }
                    .collect {
                        logi(TAG, "collect the flow value $it")
                    }
1691654053165.png

特殊的操作符

總會有一些特殊的情況博秫,比如我只需要取前幾個,我只要最新的數(shù)據(jù)等眶掌,不過在這些情況下挡育,數(shù)據(jù)的發(fā)射就是并發(fā)執(zhí)行的。

特殊 Flow 操作符的作用:

  • buffer:數(shù)據(jù)發(fā)射并發(fā)畏线,collect 不并發(fā)静盅;
  • conflate:發(fā)射數(shù)據(jù)太快,只處理最新發(fā)射的寝殴;
  • collectLatest:接收處理太慢蒿叠,只處理最新接收的;
組合操作符

組合 Flow 操作符的作用:

  • zip:組合兩個流蚣常,雙方都有新數(shù)據(jù)才會發(fā)射處理市咽;

      fun zip(): Flow<String> {
            val flow = flowOf(1, 2, 3, 4, 5).onEach { delay(10) }
            val flow2 = flowOf("a", "b", "c", "d").onEach { delay(15) }
            return flow.zip(flow2) { num, name ->
                "$num:$name"
            }
        }
    
1691659813900.png
  • combine:組合兩個流,在經(jīng)過第一次發(fā)射以后抵蚊,任意方有新數(shù)據(jù)來的時候就可以發(fā)射施绎,另一方有可能是已經(jīng)發(fā)射過的數(shù)據(jù);

    fun combine(): Flow<String> {
        val flow = flowOf(1, 2, 3, 4, 5).onEach { delay(10) }
        val flow2 = flowOf("a", "b", "c", "d").onEach { delay(15) }
        return flow.combine(flow2) { num, name ->
            "$num:$name"
        }
    }
    
1691660035392.png
展平流操作符
  • flatMapConcat:串行處理數(shù)據(jù)贞绳;
  • flatMapMerge:并發(fā) collect 數(shù)據(jù)撑蒜;
  • flatMapLatest:在每次 emit 新的數(shù)據(jù)以后,會取消先前的 collect钻弄;
末端操作符

顧名思義册招,就是幫你做 collect 處理,collect 是最基礎的末端操作符萎攒。

末端流操作符的作用:

  • collect:最基礎的消費數(shù)據(jù)遇八;
  • toList:轉(zhuǎn)化為 List 集合;
  • toSet:轉(zhuǎn)化為 Set 集合耍休;
  • first:僅僅取第一個值刃永;
  • single:確保流發(fā)射單個值;
  • reduce:規(guī)約羊精,如果發(fā)射的是 Int斯够,最終會得到一個 Int,可做累加操作;
  • fold:規(guī)約雳刺,可以說是 reduce 的升級版劫灶,可以自定義返回類型;
四掖桦、channel

Channel是一個面向多協(xié)程之間數(shù)據(jù)傳輸?shù)?BlockQueue本昏。

實現(xiàn)協(xié)程之間的數(shù)據(jù)傳輸需要三步。

4.1 創(chuàng)建channel
  • 直接創(chuàng)建
  • 使用produce
2. 發(fā)送數(shù)據(jù)

發(fā)送數(shù)據(jù)使用的 Channel#send() 方法枪汪,當我們數(shù)據(jù)發(fā)送完畢的時候涌穆,可以使用 Channel#close() 來表明通道已經(jīng)結(jié)束數(shù)據(jù)的發(fā)送。

3. 接收數(shù)據(jù)

正常情況下雀久,我們僅需要調(diào)用 Channel#receive() 獲取數(shù)據(jù)宿稀,但是該方法只能獲取一次傳遞的數(shù)據(jù),如果我們僅需獲取指定次數(shù)的數(shù)據(jù)赖捌,可以這么操作:

lifecycleScope.launch {
                // 1. 生成一個 Channel
                val channel = Channel<Int>()
                val channel2 = produce {
                    for(i in 1..5){
                        delay(200)
                        send(i * i)
                    }
                    close()
                }

                // 2. Channel 發(fā)送數(shù)據(jù)
                launch {
                    for(i in 1..5){
                        delay(200)
                        channel.send(i * i)
                    }
                    channel.close()
                }

                // 3. Channel 接收數(shù)據(jù)
                launch {
                    for( y in channel)//接收所有數(shù)據(jù)
                        Log.e(TAG, "get the data: $y from channel")

                }
                repeat(2){//接收2個數(shù)據(jù)
                    Log.e(TAG, "get the data: ${channel2.receive()} from channel2")
                }
            }
四祝沸、多協(xié)程數(shù)據(jù)處理

多協(xié)程處理并發(fā)數(shù)據(jù)的時候,原子性同樣也得不到保證越庇,協(xié)程中出了一種叫 Mutex 的鎖罩锐,區(qū)別是它的 lock 操作是掛起的,非阻塞的

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末卤唉,一起剝皮案震驚了整個濱河市涩惑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌桑驱,老刑警劉巖竭恬,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異熬的,居然都是意外死亡痊硕,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進店門押框,熙熙樓的掌柜王于貴愁眉苦臉地迎上來岔绸,“玉大人,你說我怎么就攤上這事强戴。” “怎么了挡鞍?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵骑歹,是天一觀的道長。 經(jīng)常有香客問我墨微,道長道媚,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮最域,結(jié)果婚禮上谴分,老公的妹妹穿的比我還像新娘。我一直安慰自己镀脂,他們只是感情好牺蹄,可當我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著薄翅,像睡著了一般沙兰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上翘魄,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天鼎天,我揣著相機與錄音,去河邊找鬼暑竟。 笑死斋射,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的但荤。 我是一名探鬼主播罗岖,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼纱兑!你這毒婦竟也來了呀闻?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤潜慎,失蹤者是張志新(化名)和其女友劉穎捡多,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體铐炫,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡垒手,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了倒信。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片科贬。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖鳖悠,靈堂內(nèi)的尸體忽然破棺而出榜掌,到底是詐尸還是另有隱情,我是刑警寧澤乘综,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布憎账,位于F島的核電站,受9級特大地震影響卡辰,放射性物質(zhì)發(fā)生泄漏胞皱。R本人自食惡果不足惜邪意,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望反砌。 院中可真熱鬧雾鬼,春花似錦、人聲如沸宴树。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽森渐。三九已至做入,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間同衣,已是汗流浹背竟块。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留耐齐,地道東北人浪秘。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像埠况,于是被迫代替她去往敵國和親耸携。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,629評論 2 354

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