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.coroutineScope
或 lifecycleOwner.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.coroutineScope
或 lifecycleOwner.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ù)
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:
結(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")
}
特殊的操作符
總會有一些特殊的情況博秫,比如我只需要取前幾個,我只要最新的數(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" } }
-
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" } }
展平流操作符
-
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
操作是掛起的,非阻塞的