Kotlin
一、为什么要优先使用 Kotlin 进行 Android 开发?
我们查看了直接来自与会开发者、我们的客户顾问委员会 (CAB)、Google Developers 专家 (GDE) 的反馈,以及我们通过开发者调研获得的反馈。许多开发者已喜欢上使用 Kotlin,且提供更多 Kotlin 支持的呼声很高。下面介绍了开发者喜欢用 Kotlin 编写代码的原因:
- 富有表现力且简洁:您可以使用更少的代码实现更多的功能。表达自己的想法,少编写样板代码。在使用 Kotlin 的专业开发者中,有 67% 的人反映其工作效率有所提高。
- 更安全的代码:Kotlin 有许多语言功能,可帮助您避免 null 指针异常等常见编程错误。包含 Kotlin 代码的 Android 应用发生崩溃的可能性降低了 20%。
- 可互操作:您可以在 Kotlin 代码中调用 Java 代码,或者在 Java 代码中调用 Kotlin 代码。Kotlin 可完全与 Java 编程语言互操作,因此您可以根据需要在项目中添加任意数量的 Kotlin 代码。
- 结构化并发:Kotlin 协程让异步代码像阻塞代码一样易于使用。协程可大幅简化后台任务管理,例如网络调用、本地数据访问等任务的管理。
二、Kotlin常用知识
1.变量声明
Kotlin 使用两个不同的关键字(即 val [不可修改] 和 var[可修改])来声明变量:
//可变变量赋值
var str: String = "str1"
//变量可赋空值
var str2: String? = null
//延迟赋值使用lateinit
lateinit var str3: String
//val类似java的final关键字
val str4: String = "final str"
//下面是自动类型推断的写法
var str5 = "str5"
var int6 = 5
2.数据类型
-
kotlin与java的基本数据类型对比
kotlin与java的基本数据类型对比 - 数组
val arr1 = arrayOf("a", "b", "c")
for (i in arr1.indices) {
println("地${i}个元素是${arr1[i]}")
}
arr1.set(2, "f")
for ((index, value) in arr1.withIndex()) {
println("地${index}个元素是${value}")
}
- 集合 Set/MutableSet
//不能调用add方法
var set1 = setOf<String>("1", "2", "3")
//取值
var v1: String = set1.elementAtOrElse<String>(0, defaultValue = { "0" })
//可以调用add方法
var mutaset2 = mutableSetOf<String>("1", "2", "3")
mutaset2.add("4")
//取值
var v2: String? = mutaset2.elementAtOrElse<String>(1, { "0" })
var v3: String? = mutaset2.elementAtOrElse<String>(1) { "0" }
val size = set1.size
println("set size:${size}")
//遍历方式1
for (item in mutaset2) {
print(item)
print(" ")
}
println()
//遍历方式二
val iterator = mutaset2.iterator()
while (iterator.hasNext()) {
print(iterator.next())
print(" ")
}
println()
//遍历方式三
mutaset2.forEach {
print(it)
print(" ")
}
println()
MutableSet常用方法,更多方法可以点击MutableSet 方法
属性 | 描述 |
---|---|
fun add(element: E): Boolean | 将给定元素添加到集合中 |
fun addAll(elements: Collection< E>): Boolean | 将给定集合的所有元素添加到当前集合中 |
fun clear() | 将删除此集合中的所有元素 |
fun iterator(): MutableIterator< E> | 它返回此对象元素的迭代器 |
fun remove(element: E): Boolean | 如果指定元素存在于集合中,将从此集合中删除单个指定元素 |
fun removeAll(elements: Collection< E>): Boolean | 会删除集合中指定的当前集合中的所有元素 |
fun retainAll(elements: Collection< E>): Boolean | 仅保留当前集合中存在于指定集合中的那些元素 |
fun contains(element: E): Boolean | 检查当前集合中是否包含的指定元素 |
fun containsAll(elements: Collection< E>): Boolean | 检查当前集合中是否存在指定集合的所有元素 |
fun isEmpty(): Boolean | 如果集合为空(不包含任何元素),则返回true,否则返回false |
fun Iterable.any(): Boolean | 如果集合包含至少一个元素,则返回true |
fun Iterable.any(predicate: (T) -> Boolean): Boolean | 如果至少元素与给定的谓词匹配,则返回true |
fun Iterable.distinct(): List | 返回一个列表,其中只包含给定集合中的不同元素 |
fun Iterable.drop(n: Int): List | 返回一个列表,其中包含除前n个元素之外的所有元素 |
fun Iterable.elementAt(index: Int): T | 返回给定索引处的元素,或者如果集合中不存在给定索引则抛出IndexOutOfBoundException |
fun Iterable.elementAtOrElse(index: Int, defaultValue: (Int) -> T): T | 如果索引在当前集合中超出范围,它将返回给定索引处的元素或调用defaultValue函数的结果 |
fun <T : Comparable> Iterable.max(): T? | 返回最大的元素,如果集合中没有元素,则返回null |
fun <T : Comparable> Iterable.min(): T? | 返回最小的元素,如果集合中没有元素,则返回null |
fun MutableCollection.remove(element: T): Boolean | 如果它存在于当前集合中,它将删除单个指定元素 |
fun MutableCollection.removeAll(elements: Collection): Boolean | 删除了包含在指定集合中的当前集合的所有元素。 |
fun MutableCollection.retainAll(elements: Collection): Boolean | 保留当前集合中包含在指定集合中的所有元素 |
fun Iterable.reversed(): List | 以相反的顺序返回元素 |
- 队列 List/MutableList
// list不能调用add方法,不能修改
var list1 = listOf<Int>(1, 2, 3, 4)
var v5 = list1.get(0)
var v6 = list1[0]
var list2 = mutableListOf<Int>(1, 2, 3, 4)
//设置元素
list2.set(1, 5)
list2[1] = 5
//添加元素
list2.add(7)
var v7 = list2[0]
println("list size:${list2.size}")
//遍历方式1
list2.forEach {
print(it)
print(" ")
}
println()
//遍历方式2
val iterator = list2.iterator()
while (iterator.hasNext()){
print(iterator.next())
print(" ")
}
println()
//遍历方式三
for(item in list2){
print("${item} ")
}
println()
//遍历方式四 获取index
fun forTest() {
val list = mutableListOf(1, 2, 3, 4, 5)
for (index in 0 until list.size) {
println("index=${index}")
}
//遍历方式五 获取index
fun forTest() {
val list = mutableListOf(1, 2, 3, 4, 5)
for (index in 0 .. list.size) {
println("index=${index}")
}
}
//遍历方式六 获取index
fun forTest() {
val list = mutableListOf(1, 2, 3, 4, 5)
for ((index, value) in list.withIndex()) {
println("index=${index}")
println("value=${value}")
}
}
- 映射 Map/MutableMap
val map1 = mapOf("key1" to 1, "key2" to 2)
val entries = map1.entries
for (item in entries) {
println("key为${item.key}的值是${item.value}")
}
println()
val map2 = mutableMapOf<String, Int>("key1" to 1, "key2" to 2)
map2.put("ke3",3)
for((k,v) in map2){
println("key为${k}的值是${v}")
}
println()
map2.remove("key2")
val iterator = map2.iterator()
while(iterator.hasNext()){
val next = iterator.next()
println("key为${next.key}的值是${next.value}")
}
3.Kotlin 条件语句
//这块主要看看基础语法:https://kotlinlang.org/docs/basic-syntax.html
// 比较测试一
var name = ""
if (name.equals("")) {
println("name is null")
}
// 比较测试二
val x = 10
val y = 9
if (x in 1..y + 1) {
println("fits in range")
}
// 比较测试三
val list = listOf("a", "b", "c")
if (-1 !in 0..list.lastIndex) {
println("-1 is out of range")
}
if (list.size !in list.indices) {
println("list size is out of valid list indices range, too")
}
// kotln中使用when代理switch,参考:https://kotlinlang.org/docs/basic-syntax.html#when-expression
var key = 3
when (key) {
1 -> println("打印了1")
2 -> println("打印了2")
3 -> println("打印了3")
}
4.Kotlin的表达式
语句没有值,总是包围着它代码块中的顶层元素,表达式有值,能作为另一个表达式的一部分。举个例子:
java代码:
public int max(int a, int b) {
if (a > b) {
return a;
} else {
return b;
}
}
Kotlin代码:(Kotlin中,if是表达式,不是语句,可以直接return if表达式)
fun max(a: Int, b: Int): Int {
return if (a > b) a else b
}
其余常用的表达式可以参考:
https://www.jianshu.com/p/2a1755457add/
5.Kotlin String模板
主要是怎么输出String的各种数据格式:
val i = 10
println("i = $i") // Prints "i = 10"
val s = "abc"
println("$s.length is ${s.length}") // Prints "abc.length is 3"
更多细节可以参考:https://kotlinlang.org/docs/strings.html#string-templates
6.Kotlin函数
- 普通函数
fun double(a: Int): Int {
return a * 2
}
//没有返回值,Unit可以不写,等同于fun printMethod(txt: String)
fun printMethod(txt: String): Unit {
println(txt)
}
- 函数中的参数可以具有默认值
fun method1(x: Int, useCache: Boolean = false, str: String = "defaultStr") {
println("x:${x}")
println("useCache:${useCache}")
println("str:${str}")
}
//调用的时候可以使用method1(10), 其余参数都是使用默认值
method1(10)
//调用的时候使用 method1(x = 20),就是指定给哪个参数赋值
method1(x = 20)
- 当我们继承与父类的一个成员函数的时候,不能修改其参数默认值
open class A {
open fun method(i: Int = 10) { /*……*/ }
}
class B : A() {
override fun method(i: Int) { /*……*/ } // 不能有默认值
}
- 可变的函数参数(当一个参数是是可变的时候,使用vararg关键字)
fun getList(vararg strs: String):List<String>{
var list =ArrayList<String>()
for(item in strs) {
list.add(item)
}
return list;
}
泛型实现方式:
//这里的T代表泛型,总是泛型总是出现在函数名的前面
fun <T> asList(vararg ts: T): List<T> {
val result = ArrayList<T>()
for (t in ts) // ts is an Array
result.add(t)
return result
}
//调用的时候,使用逗号分隔
val list = asList(1,2,3)
//当我们已经有一个数组类型的参数的时候,我们可以用扩展符(*)来作为参数传入
val arr = arrayof(1,2,3)
val list = asList(4,5,*arr)
- 泛型函数
out指明了他是一个可读的,但不是可写的;in 指明了他是一个可写,但不可读的;类似java中的super和extend,指定了一个类型的下界和上界
interface Source<out T> {
fun nextT(): T
}
fun demo(strs: Source<String>) {
val objects: Source<Any> = strs // 这个没问题,因为 T 是一个 out-参数
// ……
}
interface Comparable<in T> {
operator fun compareTo(other: T): Int
}
fun demo(x: Comparable<Number>) {
x.compareTo(1.0) // 1.0 拥有类型 Double,它是 Number 的子类型
// 因此,我们可以将 x 赋给类型为 Comparable <Double> 的变量
val y: Comparable<Double> = x // OK!
}
- 内联函数
内联函数会将方法直接写在调用的地方,简而言之就是内联函数等于将代码块,copy到调用的地方。 - noinline关键字
noinline: 让原本的内联函数变为不是内联的,保留 数据 特征 - crossinline
非局部返回标记,为了不让lamba表达式直接返回内联函数,所做的标记
相关知识点:我们都知道,kotlin中,如果一个函数中,存在一个lambda表达式,在该lambda中不支持直接通过return退出该函数的,只能通过return@XXXinterface这种方式 - Lambada
可参考文章 - kotlin之闭包、匿名方法、lambda
参考kotlin之闭包、匿名方法、lambda
7.Kotlin类型转换
- 使用is或者!is在运行时检测对象是否符合给定类型。
- 使用as后者as?进行类型转换,后者返回的是空安全类型。
var str:String ="123"
println( str is String )
println( str !is String )
val value = str as CharSequence
println(value)
8.kotlin 注解
- @Target: 目标元素类型
- @Retention: 存储类型
- @Repeatable:单个元素上是否可以应用多个
- @MustBeDocumented: 标记注解是公开API的一部分,会被包含到生成的公开的API文档中。
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION,
AnnotationTarget.TYPE_PARAMETER, AnnotationTarget.VALUE_PARAMETER,
AnnotationTarget.EXPRESSION)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
annotation class TestAnnotationTarget
//声明无参注解
annotation class AnnotationTest
//使用有参注解
@AnnotationTest
fun testMethod() {
println()
}
//声明有参注解
annotation class AnnotationTest1(val str: String)
//使用有参注解
@AnnotationTest1(str = "aaa")
fun testMethod1() {
println()
}
9.kotlin协程
//测试协程的使用
private fun runCoroutines() {
GlobalScope.launch(Dispatchers.Main) {
val data = getNetData()
val processData = procesData(data)
Log.i(TAG, processData)
}
}
//处理网络请求返回的数据
private suspend fun procesData(data: String?): String? {
val dd = data
return withContext(Dispatchers.IO) {
Log.i(TAG, "数据是:${dd}")
Log.i(TAG, "开始处理数据了")
delay(1000)
Log.i(TAG, "成功处理数据了")
dd?.split("-")
?.map { it.capitalize() }
?.reduce { acc, s -> acc + s }
}
}
//模拟网络请求获取数据
private suspend fun getNetData(): String {
return withContext(Dispatchers.IO) {
Log.i(TAG, "开始获取数据了")
delay(2000)
Log.i(TAG, "成功获取数据了")
"hello-abc"
}
}
几个开启协程Scope的区别
- CoroutineScope
他会跟踪所有协程。同样他还可以取消由它所启动的所有协程。 - GlobalScope
声明周期是process级别的。即使Activty与Fragment已经被销毁,协程仍然在运行。 - MainScope
通常在 在Activity中使用。 在onDestory 要记得手动销毁掉。 - viewModelScope
只能在ViewModel中使用。绑定viewModel的生命周期 - lifecycleScope
只能在Activity、Fragment中使用。并且会和Activity、Fragment生命周期进行绑定。
参考一个在Activity中使用的例子:https://blog.csdn.net/mp624183768/article/details/125139784
10.kotlin之let、with、run、apply、also
方法 | 上下文对象 | 返回值 | 扩展函数 | 使用场景 |
---|---|---|---|---|
let | it | lambda 结果 | 是 | 在非空对象上执行一个 lambda或者在局部域中引入一个变量 |
with | this | lambda 结果 | 否 | 在一个对象上组合函数调用 |
run | this | lambda 结果 | 是 | 对象配置并计算结果 |
apply | this | 上下文对象 | 是 | 对象配置 |
also | it | 上下文对象 | 是 | 额外的效果 |
非扩展函数run | 无 | lambda 结果 | 否 | 在需要表达式的地方运行语句 |
表格里的上下文对象中,this 是可以省略的, it 是隐含的默认参数名,可以显式地指定为其他名字。
- run、with 和 apply 的上下文对象是 this ,可以省略掉 this 单词,因此主要用于操作对象成员(例如调用对象的方法或使用其属性)的时候
- let 和 also 的上下文对象是 it , 适用于将此对象作为方法调用参数传入
- apply 和 also 返回上下文对象,可用于链式调用和返回上下文对象的函数的返回语句
- let、run 和 with 返回 lambda 结果,可以在将结果分配给变量时使用
var str: String? = "hello world"
//let 链式调用中最后一行是返回值会带到下个调用中。
str?.let { it ->
println("第一次收到let值:${it}")
"第一次处理let${it}"
}.let {
println("第二次收到let值:${it}")
}
println("let 处理后:${str}")
println("-------------------------------")
//with 最后一行是返回值有作用
// https://www.jianshu.com/p/272372acc00c
//写法一
var withStr: String = with(str, {
println("收到with的值:${this}")
"返回with的值:${this}"
})
println("==withStr 处理后==>${withStr}")
with(str) {
println("长度是:${this?.length}")
}
println("with 处理后:${str}")
println("-------------------------------")
//run 最后一行是返回值有作用
str.run {
println("收到run的值:${this}")
"run1"
}.run {
println("收到run的值:${this}")
}
var runStr: String = str.run {
"run -------------${this}"
}
println("run 处理后:${runStr}")
println("-------------------------------")
//apply 最后一行是返回值没作用
str.apply {
println("收到apply的值1:${this}")
"11111"
}.apply {
println("收到apply的值2:${this}")
"2222"
}.apply {
println("收到apply的值3:${this}")
}
var applyStr: String? = str.apply {
println("收到apply的值:${this}")
// "apply-${this}"
}
println("apply 处理后:${applyStr}")
println("-------------------------------")
//also 最后一行是返回值没作用
str.also {
println("收到also的值1:${it}")
// "ddddd"
}.also {
println("收到also的值2:${it}")
}
//打印日志
/**
第一次收到let值:hello world
第二次收到let值:第一次处理lethello world
let 处理后:hello world
-------------------------------
收到with的值:hello world
==withStr 处理后==>返回with的值:hello world
长度是:11
with 处理后:hello world
-------------------------------
收到run的值:hello world
收到run的值:run1
run 处理后:run -------------hello world
-------------------------------
收到apply的值1:hello world
收到apply的值2:hello world
收到apply的值3:hello world
收到apply的值:hello world
apply 处理后:hello world
-------------------------------
收到also的值1:hello world
收到also的值2:hello world
*/
有篇文章写的不错可以看看:https://www.jianshu.com/p/ba89322cf92d
11.kotlin之takeIf、takeUnless
println("-------------------------------")
//takeIf 当代码块的返回false,对象为空链式调用终止
var name:String="放羊娃"
name.takeIf {
it.length>4
}?.let {
println("测试takeif:${it}")
}
println("-------------------------------")
//takeUnless 当代码块的返回true,对象为空链式调用终止
name.takeUnless {
it.length>4
}.let {
println("测试takeUnless:${it}")
}
三、常用的写法
Activity示例:
class KotlinTestActivity : AppCompatActivity() , View.OnClickListener {
override fun onClick(v: View?) {
}
}
单例示例:
class SingleDemo {
companion object {
val instance: SingleDemo by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { SingleDemo() }
}
fun doTask(){
println("调用到我了")
}
}
fun main() {
SingleDemo.instance.doTask()
}
在推荐其余的单例写法:https://www.jianshu.com/p/2497f6a5a461
Kotlin工具类Util的示例:
//需要构建MyAppUtil对象的方式
open class MyAppUtil {
fun getImei(): String? {
return "imei"
}
}
//调用
var util= MyAppUtil()
util.getImei()
//类似java的static的形式
object MyAppUtil2{
fun getAnroidId():String?{
return "android id"
}
}
//调用
MyAppUtil2.getAnroidId()
Kotlin利用Gson解析json
//数据实体
data class Entity(
val nickname: String,
@SerializedName("xname")
var name: String
)
//调用解析方法
val json = Gson().fromJson(result, Entity::class.java)
Kotlin构造函数可以参考
//@JvmOverloads 加了这个注解就相当生成了一个、两个、三个参数的构造函数了
class CustomTextView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : AppCompatTextView(context, attrs, defStyleAttr) {
init {
//设置ui啥的
}
}
//不用@JvmOverloads 可以自己添加一个和两个的构造方法
class CustomTextView constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : AppCompatTextView(context, attrs, defStyleAttr) {
constructor(context: Context) : this(context, null, 0) {
//todo something
}
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) {
//todo something
}
init {
//todo something
}
}
//Kotlin方法返回对象
data class User(val name: String?, val age: Int) {
}
fun getUser(name: String?, age: Int): User {
return User(name, age)
}
fun main() {
val user = getUser("阿美", 19)
print(user.toString())
}
Kotlin解构解构参考
data class User(val name: String?, val age: Int) {
}
fun getUser(name: String?, age: Int): User {
return User(name, age)
}
fun main() {
val user = getUser("阿美", 19)
print(user.toString())
var (a,b)=user
print("${a},${b}")
}
Kotlin闭包的写法:
fun main() {
var result= test("22", ::getWord2)
println("final result==>${result}")
}
fun getWord2(name: String, age: Int): String {
println("getword2 收到参数: ${name} -${age}")
return "getword2 返回的参数 ${name} -${age}";
}
fun test(name: String, getWord: (String, Int) -> String) {
println("test String ==>${name}")
var age = 100
println("test getword:")
println(getWord(name, age))
}
//打印结果
/**
test String ==>22
test getword:
getword2 收到参数: 22 -100
getword2 返回的参数 22 -100
final result==>kotlin.Unit
*/
kotlin匿名函数
fun main() {
println(blessFunction())
println(blessFunction2())
println(blessFunction3())
println(blessFunction4("hhhhhh"))
}
var blessFunction: () -> String = {
"bless function return value"
}
var blessFunction2: () -> Unit = {
println("blessFunction2")
}
var blessFunction3 = {
"blessFunction3"
}
var blessFunction4: (str: String) -> String = {
"bless function return value-${it}"
}
//打印结果
/**
bless function return value
blessFunction2
kotlin.Unit
blessFunction3
bless function return value-hhhhhh
Function0<java.lang.String>
*/
Kotlin空合并操作符号
var str:String?=null
var name: String =str ?:"ddd-----------"
println(name)
Kotlin先决条件函数
image.png
四、实用代码
1.获取sdkROM大小
//ROM内存大小,返回 64G/128G/256G/512G
private fun getTotalRom(): String {
val dataDir = Environment.getDataDirectory();
val stat = StatFs(dataDir.path)
val blockSize:Long = stat.blockSizeLong
val totalBlocks:Long = stat.blockCountLong
val size:Long = totalBlocks * blockSize
val GB:Long = 1024 * 1024 * 1024
val deviceRomMemoryMap = arrayOf(
2 * GB,
4 * GB,
8 * GB,
16 * GB,
32 * GB,
64 * GB,
128 * GB,
256 * GB,
512 * GB,
1024 * GB,
2048 * GB
)
val displayRomSize = arrayOf(
"2GB",
"4GB",
"8GB",
"16GB",
"32GB",
"64GB",
"128GB",
"256GB",
"512GB",
"1024GB",
"2048GB"
)
var resultJ = 0
for (i in deviceRomMemoryMap.indices) {
resultJ = i
if (size <= deviceRomMemoryMap[i]) {
break
}
if (i == deviceRomMemoryMap.size) {
resultJ = i - 1
}
}
return displayRomSize[resultJ]
}
五、参考文档
https://developer.android.google.cn/kotlin/learn
https://kotlinlang.org/docs/basic-syntax.html