一、混合开发配置
新建一个kotlin类,若没配置过kotlin,android studio 会有提示“kotlin not configured”
build.grade中会自动添加kotlin的相关配置
二、 运行项目可能会出现:
问题1
The minCompileSdk (32) specified in a
dependency's AAR metadata (META-INF/com/android/build/gradle/aar-metadata.properties)
is greater than this module's compileSdkVersion (android-29).
Dependency: androidx.core:core:1.9.0-alpha05.
解决方法
问题2
路由框架不能正常跳转页面,点击首页导航tab没有反应
解决方法:
// annotationProcessor "com.github.Dovar66.DRouter:router-compiler:${rootProject.ext.routerVersion}"
//add for 路由框架找不到kolin类
kapt "com.github.Dovar66.DRouter:router-compiler:${rootProject.ext.routerVersion}"
问题3 unresolved reference: BR
是目前kotlin-android-extensions暂时还不支持跨模块,
,在模块的build.gradle中添加Kotlin-apt插件
apply plugin: 'kotlin-kapt'
三、kotlin的变量定义与类型
3.1、声明变量(val 声明不可变变量,var声明可变变量)
val a:Int = 0 // 变量名:返回类型
var b = "I'm kotlin" //返回类型可以省略,自动推动
3.2 kotlin内置变量类型
3.3、Kotlin 的引用类型和基本类型
java中的变量有引用类型和基本类型
kotlin中变量只有引用类型,但出于高性能的需求,kotlin编译器会在java字节中改用基本类型
3.4、查看kotlin 字节码
四、函数与函数类型
4.1 函数参数
- 默认值参
如果不打算传入参数,可以预先给参数设定默认值
fun main() {
fix("jase")
}
private fun fix(name: String , age:Int =20) {
println("$name's age $age")
}
- 具名参数
使用具名参数,可以不用管参数的顺序
fun main() {
fix(age = 30,name ="Rose")
}
private fun fix(name: String , age:Int =20) {
println("$name's age is $age")
}
- 反引号中的函数名
- kotlin中允许用空格和特殊符号来命名函数,但要用反引号包括起来
fun main() {
`*34455473210sss`()
}
fun `*34455473210sss`(){
println("函数名中带有反引号")
}
- 为了支持Java与kotlin互操作,而它们都有不同的预留关键字不能作为函数名,使用反引号包括函数名就能避免冲突
4.2 匿名函数
- 和具名函数不一样,除极少数情况下,匿名的返回不需要显式调用return关键字,它会隐式或自动返回函数体最后一行的执行结果
fun main() {
//可以匿名函数可以作为变量
// (Int,Int)->Int 是变量sum的类型,sum是函数类型的变量
//它需要两个Int型参数,返回值是Int型
val sum:(Int,Int)->Int ={ a,b->
a+b
}
println(sum(10,20))
}
- 类型推断
当把一个匿名函数赋值给一个变量时,就不要显示的写明变量的类型了
fun main() {
val sum:(Int,Int)->Int ={ a,b->
a+b
}
println(sum(10,20))
val sum1 = {a:Int,b:Int->
a+b
}
}
- it关键字
当定义只有一个参数的匿名函数时,可以使用it关键字来表示参数名。当超过一个参数,it就不能用了
fun main() {
val blessFuction:(String)->String ={
val holiday = "New Year"
"$it happy $holiday"
}
println(blessFuction("zhangsan"))
}
五、null安全调用
- kotlin更多的将运行时可能出现的null问题,以编译时错误的方式,提前在编译期强迫我们重视起来,而不是等到运行时报错,防患于未然,提高我们程序的健壮性。
- 为了避免NullPointerException,kotlin不允许我们给非空变量赋予null值,除非你手动接管安全管理
手动接管安全管理:
- 选项一:安全调用操作符?.
如果变量值为null,就跳过函数调用
fun main() {
var str :String? = readLine()
//str.capitalize()
println(str?.capitalize())
}
- 选项二:使用非空断言操作符
!!.操作符又称感叹号操作符,当变量为null值时,会报NullPointerException
fun main() {
var str :String? = readLine()
//str.capitalize()
str!!.capitalize()
}
- 选项三:使用if判断null值的情况
- 选项四:使用空合并操作符?:
?:操作符的意思是当左侧的求值结果为null时,就用右侧的结果值
fun main() {
var str :String? = readLine()
val safeStr = str?:"butterfly"
println(safeStr)
}
坑:kotlin调用java方法时,有些null问题编译时,不会报错,只有运行时才会出现
如
fun getAppEntrance() {
userModel.getAppEntrance(object : RequestListener {
//override fun onSuccess(jsonObject: JSONObject)
//如果这里没有?,后台返回的jsonobject为null,运行到这里时应用会直接崩溃,
//崩溃的原因时,将一个空值付给了非空值
override fun onSuccess(jsonObject: JSONObject?) {
}
override fun onFailure(error: ErrorResponeBean?) {
}
})
}
六 、List/Map
6.1List
//不可变list
val list = listOf(1,2,3)
//可变list,可以进行add/remove等操作
val list = mutableListOf(,"joke","rose","jack")
list.add("jim")
list.remove("joke")
遍历list
val list = mutableListOf("joke","rose","jack")
//for in
for (str in list){
println(str)
}
//forEach
list.forEach {
println(it)
}
//forEachIndexed
list.forEachIndexed { index, s ->
println("index = $index value =$s")
}
解构list
val list = mutableListOf("joke","rose","jack")
val(one,two,three) = list
println(one)
println(two)
println(three)
6.2 map
//不可变map
var map = mapOf("jack" to 20,"jim" to 25)
println(map["jack"])
//可变map
val mutableMap = mutableMapOf("jack" to 20,"jim" to 25)
//插入元素
mutableMap.put("week",20)
mutableMap["robit"] = 30
//删除
mutableMap.remove("jack")
遍历map
var map = mapOf("jack" to 20,"jim" to 25)
map.forEach { (key, value) ->
println("key =$key, value =$value")
}
map.forEach {
println("${it.key}->${it.value}")
}
七 类定义
7.1 主构造函数
//主构造函数跟在类名后面 _age是临时变量,name 是成员变量
class Student(var name:String,_age:Int){
var age = _age
}
fun main() {
val mStudent = Student("rose",18)
println(mStudent.name)
println(mStudent.age)
}
7.2次构造函数
与主构造函数应用的是次要函数,可以定义多个次构造函数
class Student(var name:String,_age:Int,var idNumber:Int){
var age = _age
constructor(name:String):this(name,20,1001)
constructor(name: String,idNumber: Int):this(name,20,idNumber){
this.name = name.uppercase()
}
}
fun main() {
val mStudent = Student("rose",1002)
println(mStudent.name)
println(mStudent.age)
println(mStudent.idNumber)
}
- kotlin中的类默认是final的,如果需要被继承需用open修饰
open class Student(var name:String,_age:Int,var idNumber:Int){}
- 方法也是,如需重写,也需用open修饰
八、object关键字的作用
8.1 对象声明,使用object关键字可以声明一个只产生一次的对象实例----单例,而且可以实现类似静态类的效果。
object AppConfig{
init {
println("loading config")
}
fun setSomthing(){
println("please setSomthing")
}
}
fun main() {
AppConfig.setSomthing()
//只会产生一个实例
println(AppConfig)
println(AppConfig)
}
loading config
please setSomthing
AppConfig@27f674d
AppConfig@27f674d
8.2 对象表达式
有时候你不一定非要定义一个新的命名类不可,也许你需要一个现有类的某种变种实例,但只需要用一次,事实上对这种用完一次就丢的类实例,连命名都可以省略。这个匿名类依然遵循object关键字的特性,只会实例化一次。
llDiamond.setOnClickListener(object : View.OnClickListener{
override fun onClick(v: View?) {
}
})
8.3 伴生对象
- 如果你想把一个对象的初始化和另外一个类实例绑定在一起,可以用伴生对象,用companion关键字声明伴生对象,一个类中只能有一个伴生对象。
- Kotlin中没有static关键字,如果想实现某个方法或某个属性实现静态,可以通过伴生对象实现
open class BindPhoneNumberActivity : MyBaseActivity<ActivityBindPhoneNumberBinding, BindPhoneNumberViewModel>(){
companion object{
@JvmStatic
fun jump(context: Context){
jump(context,IntentUtil.SourceFrom.FROM_OTHER)
}
@JvmStatic
fun jump(context: Context,from:IntentUtil.SourceFrom){
val intent = Intent(context, BindPhoneNumberActivity::class.java)
intent.putExtra(UserService.FROM,from)
context.startActivity(intent)
}
}
}
//调用
BindPhoneNumberActivity.jump(this@BindPhoneNumberErrorActivity)
九、协程的简单使用
协程是轻量级线程,使用协程可以很方便的进行线程切换,减少回调
//导入依赖
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0-RC-native-mt'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0-RC-native-mt'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0'//lifecycleScope
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'//viewModelScope
anroid中常用的协程api:
- GlobalScope 生命周期是进程级别的,即使activity和fragment已经销毁,协程依
然执行 - MainScope 在Activity中使用,需在onDestory() 中取消协程
- viewModelScope 在ViewModel中使用,绑定ViewModel的生命周期,无需手动cancel
- lifecycleScope 在Activity、Fragment中使用,会绑定Activity和Fragment的生命周期,无需手动cancel
//源码中当DESTROYED时,viewModelScope 、lifecycleScope 会自动取消
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
if (lifecycle.currentState <= Lifecycle.State.DESTROYED) {
lifecycle.removeObserver(this)
coroutineContext.cancel()
}
}
以lifecycleScope为例简单讲解协程的使用
实例1
private fun doSomeThing() {
Log.d(TAG, "doSomeThing: " + "开始")
lifecycleScope.launch {
delay(2000)
Log.d(TAG, "MainScope: " + Thread.currentThread().name)
Log.d(TAG, "MainScope: " + "协程结束")
}
Log.d(TAG, "doSomeThing: " + "结束")
}
2022-08-04 23:16:04.090 4094-4094/com.example.coroutines D/MainActivity: doSomeThing: 开始
2022-08-04 23:16:04.114 4094-4094/com.example.coroutines D/MainActivity: doSomeThing: 结束
2022-08-04 23:16:06.119 4094-4094/com.example.coroutines D/MainActivity: lifecycleScope: main
2022-08-04 23:16:06.119 4094-4094/com.example.coroutines D/MainActivity: lifecycleScope: 协程结束
- 协程内的阻塞delay(2000) 并未阻塞线程外的主线程执行
实例2 线程切换
private fun doSomeThing() {
Log.d(TAG, "doSomeThing: " + "开始")
lifecycleScope.launch {
withContext(Dispatchers.IO){ //切换到IO
delay(2000)
Log.d(TAG, "lifecycleScope: " + Thread.currentThread().name)
Log.d(TAG, "lifecycleScope: " + "协程结束")
}
//自动切换到主线程,可进行UI操作
Log.d(TAG, "lifecycleScope: " + Thread.currentThread().name)
}
Log.d(TAG, "doSomeThing: " + "结束")
}
2022-08-04 23:21:12.907 4251-4251/com.example.coroutines D/MainActivity: doSomeThing: 开始
2022-08-04 23:21:12.919 4251-4251/com.example.coroutines D/MainActivity: doSomeThing: 结束
2022-08-04 23:21:14.922 4251-4306/com.example.coroutines D/MainActivity: lifecycleScope: DefaultDispatcher-worker-1
2022-08-04 23:21:14.922 4251-4306/com.example.coroutines D/MainActivity: lifecycleScope: 协程结束
2022-08-04 23:21:14.922 4251-4251/com.example.coroutines D/MainActivity: lifecycleScope: main
实例三
多任务顺序执行
private fun doSomeThing2(){
Log.d(TAG, "doSomeThing2: 开始")
lifecycleScope.launch {
val time = measureTimeMillis {
val a = getA()
val b = getB()
Log.d(TAG, "lifecycleScope: 结果${a+b}")
}
Log.d(TAG, "lifecycleScope: 用时$time")
}
Log.d(TAG, "doSomeThing2: 结束")
}
private suspend fun getA():Int{
delay(2000)
Log.d(TAG, "lifecycleScope: getA()")
return 20
}
private suspend fun getB():Int{
delay(1000)
Log.d(TAG, "lifecycleScope: getB()")
return 30
}
2022-08-04 23:40:08.722 5361-5361/com.example.coroutines D/MainActivity: doSomeThing2: 开始
2022-08-04 23:40:08.735 5361-5361/com.example.coroutines D/MainActivity: doSomeThing2: 结束
2022-08-04 23:40:10.738 5361-5361/com.example.coroutines D/MainActivity: lifecycleScope: getA()
2022-08-04 23:40:11.742 5361-5361/com.example.coroutines D/MainActivity: lifecycleScope: getB()
2022-08-04 23:40:11.742 5361-5361/com.example.coroutines D/MainActivity: lifecycleScope: 结果50
2022-08-04 23:40:11.742 5361-5361/com.example.coroutines D/MainActivity: lifecycleScope: 用时3007
实例三 多任务并行
private fun doSomeThing3(){
Log.d(TAG, "doSomeThing3: 开始")
lifecycleScope.launch {
val time = measureTimeMillis {
val a = async { getA() }
val b = async { getB() }
Log.d(TAG, "lifecycleScope: 结果${a.await()+b.await()}")
}
Log.d(TAG, "lifecycleScope: 用时$time")
}
Log.d(TAG, "doSomeThing3: 结束")
}
private suspend fun getA():Int{
delay(2000)
Log.d(TAG, "lifecycleScope: getA()" +Thread.currentThread().name)
return 20
}
private suspend fun getB():Int{
delay(1000)
Log.d(TAG, "lifecycleScope: getB()"+Thread.currentThread().name)
return 30
}
2022-08-04 23:44:41.873 5672-5672/com.example.coroutines D/MainActivity: doSomeThing3: 开始
2022-08-04 23:44:41.902 5672-5672/com.example.coroutines D/MainActivity: doSomeThing3: 结束
2022-08-04 23:44:42.907 5672-5672/com.example.coroutines D/MainActivity: lifecycleScope: getB()main
2022-08-04 23:44:43.904 5672-5672/com.example.coroutines D/MainActivity: lifecycleScope: getA()main
2022-08-04 23:44:43.905 5672-5672/com.example.coroutines D/MainActivity: lifecycleScope: 结果50
2022-08-04 23:44:43.905 5672-5672/com.example.coroutines D/MainActivity: lifecycleScope: 用时2005
实例4 多任务并行且线程切换
private fun doSomeThing3(){
Log.d(TAG, "doSomeThing3: 开始")
lifecycleScope.launch {
val time = measureTimeMillis {
val a = async(context = Dispatchers.IO){getA()}
val b = async { withContext(Dispatchers.IO){
getB()
} }
Log.d(TAG, "lifecycleScope: 结果${a.await()+b.await()}")
}
Log.d(TAG, "lifecycleScope: 用时$time" +Thread.currentThread().name)
}
Log.d(TAG, "doSomeThing3: 结束")
}
private suspend fun getA():Int{
delay(2000)
Log.d(TAG, "lifecycleScope: getA()" +Thread.currentThread().name)
return 20
}
private suspend fun getB():Int{
delay(1000)
Log.d(TAG, "lifecycleScope: getB()"+Thread.currentThread().name)
return 30
}
2022-08-04 23:55:49.991 6432-6432/com.example.coroutines D/MainActivity: doSomeThing3: 开始
2022-08-04 23:55:50.018 6432-6432/com.example.coroutines D/MainActivity: doSomeThing3: 结束
2022-08-04 23:55:51.029 6432-6487/com.example.coroutines D/MainActivity: lifecycleScope: getB()DefaultDispatcher-worker-1
2022-08-04 23:55:52.020 6432-6489/com.example.coroutines D/MainActivity: lifecycleScope: getA()DefaultDispatcher-worker-3
2022-08-04 23:55:52.021 6432-6432/com.example.coroutines D/MainActivity: lifecycleScope: 结果50
2022-08-04 23:55:52.021 6432-6432/com.example.coroutines D/MainActivity: lifecycleScope: 用时2008main