接《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 解说
- 我们在界面中配置了一个id为btn_show_dialog的按钮,并通过熟悉的findViewById方法获取到一个Button对象,请留意IDE上其类型是Button!,表示是一个平台类型,它有可能为空。
- 给Button对象的showDialogButton变量设置点击回调,setOnClickListener方法通过SAM转换后可直接以表达式的形式来进行。
- lifecycle.coroutineScope.launch{…} 用于声明一个当 LifeCycle 回调 onDestroy() 时变自动取消的协程。
- 点击的处理是要弹出一个Android原生的对话框,我们定义了一个Context的扩展方法showDialog用于对话框的生成。
- showDialog函数用suspend修饰,表示它还是一个挂起函数,它指向于一个suspendCancellableCoroutine函数。
- suspendCancellableCoroutine是suspendCoroutine的高级版,它支持如果函数在挂起时取消或完成协同,就会引发CancellationException,表示支持协程的取消。
- AlertDialog内部存在确定、取消和关闭三种结果的正确形式返回。
- also当协程取消时关闭对话框。
- 最后调用处通过同步的写法得到三种不同结果的返回值并通过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 解说
- 示例使用了Okhttp3进行了网络请求,所以别忘记添加Okhttp3的依赖(implementation "com.squareup.okhttp3:okhttp:4.6.0")和联网权限的声明(<uses-permission android:name="android.permission.INTERNET" />)。
- 网络请求最络会在协程中返回请求码和请求结果两个值,所以选择使用了Flow的协程形式进行。
- 因为协程可通过调度器运行在IO线程池,所以使用了okhttp的同步请求函数execute省去回调的嵌套。
- Okhttp3成功返回的结果最终通过使用了高阶函数use进行辅助将其转化成String。use函数内部封装了异常捕获和自动关闭资源。
17.4 示例下载
18 Kotlin总结和展望
很多朋友在学习Kotlin时总会听到两个声音:其一是Kotlin并不能取代Java,它只不过是Google官司后的一条后路,它前途有限...另外是Kotlin作为Android的首选开发语言且是一门朝阳语言,还有Google这么强大的干爹,将来必成大器...
其实不管Kotlin将来能到达什么样的程度,我们作为Android的程序开发者,面对一门官方力推新的语言,还是很有学习并学好它的必要,不为别的,就当作是面试时吹吹牛逼也好。但是说我们需要花很大的精力去精通它吗?笔者个人建议这个倒不需要深挖到很深,Kotlin的核心价值是拥有很多实用的高级语法糖,它可以帮助我们提高的编码效率,它对于我们原来使用Java语言开发而言是一种锦上添花。所以也正是如此,它还没有成为不可替代的东西。阅读完《Android开发者快速上手Kotlin》这一系列的十篇文章,记录好笔记和知识点总结,你已经算是学会Kotlin了,也足已对应日常开发了。Kotlin往后的发展让我们共同见证和拭目以待吧。