kotlin和flutter笔记

94 阅读6分钟

kotlin

kotlin拓展函数,顶层函数本质为新建类和静态方法

伴生对象会新建一个静态内部类,伴生对象中定义的属性会直接编译为外部类的静态字段,而函数会被编译为静态内部类的方法

高阶函数

高阶函数会把函数转换为匿名类的实现方式(实现各种Function接口)

inline修饰高阶函数,noinline和crossinline修饰函数参数

非内联的函数类型参数可以自由地传递给其他任何函数,因为它就是一个真实的参数,而内联的函数类型参数只允许传递给另外一个内联函数,这也是它最大的局限性。
使用noinline修饰参数可以使参数不内联
另外,内联函数和非内联函数还有一个重要的区别,那就是内联函数所引用的Lambda表达式中是可以使用return关键字来进行函数返回的,而非内联函数只能进行局部返回。
内联函数在匿名类中调用的函数类型参数,不能全局return,会提示出错,需使用crossinline关键字约定在内联函数中不使用return
内联函数会优化高阶函数的性能,减少方法出入栈,参数中的lambda函数也会变成内联,不是函数对象了,也就不能作为一个参数再次传递了

by lazy默认同步,可配置
实现原理:lazy(init:()->T) lazy函数里传lambda表达式,在属性的get()中判断是否为首次初始化并执行初始化 类委托
class Derived(b: Base) : Base by b
类的委托即一个类中定义的方法实际是调用另一个类的对象的方法来实现的

协程
1.实现CoroutineScope 接口,使用委托初始化作用域或自己实现(初始化上下文)
2.构造CoroutineScope对象,初始化上下文

当协程被取消时,遇到挂起函数会抛异常并退出协程,如果协程正在执行计算任务(没有挂起的持续计算),则不会被取消,这个时候需要自己检查取消状态,使用isActive检查,逻辑退出或者抛异常。Android页面退出需要注意此项
子协程和父协程使用同一个作用域,父协程会等子协程完成
协程是在协程作用域内执行的轻量级并发单元
非子协程launch遇到异常会抛出,async遇到异常不会抛出,async.await()会抛出
CoroutineExceptionHandler对于根协程launch生效
supervisorScope函数创建的协程作用域的特点是抛出的异常不会连锁取消同级协程和父协程

gson解析时非空基本类型会有默认值,非空引用类型依然可能为空

flutter

FlutterEngine 

1.当使用一个新的引擎并指定路由时(initialRoute),flutter会先显示默认路由然后跳转指定路由

父控件状态更新时,子控件的build方法也会执行

异步任务和事件循环机制
Dart应用有一个消息循环和两个消息队列
运行过程:

  • Dart程序启动时会创建两个队列,分别是MicroTak队列(微服务队列)和Event队列(事件队列);
  • 然后执行main()方法;
  • 最后启动事件循环;

执行顺序:

  1. 执行main();
  2. 执行MicroTask队列中事件(事件+then,事件+then);
  3. 执行事件队列中的事件(事件+then,事件+then);

Future会将异步任务放入事件队列
Future.value()和Future.microtask会将事件加入到Microtask Queue中
Future前加await可以返回泛型类型

Async 和 await 实现的异步只适合耗时操作为等待类型的,例如接口请求和延时操作
await之后的代码将在微任务队列中执行

Dart是单线程模型,计算密集型会阻塞线程,Dart中 await的异步是单线程的异步

由于Isolate是一种单线程模型,代码运行时碰到异步代码会将其丢到Queue中,只按顺序运行同步代码。等同步代码都执行完成后才会按顺序执行异步任务。
dart中的spawn和flutter中的compute,compute有返回值,spawn前加await不会同步执行
spawn使用SendPort监听结束后需要close,不然程序会一直运行

lsolate有自己私有的内存空间和一个基于事件循环的线程

Isolate之间的通信机制:
接收端口(ReceivePort)的发送端口(SendPort)
支持的消息数据类型:
原始数据类型,如 null,num,bool,double,String等
SendPort实例,包含前面两种类型的list和map
处于同一进程的两个Isolate,也可以发送实例对象

闭包是指一个函数对象,它可以访问和操作其定义范围之外的变量,即使在其定义之后使用,这种能力被称为"捕获"外部变量

  • 访问外部变量:闭包可以捕获和访问其定义范围之外的变量,即使在其定义之后使用,仍然可以引用和操作外部变量的值。
  • 保留状态:闭包可以在函数执行完毕后保留其内部变量的状态,从而实现状态的持久化。

函数表示方法:3种

typedef TestFun = Future Function(String s);

Future test(String s)

Function test//无校验

String Function(String s)

harmony

在ForEach循环渲染过程中,系统会为每个数组元素生成一个唯一且持久的键值,用于标识对应的组件。当这个键值变化时,ArkUI框架将视为该数组元素已被替换或修改,并会基于新的键值创建一个新的组件。
ForEach的第三个参数KeyGenerator如果使用默认的键值生成规则index + '__' + JSON.stringify(item)或者包含index相关的规则会导致list增减item时item后的组件全部重新渲染,因为规则是index相关的,增减item会导致index变化

@Builder函数的参数必须按照对象字面量的形式,把所需要的属性一一传入,或者@Builder函数内直接使用@State变量,才会触发动态渲染UI,按值传递参数不会,传入的参数是两个或两个以上也不会

数组初始化Array(1000)或[]

可以用对象字面量{}创建对象

生命周期aboutToAppear-build-onDidBuild-onPageShow-onPageHide-aboutToDisappear

@State对嵌套属性的赋值观察不到,数组项中属性的赋值观察不到

@Prop父子单向同步,@Link父子双向同步。通过父组件将数据源传递给子组件建立关系(父子组件之间通过命名参数机制传递)
@Provide和@Consume应用于与后代组件的双向数据同步,通过相同的变量名或者相同的变量别名绑定

let para: Record<string, number> = { 'PropA': 47 };
let storage: LocalStorage = new LocalStorage(para);
@LocalStorageLink('PropA') storageLink: number = 1; 初始值为47