探究java语言的运行机制
变量、函数、语法糖
when条件语句
for循环
主构造函数、次构造函数
数据类和单例类
集合的创建与遍历
集合的函数式API
创建菜单
隐式使用Intent
Activity生存周期
四种启动模式
标准函数和静态方法

Android操作系统是由Google开发的,为什么JetBrains作为一个第三方公司却可以自己设计出一门编程语言来开发Android应用?

探究java语言的运行机制:

编程语言大致可以分成两类:编译型语言和解释性语言。编译型语言的特点是编译器会将我们编写的源代码一次性的编译成计算机识别的二进制代码,然后计算机直接执行,像C和C++属于编译型语言。
解释型语言则完全不一样,他有一个解释器,在程序运行时,解释器会一行一行的读取我们编写的源代码,然后实时的将这些源代码解释称计算机可识别的二进制数据后再执行,因此解释型语言通常效率会差一些,像Python和JavaScript都是属于解释型语言。

Java语言是属于解释型语言。
虽然java语言是需要先编译在运行的,但是java代码编译之后生成的并不是计算机可识别的二进制文件,而是一种特殊的class文件,这种class文件只有java虚拟机(Android中叫ARTT,一种移动优化版的虚拟机)才能识别,而这个Java虚拟机担当的其实就是解释器的角色,它会在程序运行时将编译后的class文件解释称计算机可识别的二进制数据后再执行。

在Android中直接声明fun main(){}即可编写。不用写分号哦。

变量


只允许在变量前使用两种关键字:val(对应java中的final变量)和var(对应在Kotlin中Int是一个类,它拥有自己的方法和继承结构。下表为对应关系:

函数
语法规则如下:

fun methodName(param1: Int,  params2: Int) : Int{return 0 }

首先fun是定义函数的关键字,无论定义什么函数都要使用fun来声明。

紧跟着fun后面的是函数名,这个没有什么要求。

函数名后紧跟着一堆括号,里面可以声明该函数接受什么参数,参数的数量可以是任意个,例如上述为接受两个Int型的参数。

参数括号后面是可选的,意为返回值类型,如果函数不返回值则可以不写。

Kotlin函数的语法糖

当一个函数中只有一行代码时,Kotlin允许我们不必编写函数体,可以直接将唯一的一行代码写在函数定义的尾部,中间用等号连接即可。

例:

fun largerNumber(num1: Int, num2: Int): Int{return max(num1, num2)
}

可以写为

fun largerNumber(num1: Int, num2: Int): Int = max(num1, num2)

使用这种语法,return关键字也可以省略了,等号足以表达返回值的意思。

另外,Kotlin有着出色的类型推导机制。由于max函数返回的是一个Int值,而我们在largerNumber函数的尾部又使用了等号连接max函数,因此可以推断出函数返回的必定是Int值,所以不用再显式声明的返回值类型了,代码可以进一步简化

fun largerNumber(num1: Int, num2: Int) = max(num1, num2)

Kotlin中的if语句比java中多一个额外的功能,它可以有返回值的,返回值就是if语句每一个条件中最后一行代码的返回值。、
eg:

fun largerNumber(num1: Int, num2: Int) : Int {var value = 0if (num1 > num2){value = num1} else {value= num2}return value
}

可简化为

fun largerNumber(num1: Int, num2: Int) : Int {var value = if (num1 > num2){num1} else {num2}return value
}

其实这个value是一个多余的,化简

fun largerNumber(num1: Int, num2: Int) : Int {if (num1 > num2){num1} else {num2}
}

还可以使用刚刚学过的语法糖,当一个函数只有一行代码的时候可以省略函数体部分,直接用等号串联在函数的尾部。
虽然上述函数不只有一行代码,但是它和只有一行代码的作用相同,只是返回了一下if语句的返回值而已。

fun largerNumber(num1: Int, num2: Int) : Int = if (num1 > num2){num1} else {num2}

还可以再短

fun largerNumber(num1: Int, num2: Int) : Int = if (num1 > num2) num1 else num2

**

when条件语句

**Kotlin中的when语句有点类似于java中的switch语句,但它又远比switch语句强大得多。

如果你熟悉java的switch语句的话你应该清楚java的switch语句并不怎么好用。首先,switch只能传入整形或短语整形的变量作为条件,JDK 1.7之后增加了对字符串变量的支持,但如果逻辑判断使用的并非是上述几种类型的变量,那么就没法用了。
其次,swtich中每个case条件都要加一个break,有时候会忘。

而Kotlin中的when解决了这些问题,而且还增加了许多强大新特性,有时候比if还好用。

首先写一个查询考试成绩的功能,输入一个学生名字,返回考试成绩,先用if语句实现

fun getScore(name : String) = if (name == "Tom"){86
} else if ( name == "Jim"){77
}else if (name == "Jack"){95
}else if (name == "Lily"){100
} else {0
}

使用when

fun getScore(name : String) = when(name){"Tom" -> 86"Jim" -> 77"Jack" -> 95"Lily" -> 100else -> 0}

when语句和if语句都也是可以有返回值的,因此我们仍然可以使用单行代码函数的语法糖。

when语句允许传入一个任意类型的参数,然后可以在when结构体中定义一系列的条件,格式是:
匹配值 -> {执行逻辑}
当你的执行逻辑只有一行代码的时候, {}可以省略。

除了精确匹配之后,when语句还允许进行类型匹配。什么是类型匹配呢?
eg:

fun checkNumber(num: Number){when(num){is Int -> println("number is Int")is Double -> println("number is Double")else -> println("number not support")}}

is关键字就是类型匹配的核心,它相当于Java中的instanceof关键字。由于checkNumber函数接收一个Number类型的参数,这是Kotlin内置的一个抽象类,像 Int、Long、Float、Double等与数字相关的都是它的子类,所以这里就可以使用类型匹配来判断传入的参数属于什么类型。

when的基本用法就是这些,但其实when还有一种不带参数的用法,虽然这种用法可能不太常用,但有时候却能有很强的扩展性。

拿刚才的getScore举例,如果我们不在when中传入参数的话,还可以这么写:

fun getScore(name : String) = when{name == "Tom" -> 86name == "Jim" -> 77name == "Jack" -> 95name == "Lily" -> 100else -> 0}

可以看到,这种用法是将判断的表达式完整的写在when的结构体当中。注意,Kotlin中判断字符串或对象是否相等可以直接使用==关键字,而不用像Java那样调用equals方法。可能你会觉得这种无参数的when语句写起来比较冗余,但有些场景必须这样实现。
比如假设所有名字以Tom开头的人,他的分数都是86,这种场景如果使用带参数的when就无法实现,而使用不带参数的when语句就可以这样写

fun getScore(name : String) = when{name.startsWtih("Tom") -> 86name == "Jim" -> 77name == "Jack" -> 95name == "Lily" -> 100else -> 0}

现在不管你传入的是Tom还是Tommy分数都是86.

for循环
Kotlin在for循环方面做了很大幅度的修改,java中最常用的for-i循环在Kotlin中直接被舍弃了,而Java中另一种for-each循环则被Kotlin进行了大幅度的加强,变成了for-in循环,所以我们只需要学习for-in循环就可以了

先普及一下区间的概念,我们可以使用Kotlin代码来表示一个区间:

var range = 0..10

上述代码表示0-10,两端都是闭区间。
有了区间就可以通过for-in来遍历区间。

但其实很多情况下,双端闭区间却不如单端闭区间好用。
数组的下标都是从0开始的,一个长度为10的数组,它的下标区间范围是0-9,因此左闭右开在程序设计当中最为常用。

Kotlin中可以使用until关键字来创建一个左闭右开的区间,如下:

var range = 0 until 10

默认情况下,for-in循环每次执行循环时会在区间范围内递增1,相当于Java for-i循环中i++的效果,而如果你想跳过其中的一些元素,可以使用step关键字:

fun main(){for(i in 0 until 10 step 2){printlin(i)}}

上述代码表示在遍历这个区间的时候,每次执行循环都会在区间范围内递增2,相当于for-i循环中i = i + 2 的效果。

运行结果是 0 2 4 6 8

前面学习的…和until关键字都要求区间的左端必须小于等于区间的右端,也就是这两种关键字创建的都是一个升序的区间。如果你想创建一个降序的区间,可以使用downTo关键字,用法如下:

fun main(){for(i in 10 downTo 1) {println(i)}}

类的实例化:
例如Person:

val p = Person()

Kotlin中想要被继承,必须声明它可以被继承。

在类前面加上open就可以啦。

java中继承是extend,而在Kotlin中变成了一个冒号

eg:

class Student : Person(){var sno  = " "var grade = 0}

问题来了!
继承写法如果只是替换一下关键字倒也挺简单的,但是为什么Person类的后面要加上一对括号呢?

这里设计主构造函数、次构造函数等方面的知识。

主构造函数、次构造函数

Kotlin将构造函数分为主构造函数、次构造函数。

主构造函数将会是最常用的构造函数,每个类默认都会有一个不带参数的主构造函数,当然也可以显示的给它指明参数。主构造函数的特点是没有函数体,直接定义在类名的后面即可。
eg:

class Student(val sno: String, val grade: Int) : Person(){}

这里将学号和年级这两个字段放在了主构造函数当中,这就表明对Student类进行实例化的时候,必须传入构造函数中要求的所有参数,比如:
(init可以把主构造函数的逻辑写进去)

class Student(val sno: String, val grade: Int) : Person{init{println("sno is " + sno)println("grade is " + grade)}
}

但是这里和括号有什么关系呢?
这就涉及了java继承特性中的一个规定,子类中的构造函数必须调用父类中的构造函数,这个规定在Kotlin中也要遵守。

那么回头看一下Student类,现在我们声明了一个主构造函数,根据集成的特性,子类的构造函数必须调用父类的构造函数,可主构造函数中没有函数体,怎么去调用父类构造函数呢?init结构体中或许是一种办法,但绝对不是一种好办法,因为在绝大多数的场景下,我们是不需要编写init结构体。

Kotlin采用了一种简单的设计方式:
括号。
子类的主构造函数调用父类中的哪个构造函数,在集成的时候通过括号来指定。

class Student(val sno: String, val grade: Int) : Person(){}

这里Person类后面的一对空括号表示Student类的主构造函数在初始化的时候会调用Person了的无参构造函数,即使在无参数的情况况下,这对括号也不能省略。

现在改造Person

open class Person(cal name: String, val age: Int){...
}

此时Student类一定会报错,因为Person类后面的括号表示调用无参构造函数,但现在Person没有无参构造函数了。

解决方法

class Student (val sno: String, val grade: Int,  name: String, age: Int) : Person(name, age){...
}

注意,在student类的构造函数中添加name和age这两个字段时,不能再将他们声明称val,因为在主构造函数中声明称val或者var的参数将自动成为该类的字段,这就会导致和父类中同名的name和age字段造成冲突。因此这里的name和age参数前面我们不用加任何关键字,让他的作用域仅限定在主构造函数中即可

次构造函数:

Kotlin提供了一个给函数设定参数默认值的功能,基本上可以替代次构造函数的作用。

一个类只能有一个主构造函数,但是可以有多个次构造函数。次构造函数可以用于实例化一个类,这一点和主构造函数没有什么不同,只不过它是有函数体的。

Kotlin规定,当一个类既有主构造函数又有次构造函数时,所有的次构造函数都必须调用主构造函数(包括间接调用)。
eg:

class Student(val sno: String, val grade: Int, name: String, age: Int) : Person(name, age){constructor(name: String, age: Int) : this("", 0, name, age){}constructor() : this("", 0){}
}

次构造函数是通过constructor关键字来定义的,这里定义了两个次构造函数:
第一个次构造函数接受name和age参数,然后它又通过this关键字调用了主构造函数,并将sno和grade这两个参数赋值成初始值;
第二个次构造函数不接受任何参数,它通过this关键字调用了我们刚才定义的第一个次构造函数,并将name和age参数也赋值成了初始值,由于第二个此构造函数间接调用了主构造函数,因此这仍然是合法的。

(都调用主构造函数,那要次构造函数干啥啊)

哦哦有用了,
现在可以实体化:
var student1 = Student()
var student2 = Student(“Jack”, 19)
var student1 = Student(“a123”, 5, “Jack”, 19)

下面是类中只有次构造函数,没有主构造函数:

当一个类没有显式定义主构造函数且定义了次构造函数时,他就是没有主构造函数的。

class Student: Person{constructor(name: String, age: Int) : super(name, age) {}
}

注意,首先Student类的后面没有显示的定义主构造函数,同时又因为定义了次构造函数,所以它现在是没有主构造函数的。
那么既然没有主构造函数,那么定义Person类的时候就不需要再加上括号了。

另外由于没有主构造函数,次构造函数只能调用父类的构造函数,上述代码就从this变成了super了。

函数的可见性修饰符:

数据类和单例类

在一个规范的系统架构中,数据类通常占据着非常重要的角色,它们用于将服务器端或数据库中的数据映射到内存中,为编程逻辑提供数据模型的支持。
或许你听说过MVC、MVP、MCCM之类的架构模式,这里的M就是指的数据类。

数据类通常需要重写equals、hashcode、toString这几个方法。
比如想要创建一个手机数据类,需要重写,很麻烦,但是用Kotlin来实现就会很简单:

data class Cellphone(cval brand: String, val price: Double)

神奇的地方在于这个data关键字,表明你希望这个类是数据类,Kotlin会自动生成equals、hashcode、toString这几个方法。

另外,当一个类中没有任何代码时,还可以将尾部的大括号省略。

单例类:

想必你一定听过单例模式吧(听过,没学过啊。。),这是最常用、最基础的设计模式之一,它可以用于避免创建重复的对象。
比如我们希望某个类在全局中最多只能拥有一个实例,这时就可以使用单例模式。
当然单例模式也有很多写法,演示一下最常见的java写法

public class Singleton {private static Singleton instance;   private Singleton () {public synchronized static Singlegon getInstance() {if (instance == null ) {instance = new Singleton() ;}return instance;}public void singletonTest() {sout("singletonTest is called");}
}

这段代码的意思是:
首先为了禁止外部创建singleton的实例,我们需要用private关键字将Singleton的构造函数私有化,然后给外部提供一个getInstance的静态方法用于获取Singleton的实例。在getInstance方法中,我们判断如果当前缓存的Singleton实例为null,就创建一个新的实例,否则直接返回缓存的实例即可,这就是单例模型的工作机制。

如果想调用单例类中的方法:

Singleton singleton = Singleton.getInstance();
singleton.singletonTest();

虽然java中的单例实现并不复杂,但是Kotlin明显做得更好

集合的创建与遍历

集合的函数式API是用来入门Lambda变成的绝佳示例,先学习创建集合的方式。

需求:创建一个包含许多水果名称的集合。在java中一般是创建一个Arraylist的实例然后将水果名称一个个添加到集合当中。
Kotlin:

var list = ArrayList<String>()
list.add("apple");
...
...
...

但是这种初始化的方式比较繁琐,为此Kotlin专门提供了一个内置的listof函数来初始化这种集合的写法,如下:

var list = listOf("Apple", "...")

刚才学到的for-in,不仅可以遍历区间,还可以遍历循环。

fun main() {var list = listOf("apple", "...")for(fruit in list) {println(fruit)}
}

需要注意的是,listOf函数创建的是一个不可变的集合。只能用于读取,无法对集合进行添加修改或者删除。。

那如果确实需要创建一个可变的集合呢?使用mutableListOf函数就可以了,如下:

fun main() {var list  = mutableListOf("Apple", "...")list.add("Watermelon")for(fruit in list) {println(fruit)}
}

set方法和list用法几乎一模一样,知识将集合创建方式改成了setOf和mutableSetOf。

最后看一下Map集合用法。
传统Map用法(Kotlin)

var map = HashMap<String, Int>)_
map.put("apple", 1)
...
...
...

这种写法是和java写法最相似的。
但Kotlin中并不建议使用put和get对Map进行添加和读取数据操作,而是更加推荐使用一种类似于数组下标的语法结构,比如向Map中添加一条数据就可以这样写:

map["apple"] = 1

读取:

val number = map["apple"]

Kotlin当然也提供了mapOf和mutableMapOf函数来继续简化Map集合的用法。在mapOf函数中,可以直接传入初始化的键值对组合来完成Map集合的创建:

val map = mapOf("apple" to 1, "..." to 2)

这里的键值对组合看上去好像是用to这个关键字进行关联的,但其实to并不是关键字,而是一个infix函数。

再用for-in来遍历下map集合

fun main() {val map = mapOf("Apple" to 1, "..." to 2)for( ( fruit, number) in map) {println("fruit is "+ fruit +", number is " + number)}}

这段岱庙的主要区别在于,在for-in循环中,会将Map的键值对变量一起声明到了一对括号里面,这样循环遍历的时候,每次遍历的结果就会赋值给这两个键值对变量,最后将他们的值打印出来。

Lambda变成

集合的函数式API

需求:如何在一个水果集合里面找到单词最长的那个水果,普遍:

val list = listOf("apple", "banana", ...)
var maxLength = " "
for ( fruit in list) {if ( fruit.length > maxLengthFruit.length) { maxLengthFruit  = fruit}}
println("max length fruit is " + maxLengthFruit)

使用函数式API:

var list = listOf("Apple", "Banana", "...")
val maxLengthFruit = list.maxBy{it.length}
println("max length fruit is " + maxLengthFruit)

首先看下Lambda的定义,如果用最直白的语言来阐述的话,Lambda就是就是一小段可以作为参数传递的代码。
从第一上看,我们向某个函数传参时只能传入变量,而借助Lambda却允许传入一小段代码。

接着看一下Lambda表达式的语法结构:

{参数名1: 参数类型, 参数名2: 参数类型 -> 函数体}

接着一步步简化
(maxBy就是一个普通函数,可以求出最大值)

如果使用刚才的完整表达式写:

val list = listOf("Apple", "Banana", "...")
val lambda = {fruit: String -> fruit.length}
var maxLengthFruit = list.maxBy(lambda)

首先不需要专门定义一个lambda变量:

var maxLengthFruit = list.maxBy({fruit: String -> fruit.length})

然后Kotlin规定,当Lambda参数时函数的最后一个参数的话,可以将Lambda表达式移到括号的外面,如下:

var maxLengthFruit = list.maxBy () {fruit: String -> fruit.length}

接下来,如果Lambda参数是函数唯一的一个参数的话,还可以将函数的括号省略:

var maxLengthFruit = list.maxBy  {fruit: String -> fruit.length}

由于Kotlin的类型推导机制,Lambda表达式中的参数列表其实在大多情况下不必声明参数类型:

var maxLengthFruit = list.maxBy  {fruit -> fruit.length}

遭遇过i有,当Lambda表达式的参数列表中只有一个参数的时候也不必声明参数名,而是可以用it关键字来代替:

var maxLengthFruit = list.maxBy  { it.length }

集合中的map函数是最常用的一种函数式API,它用于将集合中的每个元素都映射成另外的一个值,映射的规则在Lambda表达式中指定,最终生成一个新的集合。比如希望所有水果名都变成大写:

fun main(){val list = listOf("Apple", "Banana", "...")val newList = list.map { it.toUpperCase()}for (fruit in newList) {println(fruit)}
}

另一个比较常用的函数式API–filter函数。

filter函数是用来过滤集合中的数据的,它可以单独使用,也可以配合刚才的map函数一起使用。

比如只想保留五个字母以内的水果,就可以借助filter来实现:

fun main(){val list = listOf("Apple", "Banana", "...")val newList = list.filter{ it.length <= 5 }.map { it.toUpperCase() }for (fruit in newList) {println(fruit)}
}

注意,上面是先调用了filter函数再调用map函数,如果反过来也能起到相同的效果,但是效率会差很多,因为这样要先对集合中所有的元素都进行一次映射再尽心过滤。

API–any和all函数。
其中any函数用于判断集合中是否至少存在一个元素满足指定条件,all函数用于判断集合中是否所有元素满足条件。

Java函数式API的使用

现在已经学习了Kotlin中函数式API的用法,但实际在Kotlin中调用Java方法时也可以使用函数式API,只不过是有一定条件限制的,如果在Koltin代码中调用了一个Java方法,并且该方法接收一个Java单抽象方法接口参数,就可以使用函数式API。java但抽象方法指的是接口中只有一个待实现方法,如果接口中有多个待实现方法,则无法使用函数式API。

eg:创建子线程

new Thread(new Runnable() {@Overridepublic void run () {sout thread is running;}
}).start();

Koltin版本

Thread(object : Runnable {override fun run() {println bulabulabula}
}).start()

由于Koltin完全舍弃了new关键字,因此创建匿名类实例的时候就不能再使用new了,而是改用了object关键字。
目前Thread类的构造方法是符合Java函数式API的使用条件的,精简:

Thread(Runnable {prinfln bulabulabula
}).start()

这段代码是因为Runnable类中只有一个待实现方法,即使这里没有显示的重写run方法,Kotlin也能自动明白Runnable后面的Lambda表达式就是要在run方法中实现的内容。

另外,如果一个java方法的参数中有且仅有一个Java但抽象方法接口参数,还可以将接口名进行省略,这样代码就变得更加精简了:

Thread({println bulabula}).start()

还没有结束!Lambda表达式是方法的最后一个参数时,可以将Lambda表达式移到方法括号的外面。
同时如果Lambda表达式是方法的唯一一个参数,还可以将方法的括号省略,如下:

Thread{println bulabula}.start()

后面要打交道的Android SDK还是用java语言编写的,当我们在Koltin中调用这些SDK接口时,就很可能会用到这种java函数式API 的写法
举例:

android中有一个极为常用的点击事件接口OnClickListener,其定义如下:

public inteface OnClickListener {void onClick( View v) ;
}

可以看到,这又是一个单抽象方法接口。假设现在拥有一个按钮button的实例,然后使用java代码去注册这个按钮的点击事件:

button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {}
});

而使用Kolin:

button.setOnClickListener{}

创建菜单

override fun onCreateOptionsMenu(menu: Menu) : Boolean {menuInflater.inflate(R.menu.main, menu)return true
}

Java Bean,一个非常简单的java类,会根据类中的字段自动生成相应的Getter和Setter方法,使用Kotlin可以简化:
比如Book类中属性有page

val book = Book()
book.pages = 500
val bookPages = book.pages

这里是Kotlin自动提供的语法糖,会调用getPages和setPages。

而刚刚的onCreateOptionsMenu中的menuInflater也使用了这种语法糖。
它其实是调用了getMenuInflater方法,它能够得到一个MenuInflater对象,再调用它的inflater方法,就可以给当前活动创建菜单了。

最后给这个方法返回true,表示允许显示菜单,如果是false就不显示。

定义菜单响应时间:
在活动中重写onOptionsItemSelected方法

override fun onOptionsItemSelected (item : MenuItem) : Boolean {when (item.itemId) {R.id.add_item -> Toast.makeTest(this, " You clicked Add",               Toast.LENGTH_SHORT).show()R.id.remove_item -> Toast.makeTest(this, " You clicked Remove",              Toast.LENGTH_SHORT).show()}
return true
}

在这个方法里使用到了Kotlin的语法糖,Kotlin实际上在背后调用的是item的getItemId方法。

显式使用Intent和java不同的是,参数为(this, SecondActivity :: class.java)

隐式使用Intent

并不明确指出想要启动哪个Activity,而是指定了一系列更为抽象的action和category等信号,然后交由系统去分析这个Intent,并帮助我们找出合适的Activity去启动。

什么叫做合适的Activity呢?简单来说就是可以响应这个隐式Intent的活动,那么SecondActivity可以响应什么样的隐式Intent呢?

通过在标签下配置的内容,可以指定当前Activity能够响应action和category,打开清单文件,添加

在action标签中指明了当前的活动可以响应ACTION_START这个action,而category标签包含了一些附加信息,更精确的指明了当然活动能够响应的intent中还可能带有的category。只有两个同时满足,这个活动才能响应intent。

Intent传递数据时,如果用户通过back键返回,数据应该在哪里存放用于发送给另一个活动呢?
重写onBackPressed()方法即可。

隐式Intent不仅可以启动自己程序的活动,还可以启动其他程序的活动,这就使得多个应用程序之间的功能共享成为了可能。比如你的应用程序中需要展示一个网页,也没必要自己去实现,只需要调用系统的浏览器即可。

修改活动中按钮点击事件的代码

button.setOnClickListener {var intent = Intent(Intent.ACTION_VIEW)intent.data = Uri.parse("https://www.baidu.com")startActivity(intent)
}

Intent.ACTION_VIEW是一个系统内置的动作,通过Uri.parse方法将一个字符串网址解析成一个Uri对象,再调用Intent的setData方法来给这个Uri对象传递进去。这里又使用了语法糖,调用了setData。

与此对应,还可以再标签中再配置一个标签,用于更精确的指定当前活动能够响应的数据。标签下主要可以配置以下内容。


也是需要完全匹配才可以响应。但一般data中不会定义很多,例如在浏览器示例中只需指定scheme为https,就可以响应所有https协议的Intent了。

Activity生存周期



注意onStop方法中区别。

活动被回收了怎么办哪!!

比如你在第一个活动输入了字,然后跳转到第二个活动,这时候第一个活动因为系统内存不足被回收掉,过了一会你back到第一个活动,会发现刚刚输入的字都没啦,因为这个活动被重新创建啦。

活动中提供了一个onSaveInstanceState回调方法,这个方法可以保证在活动被回收之前一定会被调用,因此可以通过这个方法来解决问题。

onSaveInstanceState方法会携带一个Bundle类型的参数,Bundle提供了一系列的方法用于保存数据,比如可以使用putString方法保存字符串,putInt方法保存整型数据。每个保存方法需要传入两个参数,第一个参数是键,用于从Bundle中取值,第二个参数是真正要保存的内容:

override fun onSaveInstanceState(outState: Bundle) {super.onSaveInstanceState(outState)val tempData = "Something you just typed"outState.putString("data_key", tempData)
}

那么应该在哪里恢复这个数据呢?其实在onCreate方法里有一个Bundle参数,这个参数在一般情况下都是null。

修改onCreate方法:

override fun onCreate(savedInstanceState : Bundle?) {super.onCreate(onSaveInstanceState)setContentView(R.laytout.xxx)if(savedInstanceState != null)val tempData = savedInstanceState.getString("data_key")}
...
}

Intent也可以和Bundle一起使用,可以先把需要传递的数据存放在bundle中,再将Bundle对象存放在Intent中,到了目标活动后,先从Intent中去除Bundle,再从Bundle中一一取出数据。

四种启动模式:

standard是活动的默认启动模式 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210313224748875.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1ZiYmJiYmJiYmJiYg==,size_16,color_FFFFFF,t_70)

singleTop,在启动活动时如果发现返回栈的栈顶已经是该活动,则认为可以直接使用它,不会再创建新的活动实例。

singleTask
使用singleTop模式可以很好的去解决重复创建栈顶的问题,但是如果该活动没有处于栈顶的位置还是会重新创建。
启动该模式,系统首先会在返回栈中检查是否存在该活动的实例,如果发现已经存在则直接使用该实例,并在这个活动之上的所有其他活动统统出栈,没有就重新创建。

singleInstance
它是四种启动模式中最特殊也最复杂的一个。
不同于以上三种启动模式,指定为singleInstance模式的活动会启用一个新的返回栈来管理这个活动(其实如果singleTask模式指定了不同的taskAffinity,也会启动一个新的返回栈)。
意义何在?
假设程序中有一个活动是允许其他应用程序调用的,如果想实现其他程序和我们的程序可以共享这个活动的实例,该如何实现呢?前面三种启动模式肯定做不到,因为每个应用程序都有自己的返回栈,同一个活动在不同的返回栈中入栈必定创建了新的实例。
而是用singleInstance模式就可以解决这个问题,在这种模式下,会有一个单独的返回栈来管理这个活动,不管是哪个应用程序来访问这个活动,都共用一个返回栈,也就解决了共享活动实例的问题。

知晓当前是在哪一个活动

javaClass表示获取当前实例的Class对象,相当于在java中调用getClass方法,再调用simpleName获取当前实例的类名。

javaClass.simpleName

随时随地退出程序。

需要一个专门集合来管理所有的活动。
新建一个单例类ActivityCollector作为Activity的集合。

object ActivityCollector {private val activities = ArrayList<Activity>()fun addActivity(activity: Activity) {activities.add(activity)}fun removeActivity(activity: Activity) {activities.remove(activity)}fun finishAll() {for(activity in activities) {if (!activity.isFinishing) {activity.finish()}}acitivities.clear()}}

这里使用了单例类,因为全局只需要一个Activity集合。

然后设置一个BaseActivity类,让他继承AppCompatActivity,并重写onCreate()方法。

然后在onCreate方法中加入一行ActivityCollector.addAcitivity(this)。
在onDestroy中添加ActivityCollector.removeActivity(this).

定义一个按钮,调用销毁所有活动

ActivityCollector.finishAll()

当然你还可以在销毁所有的活动
android.os.Process.killProcess(android.os.Process.myPid())
killProcess方法用于杀掉一个进程,它接收一个进程的id参数,我们可以通过myPid方法来获取当前程序的进程,需要注意的是,killProcess方法只能用于杀掉当前程序进程,不能用于杀掉其他程序。

在启动活动并传递值的时候,如果按普通写法不容易进行对接,如下更好:

class SecondActivity : BaseActivity() {...compan object {fun actionStart(context: Context, data1: String, data2: String) {val intent = Intent(context, SecondActivity::class.java)intent.putExtra("param1", data1)intent.putExtra("param2", data2)context.startActivity(intent)}}
}

这里使用了新的语法结构companion object,并在其中定义了一个actionStart方法。之所以要这样写,是因为Kotlin规定,所有定义在companion object的方法都可以使用类似于java静态方法的形式调用。

现在只需要一行代码就可以启动secondActivity:

button_setOnClickListener {SecondActivity.actionStart(this, "data1", "data2")}

Kotlin课堂:标准函数和静态方法

标准函数with、run和apply

Kotlin的标准函数指的是Standard.kt文件中定义的函数,任何Kotlin代码都可以自由的调用所有的标准函数。

上次学了let这个标准函数,它的主要作用就是配合?.操作符来辅助进行判空处理。

with:
with函数接受两个参数,第一个参数可以是一个任意类型的对象,第二个参数是一个Lambda表达式。with函数会在Lambda表达式中提供第一个参数对象的上下文,并使用Lambda表达式中的最后一行代码作为返回值返回,示例代码如下:

val result = with(obj) {//这里是obj的上下文"value" //with函数的返回值
}

它可以在连续调用同一个对象的多个方法时让代码变得更加精简。

比如有一个水果列表,现在想吃完所有的水果,并将结果打印出来:

val list = listOf("apple", "banana", "pear", "grape")
val builder = StringBuilder()
builder.append("start eating fruits.\n)
for(fruit in list) {builder.append(fruit).append("\n")
}
builder.append("ate all fruits")
val result = builder.toString()
println(result)

会发现调用了很多次append对象中的方法,这个时候可以考虑使用with来让代码变得精简:

val list = listOf("apple", "banana", "pear", "grape")
val result = with(StringBuilder()) {append("start eating fruits.\n")for ( fruit in list) {append(fruit).append("\n")}append("ate all fruit")toString()
}
println(result)

首先with函数传入的第一个参数是一个StringBuilder对象,那么接下来整个lambda表达式的上下文就是这个StringBuilder对象。于是就可以不用再像刚才那样调用builder.append等方法,直接调用append和toString。

run:
它的用法和使用场景和with非常类似,只是语法有些改动。
首先run方法通常不会直接调用,而是在某个对象的基础上调用;
其次run函数只接收一个lambda对象,并且会在Lambda表达式中提供调用对象的上下文。其他方面与with一致。示例代码如下:

val result = obj.run {//这里是obj上下文
}
//result == obj

跟with方法比只需要改动一行:

val list = listOf("apple", "banana", "pear", "grape")
val result = StringBuilder().run {append("start eating fruits.\n")for ( fruit in list) {append(fruit).append("\n")}append("ate all fruit")toString()
}
println(result)

apply:
apply函数和run函数也是极其类似的,都要在某个对象上调用,并且只接收一个Lambda参数,也会在Lambda表达式中提供调用对象的上下午,但是apply函数无法指定返回值,而是会自动返回调用对象本身,示例代码如下:

val result = obj.apply {//这里是obj上下文
}
// result == obj
val list = listOf("apple", "banana", "pear", "grape")
val result = StringBuilder().apply {append("start eating fruits.\n")for ( fruit in list) {append(fruit).append("\n")}append("ate all fruit")
}
println(result.toString())

由于apply无法指定返回值,只能返回调用的对象本身,所以这里的result其实就是StringBuilder对象,最后需要调用toString方法。

定义静态方法

java中只需要加一个static关键字就可以了。
Kotlin极度弱化了静态方法这个概念,想要在Kotlin中定义一个静态方法反倒不是一件容易的事情。

那么Kotlin为什么要这么设计呢?因为他有更好用的语法特性,单例类。

eg:

object class Util {fun doAction() {println("do action")}
}

这里的doAction并不是静态方法。但是我们仍然可以使用Util.doAction来进行调用,这就是单例类的便利性。

不过,使用单例类的写法会将类中的所有方法都变成类似于静态方法的调用方式,而如果我们只是希望类中的某一个方法变成静态方法的调用方式该怎么办?

使用companion object,示例如下:

class Util {fun doAction1() {println("do acton1")}companion object {fun doAction2() {println("do action2")}}
}

这里首先将Util从一个单例类转变成了一个普通类(是class和object的区别吗),然后在类中定义两个方法。但这两个方法有本质的区别。
doAction1必须用实例调用,而doAction2可以直接Util.doAction2。

不过doAction2并不是静态方法,companion object这个关键字实际上会在Uitl类的内部创建一个伴生类,而doAction2这个方法就是定义在这个伴生类里面的实例方法。只是Kotlin会保证Uitl类始终只会存在一个伴生类对象,实际上就是调用了Util伴生类中的doAciton2方法。

如果想要真的实现静态方法:注解和顶层方法

注解:给单例类或者companion object中的方法加上@JvmStatic注解,Koltin编译器就会将这些方法编译成为真正的静态方法。

顶层方法:指的是那些没有定义在任何类中的方法。Kotlin编译器会将所有的顶层方法全部编译成静态方法。
想要定义一个顶层方法,首先需要创建一个Kotlin文件,注意创建类型是File。

直接输入方法名就可以调用,不用包名路径。

但在java中是不能直接调用,java中没有顶层方法这个概念,所有的方法必须定义在类中。那么定义的方法在哪里呢? 比如Kotlin中创建的文件叫做Helper.kt,于是Kotlin编译器会自动从黄建一个叫做HelperKt的java类,定义的方法就是以静态方法的形式定义在HelperKt类里面的,因此在java中使用HelperKt.方法名()就可以了。

Android学习-Kotlin语言入门-变量、函数、语法糖、when、for-in、主构造函数、单例类、函数式API、集合遍历、隐式Intent、Activity生命周期、四种启动模式、标准函数相关推荐

  1. Android Activity的launchMode四种启动模式备忘

    Android Activity的launchMode四种启动模式备忘 Android的Activity的启动模式有四种,在AndroidManifest.xml通过配置Activity的androi ...

  2. Android Activity:四种启动模式,Intent Flags和任务栈(转自他人博客)

    在Android中每个界面都是一个Activity,切换界面操作其实是多个不同Activity之间的实例化操作.那各个页面跳转关系如何决定呢?如果启动了顺序启动了ABCD的Activiy,如何从D调回 ...

  3. android的四种启动模式,(转)彻底弄懂Activity四大启动模式

    原地址:https://blog..net/mynameishuangshuai/article/details/51491074 最近有几位朋友给我留言,让我谈一下对Activity启动模式的理解. ...

  4. Android中Activity的四种启动模式

    每次看到这种专有词汇都十分佩服创造者的智慧,创造者一定和我一样都中二,我已然确信. 我写博客的目的,就是希望不断磨练自己,让自己能够不将一件简单的事情讲的复杂,让自己能将一件复杂的事情讲的简单.嘛嘛, ...

  5. Android入门:Activity四种启动模式

    2019独角兽企业重金招聘Python工程师标准>>> 一.启动模式介绍 启动模式简单地说就是Activity启动时的策略,在Android Manifest.xml中的标签的and ...

  6. Android入门之Activity四种启动模式

    一.启动模式介绍 启动模式简单地说就是Activity启动时的策略,在AndroidManifest.xml中的<Activity>标签的android:launchMode属性设置: 启 ...

  7. 【Android笔记】Activity的四种启动模式

    在多Activity开发中,有可能是自己应用之间的Activity跳转,或者夹带其他应用的可复用Activity. 可能会希望一个Activity跳转到原来某个Activity实例,而不是产生大量重复 ...

  8. Android之Activity的四种启动模式

    当应用运行起来后就会开启一条线程,线程中会运行一个任务栈,当Activity实例创建后就会放入任务栈中.可以根据实际的需求为Activity设置对应的启动模式,从而可以避免创建大量重复的Activit ...

  9. Android中四种启动模式,最容易理解的小白教程

    说起Android中的启动模式,其实是个很基础的内容,谁都知道是4中启动模式,没错分别是以下四种: 1,standard 2,singleTop 3,singleTask 4,singleInstan ...

最新文章

  1. 【golang程序包推荐分享】go-ini、viper、godoc
  2. Revit的Enscape基本培训(2021) Enscape Essential Training for Revit (2021)
  3. 小米网络推广连夜更换品牌LOGO——科技越是进化就越接近生命的形态
  4. unity ui插件_用Unity制作GalGame/视觉小说游戏的模型素材与插件推荐
  5. 解决Centos 7 VNC黑屏
  6. 2017双11技术揭秘—阿里数据库计算存储分离与离在线混布
  7. 51单片机雾化片自动扫频程序_单片机简介
  8. LINQ能不能用系列(一)LINQ to Object 效率比对
  9. 相机噪声与深度感知的方法梳理
  10. 在ubuntu上安装微博AIR
  11. kubernetest pod为ContainerCreating、ImagePullBackOff状态 怎么办
  12. js 打印组件的使用
  13. 尼恩Java面试宝典
  14. 计算机网络原理(04741)课后习题答案
  15. 2022-2027年中国电容器行业市场全景评估及发展战略规划报告
  16. h5直播|微直播weLiveShow|视频h5|video直播
  17. 目标检测:YOLO You Only Look Once
  18. ASO检索规则-热词覆盖如何来做?
  19. Causal Reasoning from Meta-reinforcement Learning(自用笔记)
  20. 运维-Linux简介

热门文章

  1. linux编译命令——make -j8
  2. 三种常用的电波传播方式 — 地波传播、天波传播、视距传播
  3. 壁虎书4 Training Models
  4. C盘爆满?两个超简单的解决办法
  5. 【庖丁解牛】xshell链接服务器 /usr/bin/xauth: file /root/.Xauthority does not exist
  6. Java开发规范(一)
  7. python魔法方法学不懂_深入学习Python之魔法方法
  8. B/S(WEB)系统中使用websocket插件调用扫描仪实现连续扫描并上传图像(IE文件扫描并自动上传)
  9. 服务器资源监控工具—nmon
  10. mmdetection/mmdetection3d多机多卡训练