Android開發者快速上手Kotlin(十) 之 Android工程實戰和Kotlin總結展望

《Android開發者快速上手Kotlin(九) 之 Kotlin與Java混合開發》文章繼續。

17 Android工程實戰

我們在前面一系列九篇文章已經對Kotlin的語法知識進行了跟Java對照式的學習,如果你堅持看完前面的文章恭喜你已經具備了Kotlin語言的基本開發能力了。當然學習一門語言並非一兩天的事情,就算你把語法都爛透於心但在實際開發中總會還遇到一些特殊的情況,這也是我們程序開發者的日常與樂趣。

回來正題,今天的文章是本系列的最後一篇,主要是介紹我們Android開發者在使用Kotlin語言開發的情況和列出Demo。

17.1 創建工程

我們不再使用IntelliJ了,換回最熟悉的Android Studio,在【File】-【New】-【New Project..】創建工程,在彈出的對話框中的【Language】選項中選擇“Kotlin”然後【Finish】就可以完成工程的創建。

工程創建後,Gradle裏已經幫我們配置好所有相關的Kotlin的項了。但是如果需要使用Kotlin官方的協程框架,我們還需要手動進行添加依賴。如:

dependencies {
   ……
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine_version"
}

另外,例如用於監聽Activity與Fragment的生命週期變化的Lifecycle,我們希望使用 lifecycle.coroutineScope.launch 來當 LifeCycle 回調 onDestroy() 時,協程作用域也會自動取消,還可以再依賴androidx.lifecycle,關於androidx.lifecycle的更多介紹可見:https://developer.android.google.cn/jetpack/androidx/releases/lifecycle#kotlin。如:

dependencies {
   ……
   implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
}

別忘記了coroutine_version和lifecycle_version變量的聲明,如:

ext.coroutine_version = '1.3.6'
ext.lifecycle_version = "2.2.0"

17.2 彈出對話框示例

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val showDialogButton = findViewById<Button>(R.id.btn_show_dialog)
        showDialogButton.setOnClickListener { view ->
            lifecycle.coroutineScope.launch {
                val result = showDialog()
                Toast.makeText(applicationContext, "選擇了${result}", Toast. LENGTH_SHORT).show()
            }
        }
    }
    private suspend fun Context.showDialog() = suspendCancellableCoroutine<Int> { continuation ->
        AlertDialog.Builder(this)
            .setIcon(R.mipmap.ic_launcher)
            .setTitle("dialog的標題")
            .setMessage("dialog的內容")
            .setPositiveButton("確定") { dialog, which ->
                dialog.dismiss()
                continuation.resumeWith(Result.success(1))
            }
            .setNegativeButton("取消") { dialog, which ->
                dialog.dismiss()
                continuation.resumeWith(Result.success(2))
            }
            .setOnCancelListener {
                continuation.resumeWith(Result.success(0))
            }
            .create()
            .also { dialog ->
                continuation.invokeOnCancellation {
                    dialog.dismiss()
                }
            }
            .show()
    }
}

17.2.1 解說

  1. 我們在界面中配置了一個id爲btn_show_dialog的按鈕,並通過熟悉的findViewById方法獲取到一個Button對象,請留意IDE上其類型是Button!,表示是一個平臺類型,它有可能爲空。
  2. 給Button對象的showDialogButton變量設置點擊回調,setOnClickListener方法通過SAM轉換後可直接以表達式的形式來進行。
  3. lifecycle.coroutineScope.launch{…} 用於聲明一個當 LifeCycle 回調 onDestroy() 時變自動取消的協程。
  4. 點擊的處理是要彈出一個Android原生的對話框,我們定義了一個Context的擴展方法showDialog用於對話框的生成。
  5. showDialog函數用suspend修飾,表示它還是一個掛起函數,它指向於一個suspendCancellableCoroutine函數。
  6. suspendCancellableCoroutinesuspendCoroutine的高級版,它支持如果函數在掛起時取消或完成協同,就會引發CancellationException,表示支持協程的取消。
  7. AlertDialog內部存在確定、取消和關閉三種結果的正確形式返回。
  8. also當協程取消時關閉對話框。
  9. 最後調用處通過同步的寫法得到三種不同結果的返回值並通過Toast顯示。

17.3 網絡請求示例

class MainActivity : AppCompatActivity() {
    private val mOkHttpClient = OkHttpClient()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val networkRequestButton = findViewById<Button>(R.id.btn_network_request)
        networkRequestButton.setOnClickListener {
            lifecycle.coroutineScope.launch {
                networkRequest("https://www.baidu.com")
                    .flowOn(Dispatchers.IO)
                    .collect {
                        Toast.makeText(applicationContext,"【${Thread.currentThread().name}】網絡請求結果$it", Toast.LENGTH_SHORT).show()
                    }
            }
        }
    }
    private fun networkRequest(url: String): Flow<String> {
        return flow {
            val request = Request.Builder().url(url).get().build()
            val response = mOkHttpClient.newCall(request).execute()
            if (response.isSuccessful) {
                emit(response.code.toString())
                emit(inputStreamToString(response.body!!.byteStream()))
            } else {
                throw IOException("request error!")
            }
        }.catch { t: Throwable ->
            emit("error $t")
        }
    }
    private fun inputStreamToString(inputStream: InputStream): String {
        var resultBuffer = StringBuffer()
        BufferedReader(InputStreamReader(inputStream)).use {
            while (true) {
                var temp = it.readLine() ?: break
                resultBuffer.append(temp)
            }
        }
        return resultBuffer.toString()
    }
}

17.3.1 解說

  1. 示例使用了Okhttp3進行了網絡請求,所以別忘記添加Okhttp3的依賴(implementation "com.squareup.okhttp3:okhttp:4.6.0")和聯網權限的聲明(<uses-permission android:name="android.permission.INTERNET" />)。
  2. 網絡請求最絡會在協程中返回請求碼和請求結果兩個值,所以選擇使用了Flow的協程形式進行。
  3. 因爲協程可通過調度器運行在IO線程池,所以使用了okhttp的同步請求函數execute省去回調的嵌套。
  4. Okhttp3成功返回的結果最終通過使用了高階函數use進行輔助將其轉化成String。use函數內部封裝了異常捕獲和自動關閉資源。

17.4 示例下載

點擊下載示例

18 Kotlin總結和展望

很多朋友在學習Kotlin時總會聽到兩個聲音:其一是Kotlin並不能取代Java,它只不過是Google官司後的一條後路,它前途有限...另外是Kotlin作爲Android的首選開發語言且是一門朝陽語言,還有Google這麼強大的乾爹,將來必成大器...

其實不管Kotlin將來能到達什麼樣的程度,我們作爲Android的程序開發者,面對一門官方力推新的語言,還是很有學習並學好它的必要,不爲別的,就當作是面試時吹吹牛逼也好。但是說我們需要花很大的精力去精通它嗎?筆者個人建議這個倒不需要深挖到很深,Kotlin的核心價值是擁有很多實用的高級語法糖,它可以幫助我們提高的編碼效率,它對於我們原來使用Java語言開發而言是一種錦上添花。所以也正是如此,它還沒有成爲不可替代的東西。閱讀完《Android開發者快速上手Kotlin》這一系列的十篇文章,記錄好筆記和知識點總結,你已經算是學會Kotlin了,也足已對應日常開發了。Kotlin往後的發展讓我們共同見證和拭目以待吧。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章