diff --git "a/Jetpack/architecture/12.Navigation\347\256\200\344\273\213.md" "b/Jetpack/architecture/12.Navigation\347\256\200\344\273\213.md" index dac4d75d..38c42a53 100644 --- "a/Jetpack/architecture/12.Navigation\347\256\200\344\273\213.md" +++ "b/Jetpack/architecture/12.Navigation\347\256\200\344\273\213.md" @@ -4,6 +4,11 @@ 为此,Jetpack提供了一个名为Navigation的组件,旨在方便我们管理页面和App bar。 + +Navigation 是一个框架,用于在 Android 应用中的“目的地”之间导航,该框架提供一致的 API,无论目的地是作为 fragment、activity 还是其他组件实现。 + + + 它具有以下优势: - 可视化的页面导航图,类似于Apple Xcode中的StoryBoard,便于我们理清页面间的关系。 @@ -15,6 +20,16 @@ Navigation 组件旨在用于具有一个主 Activity 和多个 Fragment 目的地的应用。主 Activity 与导航图相关联,且包含一个负责根据需要交换目的地的 `NavHostFragment`。在具有多个 Activity 目的地的应用中,每个 Activity 均拥有其自己的导航图。 + +在使用过程中,我们感受到如下的优点。 + +- 页面跳转性能更好,在单 Activity 的架构下,都是 fragment 的切换,每次 fragment 被压栈之后,View 被销毁,相比之前 Activity 跳转,更加轻量,需要的内存更少。 +- 通过 Viewmodel 进行数据共享更便捷,不需要页面之间来回传数据。 +- 统一的 Navigation API 来更精细的控制跳转逻辑。 + + + + ## 依赖 如果想要使用Navigation,需要现在build.gradle文件中添加以下依赖: @@ -51,17 +66,20 @@ dependencies { 导航组件由以下三个关键部分组成: -1. Navigation Graph +1. Navigation Graph : 图表 + 一种数据结果,用于定义应用中的所有导航目的地以及它们如何连接在一起。 在一个集中位置包含所有导航相关信息的 XML 资源。这包括应用内所有单个内容区域(称为*目标*)以及用户可以通过应用获取的可能路径。 -2. NavHost + +2. NavHost : 主机 显示导航图中目标的空白容器。导航组件包含一个默认NavHost实现 (NavHostFragment),可显示Fragment目标。 -3. NavController +3. NavController : 控制器 - 在NavHost中管理应用导航的对象。当用户在整个应用中移动时,NavController会安排NavHost中目标内容的交换。 + 在NavHost中管理应用导航的对象。当用户在整个应用中移动时,NavController会安排NavHost中目标内容的交换。 + 该控制器提供了一些方法,可用于在目的地之间导航、处理深层链接、管理返回堆栈等。 在应用中导航时,您告诉NavController,您想沿导航图中的特定路径导航至特定目标,或直接导航至特定目标。NavController便会在NavHost中显示相应目标。 @@ -476,7 +494,9 @@ override fun onCreate(savedInstanceState: Bundle?) { ``` +### 参考 +https://mp.weixin.qq.com/s?src=11×tamp=1712714064&ver=5191&signature=JTMgHGLtMGW*NoSWSrLNVuGzs-KEEDznO-ja7*X*KumZMFAuIRl7WbPYT1gG7AX810nUx6Ftb6nm6Ao92M*GzojPfqBUo1wOFc0gMs1mseTLkUWZ9Q*BIW69MM7ULPDV&new=1 - [上一篇:11.Hilt简介](https://github.com/CharonChui/AndroidNote/blob/master/Jetpack/architecture/11.Hilt%E7%AE%80%E4%BB%8B.md) diff --git "a/Jetpack/architecture/4.ViewModel\347\256\200\344\273\213.md" "b/Jetpack/architecture/4.ViewModel\347\256\200\344\273\213.md" index 7c8a3198..5ec5a6bf 100644 --- "a/Jetpack/architecture/4.ViewModel\347\256\200\344\273\213.md" +++ "b/Jetpack/architecture/4.ViewModel\347\256\200\344\273\213.md" @@ -11,6 +11,11 @@ `ViewModel`是专门用于存放应用程序页面所需的数据。ViewModel将页面所需的数据从页面中剥离出来,页面只需要处理用户交互和展示数据。 + +ViewModel类是一种业务逻辑或屏幕级状态容器。 +它用于将状态公开给界面,以及封装相关的业务逻辑。 +它的主要优点是,它可以缓存状态,并可在配置更改后持久保留相应状态。这意味着在activity之间导航时或进行配置更改后(例如旋转屏幕时),界面将无需重新提取数据。 + Viewmodel解决两个问题: 1. 数据的变更与view隔离,也就是解耦 diff --git "a/KotlinCourse/.8.Kotlin_\345\215\217\347\250\213.md.swp" "b/KotlinCourse/.8.Kotlin_\345\215\217\347\250\213.md.swp" deleted file mode 100644 index 996ab163..00000000 Binary files "a/KotlinCourse/.8.Kotlin_\345\215\217\347\250\213.md.swp" and /dev/null differ diff --git "a/KotlinCourse/1.Kotlin_\347\256\200\344\273\213&\345\217\230\351\207\217&\347\261\273&\346\216\245\345\217\243.md" "b/KotlinCourse/1.Kotlin_\347\256\200\344\273\213&\345\217\230\351\207\217&\347\261\273&\346\216\245\345\217\243.md" index 5e1b19fa..72e4986b 100644 --- "a/KotlinCourse/1.Kotlin_\347\256\200\344\273\213&\345\217\230\351\207\217&\347\261\273&\346\216\245\345\217\243.md" +++ "b/KotlinCourse/1.Kotlin_\347\256\200\344\273\213&\345\217\230\351\207\217&\347\261\273&\346\216\245\345\217\243.md" @@ -324,10 +324,42 @@ var myProperty: String } ``` +自定义set和get的重点在field,field指代当前参数,类似于java的this关键字。 + 这意味着无论何时当你使用点操作符来获取或设置属性值时,实际上你总是调用了属性的getter或是setter。那么,为什么编译器要这么做呢? 为属性添加getter和setter意味着有访问该属性的标准方法。getter处理获取值的所有请求,而setter处理所有属性值设置的请求。 因此,如果你想要改变处理这些请求的方式,你可以在不破坏任何人代码的前提下进行。通过将其包装在getter和setter中来输出对属性的直接访问称为数据隐藏。 + +在某些情况下,无参的函数与只读属性可互换通用。虽然语义相似,但在以下情况中,更多的是选择使用属性而不是方法。 + +- 不会抛出任何异常。 +- 具有O(1)的复杂度。 +- 容易计算(或者运行一次之后缓存结果)。 +- 每次调用返回同样的结果。 + + + +## 后端属性(Blocking Property) + +它实际上是一个隐含的对属性值的初始化声明。能有效避免空指针问题的产生。 + +```kotlin +var size: Int = 2; +private var _table: Map? = null + +val table: Map + get() { + if (_table == null) { + _table = HashMap() + } + return _table ?: throw AssertionError("Set to null by another thread") + } +``` + +在Java中,访问private成员变量需要通过getter和setter来实现,此处通过table来获取_table变量,优化了Java中函数调用带来的开销。 + + ## 延迟初始化 在类内声明的属性必须初始化,如果设置非`null`的属性,应该将此属性在构造器内进行初始化。 @@ -475,6 +507,47 @@ fun getName() : String { ``` +## @操作符 + +在Kotlin中,@操作符主要有两个作用: + +- 限定this的对象类型 +```kotlin +class User { + inner class State { + fun getUser(): User { + return this@User // 返回User + } + + fun getState(): State { + return this@State // 返回State + } + + } +} +``` +- 作为标签使用 +当把@操作符作为标签使用时,可以跳出双层for循环和forEach函数。 +例如: +```kotlin +val listA = listOf(1, 2, 3, 4, 5, 6) +val listB = listOf(2, 3, 4, 5, 6, 7 ) + +loop@ for(itemA in listA) { + var i: Int = 0 + for (itemB in listB) { + i++ + if (item > 2) { + break @loop // 当itemB > 2时,跳出循环 + } + println("itemB: $itemB") + } +} +``` + + + + ## 类的定义:使用`class`关键字 @@ -630,6 +703,25 @@ class Person private constructor(name: String) { } ``` + + +```kotlin +open class Base(p: Int) +class Example(p: Int) : Base(p) +``` + +如果该类有一个主构造函数,那么其基类型可以用主构造函数的参数进行初始化。 +如果该类没有主构造函数,那么每个次构造函数必须使用super关键字初始化其类型,或者委托给另一个构造函数初始化。如: +```kotlin +class Example : View { + constructor(ctx: Context) : super(ctx) + + constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs) +} +``` + + + #### 构造方法默认参数 ```kotlin @@ -841,6 +933,48 @@ var age = f1.component2() 对于有些不需要toString()、equals()、hashCode()方法的类如果使用数据类就会导致多生成这些代码,所以在使用数据类的时候不要去为了简单而乱用, 也要去想想是否需要这些方法?是否需要设计成数据类。 + +#### 获取类中成员函数的对象 + +```kotlin +fun main(args: Array) { + var user = User::special + // 调用invoke函数执行 + user.invoke(User("Jack", 30)) + + // 利用反射机制获取指定方法的内容 + var method = User::class.java.getMethod("special") + method.invoke(User("Tom", 20)) +} + +class User(val name: String, val age: Int) { + fun special() { + println("name:${name") age:${age}"); + } +} +``` + +在上面的实例中,User类还有一个special函数,使用User::special可以获取成员函数的对象,然后使用invoke函数调用special函数,以获取该函数的内容。 + + +### 构造函数引用 + +构造函数的引用和属性、方法类似,构造函数可以作用于任何函数类型的对象,该函数的对象与构造函数的参数相同,可以使用::操作符加类名的方式来引用构造函数。 + +```kotlin +class Foo + +fun function(factory: () -> Foo) { + val x: Foo = factory() +} + +// 使用::Foo方式调用类Foo的无参数构造函数 +fun main() { + function(::Foo) +} +``` + + ## 继承 在`Kotlin`中所有类都有一个共同的超类`Any`,这对于没有超类型声明的类是默认超类: @@ -1214,6 +1348,11 @@ val mList2 = vars(0, *myArray, 6, 7) ``` +在Kotlin中,方法调用也被定义为二元操作运算符,而这些方法往往可以转化为invoke函数。 +例如 a(i)方法的对应转换方法为 a.invoke(i) + + + ## 命名风格 如果拿不准的时候,默认使用`Java`的编码规范,比如: diff --git "a/KotlinCourse/5.Kotlin_\345\206\205\351\203\250\347\261\273&\345\257\206\345\260\201\347\261\273&\346\236\232\344\270\276&\345\247\224\346\211\230.md" "b/KotlinCourse/5.Kotlin_\345\206\205\351\203\250\347\261\273&\345\257\206\345\260\201\347\261\273&\346\236\232\344\270\276&\345\247\224\346\211\230.md" index 9c91998e..1d1ab9da 100644 --- "a/KotlinCourse/5.Kotlin_\345\206\205\351\203\250\347\261\273&\345\257\206\345\260\201\347\261\273&\346\236\232\344\270\276&\345\247\224\346\211\230.md" +++ "b/KotlinCourse/5.Kotlin_\345\206\205\351\203\250\347\261\273&\345\257\206\345\260\201\347\261\273&\346\236\232\344\270\276&\345\247\224\346\211\230.md" @@ -132,6 +132,8 @@ class Outter{ val outter = Outter() outter.Inner().execute() +var v = this@Outer.testVal // 引用外部类的成员 + // 输出 Inner -> execute : can read testVal=test ``` @@ -142,6 +144,11 @@ val outter = Outter() outter.Inner().execute() ``` +引用外部类中对象的方式为: var 变量名 = this@外部类名 + +在类成员中,this指代该类的当前对象。而在扩展函数或者带接受者的函数中,this表示在点左侧传递的接收者参数。 + + #### 内部类vs嵌套类 在Java中,我们通过在内部类的语法上增加一个static关键词,把它变成一个嵌套类。然而,Kotlin则是相反的思路,默认是一个嵌套类, @@ -154,6 +161,8 @@ outter.Inner().execute() #### 匿名内部类 +没有名字的内部类被称为匿名内部类,使用匿名内部类必须继承一个父类或实现一个接口,匿名内部类可以简化代码编写。 + ```kotlin // 通过对象表达式来创建匿名内部类的对象,可以避免重写抽象类的子类和接口的实现类,这和Java中匿名内部类的是接口和抽象类的延伸一致。 text.setOnClickListener(object : View.OnClickListener{ @@ -179,6 +188,16 @@ mViewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { }) ``` +如果对象实例是一个函数接口,还可以使用Lambda表达式来实现。 +```kotlin + +val listener = ActionListener { + println("clicked") +} +``` + + + ### 枚举 与Java中的enum语法大体类似,无非多了一个class关键字,表示它是一个枚举类。 @@ -612,12 +631,16 @@ public static Instance getInstance() { 具体的业务逻辑执行者 + + ##### 类委托 在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。 `kotlin`中的委托可以算是对委托模式的官方支持。 `Kotlin`直接支持委托模式,更加优雅,简洁。`Kotlin`通过关键字`by`实现委托。 +委托模式已被证明是类继承模式的一种很好的替代方案。 + 我们经常会打游戏进行升级,但是有时候因为我们的水平不行,没法升到很高的级别,这就出现了很多的游戏代练,他们帮我们代打,这就是一个委托模式: 1. 定义约束类,定义具体需要委托的业务,这里的业务: @@ -660,6 +683,10 @@ public static Instance getInstance() { ##### 属性委托 +在软件的开发中,属性的getter和setter方法的代码有很多相同或相似的地方,虽然为每个类编写相同的代码是可行的,但这样会造成大量的代码冗余,而委托属性正是为解决这一问题而提出的。 + +属性委托是指一个类的某个属性值不是在类中直接定义,而是将其委托给一个代理类,从而实现对该类属性的统一管理。 + 在Kotlin中,有一些常见的属性类型,虽然我们可以在每次需要的时候手动实现他们,但是很麻烦,为了解决这些问题,Kotlin标准提供了委托属性。 语法是`val/var <属性名>: <类型> by <表达式>`。在`by`后面的表达式是该委托,因为属性对应的`get()`和`set()`会被委托给它的`getValue()` @@ -667,12 +694,15 @@ public static Instance getInstance() { ```kotlin class Example { + // 语法中,关键词by之后的表达式就是委托的类,而且属性的get()以及set()方法将被委托给这个对象的getValue()和setValue()方法。 + // 属性委托不必实现相关接口,但必须提供getValue()方法,如果是var类型的属性,还需要提供setValue()方法。 var property : String by DelegateProperty() } class DelegateProperty { var temp = "old" // getValue和setValue方法的参数是固定的 + // getValue和setValue方法必须用关键字operator标记 operator fun getValue(ref: Any?, p: KProperty<*>): String { return "DelegateProperty --> ${p.name} --> $temp" } @@ -798,8 +828,10 @@ fun main() { +### 标准委托 -`Kotlin`通过属性委托的方式,为我们实现了一些常用的功能,包括: + +`Kotlin`通过属性委托的方式在Kotlin标准库中内置了很多工厂方法来实现属性委托,包括: - 延迟属性`lazy properties` - 可观察属性`observable properties` @@ -810,6 +842,9 @@ fun main() { 延迟属性我们应该不陌生,也就是通常说的懒汉,在定义的时候不进行初始化,把初始化的工作延迟到第一次调用的时候。`kotlin`中实现延迟属性很简单, 来看一下。 +lazy()是一个接收Lambda表达式作为参数并返回一个Lazy实例的函数,返回的实例可以作为实现延迟属性的委托。 +也就是说,延迟属性第一次调用get()时,将会执行传递给lazy()函数的Lambda表达式并记录执行结果,后续所有对get()的调用只会返回记录的结果。 + ```kotlin val lazyValue: String by lazy { Log.d(JTAG, "Just run when first being used") @@ -892,7 +927,24 @@ fun later(block: () -> T) = Later(block) ##### 可观察属性 -如果你要观察一个属性的变化过程,那么可以将属性委托给`Delegates.observable`, `observable`函数原型如下: + +所谓可观察属性(observable),就是当属性发生变化时能自动拦截其变化,实现观察属性值变化的委托函数是Delegates.observable()。 + +该函数接受两个参数: + +- 初始值 +- 属性值变化时间的响应器(handler) + +每当给属性值赋值时都会调用该响应器,该响应器主要有3个参数: + +- 被赋值的属性 +- 赋值前的旧属性值 +- 赋值后的新属性值 + + +如果你要观察一个属性的变化过程,那么可以将属性委托给`Delegates.observable`。 + + `observable`函数原型如下: ```kotlin public inline fun observable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Unit): @@ -947,9 +999,10 @@ fun test(){ -##### map映射 +##### map委托 + +Map委托常常用来将Map中的key-value映射到对象的属性中,这经常出现在解析JSON或者其他动态事情的应用中。 -一个常见的用例是在一个映射`map`里存储属性的值。这经常出现在像解析`JSON`或者做其他“动态”事情的应用中。在这种情况下,你可以使用映射实例自身作为委托来实现委托属性。 ```kotlin class User(val map: Map) { @@ -975,6 +1028,8 @@ class MutableUser(val map: MutableMap) { +委托模式是一种被广泛使用的设计模式,Kotlin的委托主要用来实现代码的重用,Kotlin默认支持委托模式,且不需任何模版方法。 + - [上一篇:4.Kotlin_表达式&关键字](https://github.com/CharonChui/AndroidNote/blob/master/KotlinCourse/4.Kotlin_%E8%A1%A8%E8%BE%BE%E5%BC%8F%26%E5%85%B3%E9%94%AE%E5%AD%97.md) - [下一篇:6.Kotlin_多继承问题](https://github.com/CharonChui/AndroidNote/blob/master/KotlinCourse/6.Kotlin_%E5%A4%9A%E7%BB%A7%E6%89%BF%E9%97%AE%E9%A2%98.md) diff --git "a/KotlinCourse/7.Kotlin_\346\263\250\350\247\243&\345\217\215\345\260\204&\346\211\251\345\261\225.md" "b/KotlinCourse/7.Kotlin_\346\263\250\350\247\243&\345\217\215\345\260\204&\346\211\251\345\261\225.md" index 50ff4d0a..a800a8d3 100644 --- "a/KotlinCourse/7.Kotlin_\346\263\250\350\247\243&\345\217\215\345\260\204&\346\211\251\345\261\225.md" +++ "b/KotlinCourse/7.Kotlin_\346\263\250\350\247\243&\345\217\215\345\260\204&\346\211\251\345\261\225.md" @@ -327,6 +327,8 @@ text.panddingH = 100 扩展属性与扩展函数一样,其本质也是对应Java中的静态方法。由于扩展没有实际的将成员插入类中,因此对扩展属性来说幕后字段是无效的。 +扩展属性允许定义在类或者Kotlin文件中,但不允许定义在函数中。 因为扩展属性不能有初始化器,所以只能由显式提供的getter/setter定义,而且扩展属性只能被声明为val。 + ##### 静态扩展 @@ -1205,6 +1207,12 @@ class Group(val name: String) { `KDoc`不支持`@deprecated`这个标签。作为替代,请使用`@Deprecated`注解。 +### 泛型 + +通配符类型参数 `? extends E`表示该方法接收E类型或者E类型的一些子类型,而不仅仅是E类型本身。 +Kotlin抛弃了通配符类型这一概念,转而引入了生产者和消费者的概念,其中,生产者表示那些只能读取数据的对象,使用表达式`out T`标记。 +消费者表示那些只能写入数据的对象,使用表达式`in T`标记。 +为了便于理解,可以简单的将`out T`理解为`? exnteds T`,将`in T`理解为`? super T`。 diff --git "a/OperatingSystem/AndroidKernal/2.Android\347\272\277\347\250\213\351\227\264\351\200\232\344\277\241\344\271\213Handler\346\266\210\346\201\257\346\234\272\345\210\266.md" "b/OperatingSystem/AndroidKernal/2.Android\347\272\277\347\250\213\351\227\264\351\200\232\344\277\241\344\271\213Handler\346\266\210\346\201\257\346\234\272\345\210\266.md" index 424a31dd..b571b6f3 100644 --- "a/OperatingSystem/AndroidKernal/2.Android\347\272\277\347\250\213\351\227\264\351\200\232\344\277\241\344\271\213Handler\346\266\210\346\201\257\346\234\272\345\210\266.md" +++ "b/OperatingSystem/AndroidKernal/2.Android\347\272\277\347\250\213\351\227\264\351\200\232\344\277\241\344\271\213Handler\346\266\210\346\201\257\346\234\272\345\210\266.md" @@ -544,6 +544,11 @@ public final void removeCallbacksAndMessages(@Nullable Object token) { +##### postAtFrontOfQueue():将消息插入到队列头部 + +通过调用sendMessageAtFrontOfQueue计入一个when为0的message到队列,即插入到队列的头部。可以尽量保证快速执行。 + + ## MessageQueue类的常用方法 MessageQueue非常重要,因为quit、next等都最终调用的是MessageQueue类: diff --git a/README.md b/README.md index 234020ac..8604e003 100644 --- a/README.md +++ b/README.md @@ -722,7 +722,7 @@ Android学习笔记 [341]: https://github.com/CharonChui/AndroidNote/blob/master/VideoDevelopment/%E8%A7%86%E9%A2%91%E5%B0%81%E8%A3%85%E6%A0%BC%E5%BC%8F/AVI.md "AVI" [342]: https://github.com/CharonChui/AndroidNote/tree/master/VideoDevelopment/OpenCV "OpenCV" [343]: https://github.com/CharonChui/AndroidNote/blob/master/VideoDevelopment/OpenCV/1.OpenCV%E7%AE%80%E4%BB%8B.md "1.OpenCV简介" -[344]: "MediaMetadataRetriever" +[344]: https://github.com/CharonChui/AndroidNote/blob/master/VideoDevelopment/Android%E9%9F%B3%E8%A7%86%E9%A2%91%E5%BC%80%E5%8F%91/MediaMetadataRetriever.md "MediaMetadataRetriever"