Kotlin入门
零
参考资料
Kotlin 官方文档 中文版 book.kotlincn.net/ Kotlin 协程 blog.csdn.net/weixin_4481…
Kotlin代码转JAVA
Tools->kotlin->Show Kotlin Bytecode-> Decompole
一、基础语法
var str = "abc"
fun getName : Boolean(){
return true
}
类 class Person { /*……*/ }
类 构造函数
class Person constructor(firstName: String) { /*……*/ }
如果主构造函数没有任何注解或者可见性修饰符,可以省略这个 constructor 关键字。
class Person(firstName: String) { /*……*/ }
class Person(val firstName: String, val lastName: String, var isEmployed: Boolean = true)
继承 open class Base(p: Int) // 该类开放继承
class Derived(p: Int) : Base(p)
方法
fun do(){
}
fun do() : Unit{
}
声明属性
class Address {
var name: String = "Holmes, Sherlock"
var street: String = "Baker"
var city: String = "London"
var state: String? = null
var zip: String = "123456"
}
fun copyAddress(address: Address): Address {
val result = Address() // Kotlin 中没有“new”关键字
result.name = address.name // 将调用访问器
result.street = address.street
return result
}
for (i in 0 until 100) {
println(i) // 输出: 0 ~ 99
}
for (i in 0..100) {
println(i) // 输出: 0 ~ 100
}
for (i in 100 downTo 0) {
println(i) // 输出: 100 ~ 0
}
for (i in 0..10 step 2) {
print("${if (i == 10) i else "$i--"}") // 输出: 0--2--4--6--8--10
}
when(animal) {
EAGLE, DUCK -> println("鸟类")
DOLPHIN, TIGER -> println("兽类")
CARP -> println("鱼类")
LOCUST -> println("昆虫类")
else -> println("未知动物")
}
object.isEmpty
object.isBlank
object?.let{}?:{}
// Java接口回调
mVar.setEventListener(new ExamEventListener(){
public void onSuccess(Data data){
// ...
}
});
// 同等效果的Kotlin接口回调(无使用lambda表达式)
mVar.setEventListener(object: ExamEventListener{
public void onSuccess(Data data){
// ...
}
});
// Kotlin接口回调(使用lambda表达式,仅留下参数)
mVar.setEventListener({
data: Data ->
// ...
})
// 继续简化
// 简化1:借助kotlin的智能类型推导,忽略数据类型
mVar.setEventListener({
data ->
// ...
})
// 简化2:若参数无使用,可忽略
mVar.setEventListener({
// ...
})
// 简化3:若setEventListener函数最后一个参数是一个函数,可把括号的实现提到圆括号外
mVar.setEventListener(){
// ...
}
// 简化3:若setEventListener函数只有一个参数 & 无使用到,可省略圆括号
mVar.setEventListener{
// ...
}
二、var val 及常用数据类型
常用数据类型:Int、Long、Float、Double、Short、Byte、char、String、Boolean
Boolean true和false
String
var str = """
abcdefg
hijklmn
opqrst
uvwxyz """
var val const var 表示可变(mutable), val 表示只读(read-only)而不是不可变(immutable)val 等价于 Java 的 final const 只能修饰没有自定义 getter 的 val 属性,而且它的值必须在编译时确定。类似Java 中可以使用 static final
[const val相对于val 不会生成get方法。性能更好]
1.只能是:字符串和基础类型。【String or a primitive type】
2.只能修饰:全局常量【top level】、object的成员、companion 对象的成员
3.const不能单独修饰某个变量,必须和val同时修饰一个变量。
class Address {
var province = "zhejiang"
val city = "hangzhou"
}
public final class Address {
public final java.lang.String getProvince();
public final void setProvince(java.lang.String);
public final java.lang.String getCity();
public Address();
}
val只读(read-only),而不是不可变(immutable)。
class Person {
var name :String = "liangfei"
var age :Int = 30
val nickname: String
get() {
return if (age > 30) "laoliang" else "xiaoliang"
}
fun grow() {
age += 1
}
}
提示:Const 'val' are only allowed on top level, in named objects, or in companion objects
只可以声明在顶级,或者使用companion objects声明
三、Object与companion objects
juejin.cn/post/684490…
区别:
companion object 类中只能一个 声明周期跟类同步,Only one companion object is allowed per class
objcet 没有限制声明 更多用在对象声明,对象表达式使用。
可以声明在类里 也可以声明在顶级包下 top-level declaration
使用原则:
如果想写工具类的功能,直接创建文件,写 top-level「顶层」函数。(声明在包下)
如果需要继承别的类或者实现接口,就用 object 或 companion object。
1.object关键字可以表达两种含义:一种是对象表达式,另一种是 对象声明。
对象表达式
val textView = findViewById<TextView>(R.id.tv)
textView.setOnClickListener(object : OnClickListener {
override fun onClick(p0: View?) {
Toast.makeText(this@TestActivity, "点击事件生效", Toast.LENGTH_LONG)
}
})
上面代码其实就是我们经常要给 `view` 设置的点击事件,
`OnClickListener` 事件是一个匿名类的对象,用`object`来修饰。
对象声明
直接声明类
用object 修饰的类为静态类,里面的方法和变量都为静态的。
object DemoManager {
private val TAG = "DemoManager"
fun a() {
Log.e(TAG,"此时 object 表示 声明静态内部类")
}
}
又可以声明静态内部类
类内部的对象声明,没有被inner 修饰的内部类都是静态的
class DemoManager{
object MyObject {
fun a() {
Log.e(TAG,"此时 object 表示 直接声明类")
}
}
}
kotlin中调用
fun init() {
MyObject.a()
}
java中调用
MyObject.INSTANCE.a();
#####object单例 饿汉式 反编译代码
object StringUtils {
fun getLength(text: String?): Int = text?.length ?: 0
}
//反编译
public final class StringUtils {
@NotNull
public static final StringUtils INSTANCE; //静态单例对象
public final int getLength(@Nullable String text) {
return text != null ? text.length() : 0;
}
private StringUtils() {
}
static { //静态代码块
StringUtils var0 = new StringUtils();
INSTANCE = var0;
}
}
2.companion object
companion object 修饰为伴生对象,伴生对象在类中只能存在一个,类似于java中的静态方法 Java 中使用类访问静态成员,静态方法。
companion object {
private val TAG = "DemoManager"
fun b() {
Log.e(TAG,"此时 companion objec t表示 伴生对象")
}
}
kotlin 中调用
fun init(){
b()
}
java 中调用
复制代码
DemoManager.Companion.b();
Kotlin
object AA {
const val HVAC_POWER_MODE = "car.hvac.power_mode"
}
Java反编译
public final class AA {
@NotNull
public static final String HVAC_POWER_MODE = "car.hvac.power_mode";
@NotNull
public static final AA INSTANCE;
private AA() {
}
static {
AA var0 = new AA();
INSTANCE = var0;
}
}
----------------
Kotlin
class BB {
companion object {
const val BB_VALUE = "bbvalue"
}
}
Java反编译
public final class BB {
@NotNull
public static final String BB_VALUE = "bbvalue";
@NotNull
public static final BB.Companion Companion = new BB.Companion((DefaultConstructorMarker)null);
public static final class Companion {
private Companion() {
}
// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
----------------
Kotlin
文件名 CC.kt
const val CC_VALUE = "cc_value"
Java反编译
public final class CCKt {
@NotNull
public static final String CC_VALUE = "cc_value ";
}
四、get set
www.jianshu.com/p/ba3869f16…
默认情况下每个属性都具有getter/setter方法
声明一个属性的完整语法如下:
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
属性初始值、getter/setter是可缺省,如果属性类型可以从初始值或getter中推断出来则也可缺省。val类型的属性不具备setter。
属性的getter/setter均可复写,即自定义访问器。如果我们定义了一个自定义的setter,那么每次给属性赋值时都会调用它。
open class Person {
var age: Int = 10
//getter缺省为默认
//setter设置参数前打印参数
set(value) {
println("setter $value")
//field关键字指向属性本身
field = value
}
}
@JvmStatic
fun main(args: Array<String>) {
val p = Person()
println(p.age)
p.age = 30
println(p.age)
}
JAVA思想 例如viewmmodel对外提供String
class Test {
private var _name = "aaa"
val name :String
get() = _name
//kotlin
var age = "age"
private set
}
五、# 内置函数let、also、with、run、apply
let & run
- 两者都是T的扩展函数,也就是任何类型对象都调用run、let;
- 两者的返回值是:最后一行非赋值代码作为闭包的返回值,否则返回Unit;
- run的闭包使用this来访问函数调用者,let的闭包使用it来访问函数的调用者。
apply & also
-
两者都是T的扩展函数,也就是任何类型对象都调用apply、also;
-
两者的返回值都是this,也就是函数调用者;
-
apply的闭包使用this来访问函数调用者,also的闭包使用it来访问函数的调用者。
1 let使用方法
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
// 作用1:使用it替代object对象去访问其公有的属性 & 方法
object.let{
it.todo()
}
// 作用2:判断object为null的操作
object?.let{//表示object不为null的条件下,才会去执行let函数体
it.todo()
}
// 注:返回值 = 最后一行 / return的表达式
String?.let{"99"}?:"0"
let使用示例 定义一个变量在特定的范围域内,避免一些判断null的操作
// 使用Java
if( mVar != null ){
mVar.function1();
mVar.function2();
mVar.function3();
}
// 使用kotlin(无使用let函数)
mVar?.function1()
mVar?.function2()
mVar?.function3()
// 使用kotlin(使用let函数)
// 方便了统一判空的处理 & 确定了mVar变量的作用域
mVar?.let {
it.function1()
it.function2()
it.function3()
}
2 also使用方法
类似let函数,但区别在于返回值:
- let函数:返回值 = 最后一行 / return的表达式
- also函数:返回值 = 传入的对象的本身
使用示例
public inline fun <T> T.also(block: (T) -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block(this)
return this
}
// also函数
var result = mVar.also {
it.function1()
it.function2()
it.function3()
999
}
// 最终结果 = 返回一个mVar对象给变量result
3 run使用方法
结合了let、with两个函数的作用,即:
- 调用同一个对象的多个方法 / 属性时,可以省去对象名重复,直接调用方法名 / 属性即可
- 定义一个变量在特定作用域内
- 统一做判空处理
使用方法
public inline fun <T, R> T.run(block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
object.run{
// ...
}
// 返回值 = 函数块的最后一行 / return表达式
使用示例
// 此处要调用people的name 和 age属性,且要判空
// kotlin
val people = People("carson", 25)
people?.run{
println("my name is $name, I am $age years old")
}
// Java
User peole = new People("carson", 25);
String var1 = "my name is " + peole.name + ", I am " + peole.age + " years old";
System.out.println(var1);
4 apply使用方法
作用 & 应用场景
与run函数类似,但区别在于返回值:
- run函数返回最后一行的值 / 表达式
- apply函数返回传入的对象的本身
应用场景
对象实例初始化时需要对对象中的属性进行赋值 & 返回该对象
使用示例
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
kotlin
复制代码
// run函数
val people = People("carson", 25)
val result = people?.run{
println("my name is $name, I am $age years old")
999
}
// 最终结果 = 返回999给变量result
// Java
val people = People("carson", 25)
val result = people?.run{
println("my name is $name, I am $age years old")
999
}
// 最终结果 = 返回一个people对象给变量result
5 with使用方法
fun main(args: Array<String>) {
val book = Book()
with(book) {
name = "《计算机网络》"
price = 40
}
print(book)
}
六、?、?:、!!、:: 特殊符号
符号一:?
声明一个变量可为空。
var userData : UserData? = null
符号二:?.
表示这个变量可以为null。该变量如果为null时,不会执行该变量后面的逻辑,也不会抛出空指针异常,俗称空安全。如果不为null,会正常执行该变量后面的内容。
UserData?.name
符号三:!!
非空断言运算符 加在变量后面,表示该变量如果为null时,会抛出空指针异常,像java语法一样空指针不安全;如果不为null,才会正常执行该变量后面的内容。
UserData!!.name
符号四:?:
Elvis操作符: A ?: B 如果A 的值为空,则会返回B。一般不推荐用!!,建议使用?:来规避崩溃、报错的发生
println(UserData ?: "当UserData为空时执行")
符号五:::
Kotlin 中 双冒号操作符 表示把一个方法当做一个参数,传递到另一个方法中进行使用,通俗的来讲就是引用一个方法。
class ColonMagic {
fun main(msg: Array<String>) {
//调用peopleDo方法,并传入参数 “I say !” 和 say方法体
//此处 ::say 是将say方法作为参数传入peopleDo方法
//此处只是将say作为参数,而不会调用say方法,也就不会println(message),不会输出日志
peopleDo("I say !", ::say)
}
/**
* 一个参数
* message:String类型
*/
private fun say(message: String) {
println(message)
}
/**
* 两个参数
* msg: String类型
* dosay: (String) -> Unit 一个参数为String不需要返回值的方法体
*/
private fun peopleDo(msg: String, doSay: (String) -> Unit) {
//doSay(msg)调用的就是传入的say方法,即say(msg),只不过参数名是doSay本质是say方法
//此处打印出来 I say ! 日志
doSay(msg)
}
}
符号六:===
=就是赋值,
== 相当于Java中的equls()用于比较对象的结构(内容)是否相等(对象的结构就是属性和方法),
=== 用于比较对象的引用是否指向同一个对象,运行时如果是基本数据类型===等价于 ==
符号七:…
range表达式 表示多少到多少之间
if(num in 10..100)//表示10..100 表示 10到100
符号八:
反引号
1、Kotlin中可以用反引号解决关键字冲突问题
2、将一个不合法的字符变为合法字符,这种情况一般可以用在一个Kotlin方法不希望被Java调用时使用,因为Java语言层面上不支持反引号这种语法。
fun `is`(){
//用is这种关键字去命名函数
}
fun `哈哈哈哈`(){
//用中文这种不合法字符去命名函数
}
符号九:$
拼接符号
"货号 ${data.code}" //将data.code值拼接到货号后
//相当于java中
"货号" + data.code
符号十:@
1、限定this的类型
class User {
inner class State{
fun getUser(): User{
//返回User
return this@User
}
fun getState(): State{
//返回State
return this@State
}
}
}
2、跳转到指定标签处,可以直接跳出多层循环。像汇编语言中的Loop
loop@ for (itemA in arraysA) { //直接跳出双循环
var i : Int = 0
for (itemB in arraysB) {
i++
if (itemB > 2) {
break@loop
}
println("itemB:$itemB")
}
}
使用注意
(1..7).forEach {
if (it == 3) {
return@forEach
}
Log.d("TAG", "Num: $it")
}
不能跳出循环 只能跳出当前循环 相当于continue
正确写法
run loop@{
(1..7).forEach {
if (it == 3) {
return@loop
}
Log.d("TAG", "Num: $it")
}
}
七、数据类(data) 密封类(sealed)枚举类(enum)
数据类
1.数据类以data字段来表示是一个数据类,类似java中的javabean,用于保存数据
2.数据类至少包含一个主构函数,其不能继承其他类,但可以实现其他接口
3.当你创建一个数据类的时候,编译器会从构造函数中提取如下四个函数
1)equals: 用于比较两个数据对象是否相等
2)toString
3)一组componentN函数,这里的N与主构造函数中声明的属性数量是相同的,其对应第N个数据
4)copy:完整复制某个数据对象
data class DataClass constructor(var name: String, var age: Int)
data class DataClass constructor(var name: String, var age: Int){
public fun printMessage(){
}
}
使用的时候和普通类一样创建即可, data class比class 多实现了 toString()、hashCode()、equals()、copy()、componentN()
var dataClas = DataClass("张三")
dataClas.toString();
println("数据类的component2方法 " + dataClas.component2());
// 输出为:
// 张三
// 数据类的component2方法 10
这里的toString不是Any类中的,是编译器为DataClass提取的函数,对于数据类,我们可以使用类似ES6中的解构解析的方式来赋值变量
var data = DataClass(name=张三, age=10)
println("数据类的解构解析 $data");
// 输出为: 数据类的解构解析 DataClass(name=张三, age=10)
此时我们就可以用解构解析的方式来获取属性了
var (name, age) = dataClas
println("数据类的解构解析name $name");
// 输出为:数据类的解构解析name 张三
密封类
1 . 使用 sealed 修饰类,密封类可以有子类,但是所有的子类都必须要内嵌在密封类中,所以我们可以在when表达式中利用密封类来做穷举半段
2 . 密封类子类的子类可以放在任何位置
3 . 它不能直接实例化
sealed class SealedClass{
data class Person(val num1 : Int, val num2 : Int) : SealedClass(){
var number1: Int = 10;
}
fun eval(expr: SealedClass): String = when(expr) {
is Person -> "Person"
}
}
枚举类
kotlin中枚举类和java枚举一致,通过enum关键字来定义一个枚举类,枚举类和密封了的区别是:枚举类的中的每一个枚举常量都只能存在一个实例。而密封类的子类可以存在多个实例。 如下定义一个普通的枚举类
enum class Color{
RED,BLACK,BLUE,GREEN,WHITE
}
Color.BLACK -》 name=BLACK ordinal=1
定义了一个枚举类Color,其枚举值分别是RED,BLACK…,如果没有指定枚举值具体指,则其值将从0开始,依次加一
八、扩展函数
Kotlin的扩展函数是一种特殊的函数,允许在不修改原始类定义的情况下向现有类添加新的函数。 扩展函数可以像普通函数一样使用,并且可以在任何地方调用。定义一个扩展函数,可以使用以下语法:
fun ClassName.functionName(parameters...) {
// Function body
}
----------------------------------
JAVA
// 展示的工具类
public class DisplayUtils {
// dp转px的方法
public static float dp2Px(Context context, float dp) {
Resources resources = context.getResources();
DisplayMetrics metrics = resources.getDisplayMetrics();
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, metrics);
}
}
调用
float pxValue = DisplayUtils.dp2px(context,10)
----------------------------------
kotlin
fun Float.dpToPx(context: Context): Float {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, this, context.resources.displayMetrics
)
}
调用
val pxValue = 10F.dp2px(context)
九、协程
runBlocking 定义: runBlocking 会阻塞线程来等待自己子协程执行完, 并且对于不是子协程的作用域,也会尽量的去执行
override fun initData() {
Log.d(TAGG,"initdata1")
runBlocking {
logThread("runBlocking start== $coroutineContext")
GlobalScope.launch {
delay(28000)
logThread("GlobalScope launch== $coroutineContext")
}
CoroutineScope(Dispatchers.IO).launch {
delay(28000)
logThread("CoroutineScope launch== $coroutineContext")
}
logThread("runBlocking end== $coroutineContext")
}
Log.d(TAGG, "initdata2")
}
15:06:25.789 14886-14886/com.example.hellokt D/LoginActivityKK: initdata1
15:06:25.805 14886-14886/com.example.hellokt D/LoginActivityKK: runBlocking runBlocking start== [BlockingCoroutine{Active}@2849b17, BlockingEventLoop@4813b04]: thread main
15:06:25.815 14886-14886/com.example.hellokt D/LoginActivityKK: runBlocking runBlocking end== [BlockingCoroutine{Active}@2849b17, BlockingEventLoop@4813b04]: thread main
15:06:25.815 14886-14886/com.example.hellokt D/LoginActivityKK: initdata2
15:06:53.817 14886-14940/com.example.hellokt D/LoginActivityKK: runBlocking CoroutineScope launch== [StandaloneCoroutine{Active}@f685e7, Dispatchers.IO]: thread DefaultDispatcher-worker-2
15:06:53.817 14886-14939/com.example.hellokt D/LoginActivityKK: runBlocking GlobalScope launch== [StandaloneCoroutine{Active}@d6d9594, Dispatchers.Default]: thread DefaultDispatcher-worker-1
开启协程两种不同的方式
在上面代码中我们提到了,开启协程有2种方式
CoroutineScope#launch{} CoroutineScope#async{}
override fun initData() {
runBlocking {
val job:Job = launch {
logThread("launch run")
}
val deferred:Deferred<String> = async {
logThread("async run")
"hello"
}
logThread("async result ${deferred.await()}")
}
}
15:22:26.994 22906-22906/com.example.hellokt D/LoginActivityKK: runBlocking launch run: thread main
15:22:26.994 22906-22906/com.example.hellokt D/LoginActivityKK: runBlocking async run: thread main
15:22:26.995 22906-22906/com.example.hellokt D/LoginActivityKK: runBlocking async result hello: thread main
返回的结果通过 Deferred#await() 来获取,并且调用Deferred#await()的时候,会等待async{} 执行完成之后在往下执行,就和Job#join一样,不过await()有返回结果
Job.cancel 取消协程
协程比线程好用的一点就是协程可以自己管理生命周期, 而线程则不可以
fun main() = runBlocking<Unit> {
println("main start")
val job = launch {
try {
(0..100).forEachIndexed { index, _ ->
delay(1000)
println("launch $index")
}
} catch (e: Exception) {
println("协程被取消了 $e")
}
}
// 协程执行完成监听
job.invokeOnCompletion {
println("协程执行完毕${it}")
}
delay(5500)
// 取消协程
job.cancel()
println("main end")
}
运行结果:
main start
launch 0
launch 1
launch 2
launch 3
launch 4
main end
协程被取消了 kotlinx.coroutines.JobCancellationException: StandaloneCoroutine was cancelled; job="coroutine#2":StandaloneCoroutine{Cancelling}@76a4d6c
协程执行完毕kotlinx.coroutines.JobCancellationException: StandaloneCoroutine was cancelled; job="coroutine#2":StandaloneCoroutine{Cancelled}@76a4d6c
CoroutineDispatcher 协程调度器
定义: 根据名字也可以看出来, 协程调度器, 主要用来切换线程,主要有4种
Dispatchers.Main - 使用此调度程序可在 Android 主线程上运行协程。 Dispatchers.IO - 此调度程序经过了专门优化,适合在主线程之外执行磁盘或网络 I/O。示例包括使用 Room 组件、从文件中读取数据或向文件中写入数据,以及运行任何网络操作。 Dispatchers.Default - 此调度程序经过了专门优化,适合在主线程之外执行占用大量 CPU 资源的工作。用例示例包括对列表排序和解析 JSON。 Dispatchers.Unconfined-始终和父协程使用同一线程
withContext() 是用来切换线程,这里切换到主线程,但是输出的结果并没有切换到主线程 withContext{} 与launch{} 调度的区别: withContext 在原有协程上切换线程 launch 创建一个新的协程来切换线程
Applicaiton添加 System.setProperty("kotlinx.coroutines.debug", "on")查看协程号
MainScope().launch {
launch (Dispatchers.IO ){
logThread("AAA")
launch {
logThread("BBB")
launch {
logThread("CCC")
}
}
}
}
7315-7372/com.example.hellokt D/LoginActivityKK: runBlocking AAA: thread DefaultDispatcher-worker-1 @coroutine#2
7315-7372/com.example.hellokt D/LoginActivityKK: runBlocking BBB: thread DefaultDispatcher-worker-1 @coroutine#3
7315-7372/com.example.hellokt D/LoginActivityKK: runBlocking CCC: thread DefaultDispatcher-worker-1 @coroutine#4
MainScope().launch {
launch (Dispatchers.IO ){
logThread("AAA")
launch {
logThread("BBB")
launch(Dispatchers.Main) {
logThread("CCC")
}
}
}
}
8226-8262/com.example.hellokt D/LoginActivityKK: runBlocking AAA: thread DefaultDispatcher-worker-1 @coroutine#2
8226-8264/com.example.hellokt D/LoginActivityKK: runBlocking BBB: thread DefaultDispatcher-worker-3 @coroutine#3
8226-8226/com.example.hellokt D/LoginActivityKK: runBlocking CCC: thread main @coroutine#4
MainScope().launch {
launch (Dispatchers.IO ){
logThread("AAA")
launch {
logThread("BBB")
withContext(Dispatchers.Main) {
logThread("CCC")
}
}
}
}
8928-8968/com.example.hellokt D/LoginActivityKK: runBlocking AAA: thread DefaultDispatcher-worker-1 @coroutine#2
8928-8969/com.example.hellokt D/LoginActivityKK: runBlocking BBB: thread DefaultDispatcher-worker-2 @coroutine#3
8928-8928/com.example.hellokt D/LoginActivityKK: runBlocking CCC: thread main @coroutine#3
CoroutineName 协程名字
MainScope().launch(context = CoroutineName("GWM 1")) {
launch {
logThread("AAA")
launch {
logThread("BBB")
launch (context = CoroutineName("GWM 2")) {
logThread("CCC")
}
}
}
}
12776-12776/com.example.hellokt D/LoginActivityKK: runBlocking AAA: thread main @GWM 1#2
12776-12776/com.example.hellokt D/LoginActivityKK: runBlocking BBB: thread main @GWM 1#3
12776-12776/com.example.hellokt D/LoginActivityKK: runBlocking CCC: thread main @GWM 2#4
Job配合CoroutineHandler 异常捕获
SupervisorJob() 假如有一个场景,我们需要某个子协程出现问题就出现问题,不应该影响到其他的子协程执行,那么我们就可以用 SupervisorJob()
MainScope().launch {
val exHandler = CoroutineExceptionHandler { _, throwable ->
logThread("catch $throwable")
}
val scope = CoroutineScope(Job() + Dispatchers.IO + exHandler)
scope.launch {
logThread("11111")
throw KotlinNullPointerException("1 null")
}.join()
scope.launch {
logThread("22222")
throw KotlinNullPointerException("2 null")
}.join()
}
20921-20968/com.example.hellokt D/LoginActivityKK: runBlocking 11111: thread DefaultDispatcher-worker-1 @coroutine#2
20921-20968/com.example.hellokt D/LoginActivityKK: runBlocking catch kotlin.KotlinNullPointerException: 1 null: thread DefaultDispatcher-worker-1 @coroutine#2
MainScope().launch {
val exHandler = CoroutineExceptionHandler { _, throwable ->
logThread("catch $throwable")
}
val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO + exHandler)
scope.launch {
logThread("11111")
throw KotlinNullPointerException("1 null")
}.join()
scope.launch {
logThread("22222")
throw KotlinNullPointerException("2 null")
}.join()
}
22417-22484/com.example.hellokt D/LoginActivityKK: runBlocking 11111: thread DefaultDispatcher-worker-1 @coroutine#2
22417-22484/com.example.hellokt D/LoginActivityKK: runBlocking catch kotlin.KotlinNullPointerException: 1 null: thread DefaultDispatcher-worker-1 @coroutine#2
22417-22484/com.example.hellokt D/LoginActivityKK: runBlocking 22222: thread DefaultDispatcher-worker-1 @coroutine#3
22417-22484/com.example.hellokt D/LoginActivityKK: runBlocking catch kotlin.KotlinNullPointerException: 2 null: thread DefaultDispatcher-worker-1 @coroutine#3