其他
干货 | 携程机票 Android Jetpack 与 Kotlin Coroutines 实践
作者简介
禹昂,携程机票移动端资深工程师,Kotlin 中文社区核心成员,图书《Kotlin 编程实践》译者。
一、前言
1.1 技术背景与选型
更简单的异步并发实现方式(近似于同步写法)
更便捷的任务管理
更便捷的生产者-消费者模式实现
更高效的 cold stream 实现(即 Flow,根据官方数据,Flow 在部分 benchmarks 场景下效率是 RxJava 的两倍,详见参考链接 1)。
1.2 业务背景
二. 热身准备
2.1 抛砖引玉
private lateinit var myViewModel: MyViewModel
......
myViewModel = ViewModelProvider(this)[MyViewModel::class.java]
myViewModel.liveData1.observer(this, Observe {
doSomething1(it)
})
myViewModel.liveData2.observer(this, Observe {
doSomething2(it)
})
......
// 顶层函数版本
inline fun <reified T : ViewModel> getViewModel(owner: ViewModelStoreOwner, configLiveData: T.() -> Unit = {}): T =
ViewModelProvider(owner)[T::class.java].apply { configLiveData() }
// 扩展函数版本
inline fun <reified T : ViewModel> ViewModelStoreOwner.getSelfViewModel(configLiveData: T.() -> Unit = {}): T =
getViewModel(this, configLiveData)
private lateinit var myViewModel: MyViewModel
......
myViewModel = getSelfViewModel {
liveData1.observe(this@MyFragment, Observer {
doSomething1(it)
})
liveData2.observe(this@MyFragment, Observer {
doSomething2(it)
})
......
}
2.2 代码角色划分
三、正式实现
fun <T> liveData(
context: CoroutineContext = EmptyCoroutineContext,
timeoutInMs: Long = DEFAULT_TIMEOUT,
@BuilderInference block: suspend LiveDataScope<T>.() -> Unit
): LiveData<T>
class CityViewModel : ViewModel() {
private val departCityTextChannel = Channel<String>(1)
val departCityTextLiveData = liveData {
for (result in departCityTextChannel)
emit(result)
}
// 外部的 UI 通过调用该方法来更新数据
fun updateCityUI() = viewModelScope.launch(Dispatchers.IO) {
val result = fetchData() // 拉取数据
departCityTextChannel.send(result)
}
}
inline val <T> Channel<T>.coroutineLiveData: LiveData<T>
get() = liveData {
for (entry in this@coroutineLiveData)
emit(entry)
}
class CityViewModel : ViewModel() {
private val departCityTextChannel = Channel<String>(1)
val departCityTextLiveData = departCityTextChannel.coroutineLiveData
...... 省略其他代码
3.2 UI 代码订阅 LiveData
class CityView : LinearLayout {
constructor(context: Context) : super(context)
constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet)
constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int) : super(context, attributeSet, defStyleAttr)
private val tvCity: TextView
// ...... 省略更多的 View 声明
init {
LayoutInflater.from(context).inflate(R.layout.flight_inquire_main_view, this).apply {
tvCIty = findViewById(R.id.tv_city)
// ...... 省略更多的 View 初始化
}
}
}
class CityView : LinearLayout {
constructor(context: Context) : super(context)
constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet)
constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int) : super(context, attributeSet, defStyleAttr)
private val tvCity: TextView
// ...... 省略更多的 View 声明
private lateinit var cityViewModel: CityViewModel
init {
LayoutInflater.from(context).inflate(R.layout.city_view, this).apply {
tvCIty = findViewById(R.id.tv_city)
// ...... 省略更多的 View 初始化
}
tvCity.setOnClickListener {
updateCityView()
}
}
fun <T> initObserver(owner: T) where T : ViewModelStoreOwner, T : LifecycleOwner {
cityViewModel = getViewModel(owner) {
cityLiveData.observe(owner, Observer {
tvCity.text = it
})
}
// ...... 省略其他 LiveData 订阅
}
private fun updateCityView() = cityVIewModel.updateCityView()
}
3.3 复杂场景
class CityView2 : LinearLayout {
// ...... 省略其他代码
private val tvCity: TextView
private lateinit var cityViewModel: CityViewModel
init {
LayoutInflater.from(context).inflate(R.layout.city_view2, this).apply {
tvCIty = findViewById(R.id.tv_city2)
}
tvCity.setOnClickListener {
updateCityView()
}
}
fun <T> initObserver(owner: T) where T : ViewModelStoreOwner, T : LifecycleOwner {
cityViewModel = getViewModel(owner) {
cityLiveData.observe(owner, Observer {
tvCity.text = it
})
}
}
private fun updateCityView() = cityVIewModel.updateCityView()
}
四、新技术在生产环境遇到的挑战
五、结语
“携程技术”公众号
分享,交流,成长