一宪睹、混合開發(fā)配置
新建一個kotlin類,若沒配置過kotlin,android studio 會有提示“kotlin not configured”
build.grade中會自動添加kotlin的相關配置
二、 運行項目可能會出現(xiàn):
問題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內(nèi)置變量類型
3.3桂躏、Kotlin 的引用類型和基本類型
java中的變量有引用類型和基本類型
kotlin中變量只有引用類型钻趋,但出于高性能的需求,kotlin編譯器會在java字節(jié)中改用基本類型
3.4剂习、查看kotlin 字節(jié)碼
四蛮位、函數(shù)與函數(shù)類型
4.1 函數(shù)參數(shù)
- 默認值參
如果不打算傳入?yún)?shù),可以預先給參數(shù)設定默認值
fun main() {
fix("jase")
}
private fun fix(name: String , age:Int =20) {
println("$name's age $age")
}
- 具名參數(shù)
使用具名參數(shù)鳞绕,可以不用管參數(shù)的順序
fun main() {
fix(age = 30,name ="Rose")
}
private fun fix(name: String , age:Int =20) {
println("$name's age is $age")
}
- 反引號中的函數(shù)名
- kotlin中允許用空格和特殊符號來命名函數(shù)失仁,但要用反引號包括起來
fun main() {
`*34455473210sss`()
}
fun `*34455473210sss`(){
println("函數(shù)名中帶有反引號")
}
- 為了支持Java與kotlin互操作,而它們都有不同的預留關鍵字不能作為函數(shù)名们何,使用反引號包括函數(shù)名就能避免沖突
4.2 匿名函數(shù)
- 和具名函數(shù)不一樣萄焦,除極少數(shù)情況下,匿名的返回不需要顯式調用return關鍵字冤竹,它會隱式或自動返回函數(shù)體最后一行的執(zhí)行結果
fun main() {
//可以匿名函數(shù)可以作為變量
// (Int,Int)->Int 是變量sum的類型拂封,sum是函數(shù)類型的變量
//它需要兩個Int型參數(shù),返回值是Int型
val sum:(Int,Int)->Int ={ a,b->
a+b
}
println(sum(10,20))
}
- 類型推斷
當把一個匿名函數(shù)賦值給一個變量時鹦蠕,就不要顯示的寫明變量的類型了
fun main() {
val sum:(Int,Int)->Int ={ a,b->
a+b
}
println(sum(10,20))
val sum1 = {a:Int,b:Int->
a+b
}
}
- it關鍵字
當定義只有一個參數(shù)的匿名函數(shù)時冒签,可以使用it關鍵字來表示參數(shù)名。當超過一個參數(shù)钟病,it就不能用了
fun main() {
val blessFuction:(String)->String ={
val holiday = "New Year"
"$it happy $holiday"
}
println(blessFuction("zhangsan"))
}
五镣衡、null安全調用
- kotlin更多的將運行時可能出現(xiàn)的null問題,以編譯時錯誤的方式档悠,提前在編譯期強迫我們重視起來廊鸥,而不是等到運行時報錯,防患于未然辖所,提高我們程序的健壯性惰说。
- 為了避免NullPointerException,kotlin不允許我們給非空變量賦予null值,除非你手動接管安全管理
手動接管安全管理:
- 選項一:安全調用操作符?.
如果變量值為null缘回,就跳過函數(shù)調用
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問題編譯時您觉,不會報錯,只有運行時才會出現(xiàn)
如
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 主構造函數(shù)
//主構造函數(shù)跟在類名后面 _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次構造函數(shù)
與主構造函數(shù)應用的是次要函數(shù)淮摔,可以定義多個次構造函數(shù)
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關鍵字可以聲明一個只產(chǎn)生一次的對象實例----單例,而且可以實現(xiàn)類似靜態(tài)類的效果魔招。
object AppConfig{
init {
println("loading config")
}
fun setSomthing(){
println("please setSomthing")
}
}
fun main() {
AppConfig.setSomthing()
//只會產(chǎn)生一個實例
println(AppConfig)
println(AppConfig)
}
loading config
please setSomthing
AppConfig@27f674d
AppConfig@27f674d
8.2 對象表達式
有時候你不一定非要定義一個新的命名類不可涨享,也許你需要一個現(xiàn)有類的某種變種實例,但只需要用一次仆百,事實上對這種用完一次就丟的類實例厕隧,連命名都可以省略。這個匿名類依然遵循object關鍵字的特性俄周,只會實例化一次吁讨。
llDiamond.setOnClickListener(object : View.OnClickListener{
override fun onClick(v: View?) {
}
})
8.3 伴生對象
- 如果你想把一個對象的初始化和另外一個類實例綁定在一起,可以用伴生對象峦朗,用companion關鍵字聲明伴生對象建丧,一個類中只能有一個伴生對象。
- Kotlin中沒有static關鍵字波势,如果想實現(xiàn)某個方法或某個屬性實現(xiàn)靜態(tài)翎朱,可以通過伴生對象實現(xiàn)
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)
九、協(xié)程的簡單使用
協(xié)程是輕量級線程尺铣,使用協(xié)程可以很方便的進行線程切換拴曲,減少回調
//導入依賴
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中常用的協(xié)程api:
- GlobalScope 生命周期是進程級別的,即使activity和fragment已經(jīng)銷毀凛忿,協(xié)程依
然執(zhí)行 - MainScope 在Activity中使用澈灼,需在onDestory() 中取消協(xié)程
- 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為例簡單講解協(xié)程的使用
實例1
private fun doSomeThing() {
Log.d(TAG, "doSomeThing: " + "開始")
lifecycleScope.launch {
delay(2000)
Log.d(TAG, "MainScope: " + Thread.currentThread().name)
Log.d(TAG, "MainScope: " + "協(xié)程結束")
}
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: 協(xié)程結束
- 協(xié)程內(nèi)的阻塞delay(2000) 并未阻塞線程外的主線程執(zhí)行
實例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: " + "協(xié)程結束")
}
//自動切換到主線程荣回,可進行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: 協(xié)程結束
2022-08-04 23:21:14.922 4251-4251/com.example.coroutines D/MainActivity: lifecycleScope: main
實例三
多任務順序執(zhí)行
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