一些学习kotlin的知识小点。。

2023-08-09  本文已影响0人  小马要加油

1. kotlin是init先执行还是成员变量先执行?

谁写前面谁先执行。

2. kotlin+databing 无法生成BR文件。

手动执行该task试试。

3. SparseArray put 重复的id会怎么样

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

sparseArray 中键为 1 的值将为 "two"

4.生命感知组件协程范围

ViewModelScope

在这个范围内启动的协程会在viewmodel清除时自动取消,可以避免消耗资源

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

LifecycleScope

每个 Lifecycle对象都定义了 LifecycleScope,在此范围内启动的协程会在 Lifecycle 被销毁时取消.可以通过lifecycle.coroutineScopelifecycleOwner.lifecycleScope属性访问

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

5.协程

一、概念

协程可以使用阻塞的方式写出非阻塞的代码,解决并发常用的回调问题。

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

分三部分:协程作用域(GlobalScope)、调度器(Dispatchers)、挂起函数

2.1 协程作用域

runBlocking:顶层函数,它和 coroutineScope 不一样,它会阻塞当前线程来等待,所以这个方法在业务中并不适用;

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

GlobalScope:全局协程作用域,可以在整个应用的声明周期中操作,且不能取消,所以仍用的时候要慎重,可能会引起泄漏

lifecycleScope:生命周期组件作用域,每个 Lifecycle对象都定义了 LifecycleScope,在此范围内启动的协程会在 Lifecycle 被销毁时取消.可以通过lifecycle.coroutineScopelifecycleOwner.lifecycleScope属性访问

 viewLifecycleOwner.lifecycleScope.launch {
            ...
        }

ViewModelScope:协程会在viewmodel清除时自动取消,可以避免消耗资源

自定义作用域:

class MainActivity : AppCompatActivity() {  
  // 1. 创建一个 MainScope  
  val scope = MainScope()  
 
  override fun onCreate(savedInstanceState: Bundle?) {  
    super.onCreate(savedInstanceState)  
    setContentView(R.layout.activity_main)  
 
    // 2. 启动协程  
    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 调度器

作用:限制协程在什么线程执行,主要的调度器有:

2.3 挂机函数
1691651194224.png

launch,启动一个新的协程,返回一个job,可以通过cancel取消这个协程

async,创建一个协程,可以并发操作,调用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 修饰挂起函数的关键字。提醒调用者这个方法耗时

2.3.2 使用 withContext 切换到指定的 IO 线程去进行网络或者数据库请求;

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

创建一个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

结论:

flowOn(Dispatchers.IO) 可以设置flow发射端的线程,在热流中是不生效的。

collect 订阅端运行的线程是lifecycleScope指定的。

3.1 操作符
普通操作符
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 //将数据改成另外一个值,会影响collect
                    }
                    .filter {
                        it % 2 == 0 //符合这个条件才会被collect
                    }
                    .collect {
                        logi(TAG, "collect the flow value $it")
                    }
1691654053165.png

特殊的操作符

总会有一些特殊的情况,比如我只需要取前几个,我只要最新的数据等,不过在这些情况下,数据的发射就是并发执行的。

特殊 Flow 操作符的作用:

组合操作符

组合 Flow 操作符的作用:

1691659813900.png 1691660035392.png
展平流操作符
末端操作符

顾名思义,就是帮你做 collect 处理,collect 是最基础的末端操作符。

末端流操作符的作用:

四、channel

Channel是一个面向多协程之间数据传输的 BlockQueue

实现协程之间的数据传输需要三步。

4.1 创建channel
2. 发送数据

发送数据使用的 Channel#send() 方法,当我们数据发送完毕的时候,可以使用 Channel#close() 来表明通道已经结束数据的发送。

3. 接收数据

正常情况下,我们仅需要调用 Channel#receive() 获取数据,但是该方法只能获取一次传递的数据,如果我们仅需获取指定次数的数据,可以这么操作:

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

                // 2. Channel 发送数据
                launch {
                    for(i in 1..5){
                        delay(200)
                        channel.send(i * i)
                    }
                    channel.close()
                }

                // 3. Channel 接收数据
                launch {
                    for( y in channel)//接收所有数据
                        Log.e(TAG, "get the data: $y from channel")

                }
                repeat(2){//接收2个数据
                    Log.e(TAG, "get the data: ${channel2.receive()} from channel2")
                }
            }
四、多协程数据处理

多协程处理并发数据的时候,原子性同样也得不到保证,协程中出了一种叫 Mutex 的锁,区别是它的 lock 操作是挂起的,非阻塞的

上一篇 下一篇

猜你喜欢

热点阅读