Android Weekly Issue #474
Navigation in multi module Android Compose UI project + Hilt
多module項目的compose導航.
在這個項目中有具體實踐:
https://github.com/FunkyMuse/Aurora
Compose architecture: MVVM or MVI with Flow?
Compose聲明式, MVVM結合MVI.
- State: 定義composable要繪制的screen.
- Event: 用戶action.
- Effect: 只被UI消費一次的action.
sample在這里:
https://github.com/catalinghita8/android-compose-mvvm-foodies
NavigationRailView
NavigationRailView
是一個material的導航組件.
Using StateFlow over LiveData for end-to-end operations
使用FLow的例子挺好的.
因為flow是hot的, 所以收集的時候要這樣:
lifecycleScope.launch {
viewModel.mainStateFlow
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.collect { state ->
if (state is PupImageState.Success) {
showImage(state.imageUrl)
} else if (state is PupImageState.Error){
binding.textViewErrorText.text = state.exception.message
}
binding.imageViewPupPicture.isVisible = state is PupImageState.Success
binding.progressBarLoading.isVisible = state is PupImageState.InProgress
binding.textViewErrorText.isVisible = state is PupImageState.Error
}
}
Multimedia Operations for Android using FFmpeg
Instagram-like particles animation using Jetpack Compose
用Jetpack Compose做的粒子動畫.
Reactive Streams on Kotlin: SharedFlow and StateFlow
SharedFlow:
- hot: 沒有人collect也會發(fā)射.
- 可以有多個訂閱者.
- 永遠不會complete.
代碼:
private val _sharedViewEffects = MutableSharedFlow<SharedViewEffects>() // 1
val sharedViewEffects = _sharedViewEffects.asSharedFlow() // 2
發(fā)射元素有兩個方法:
-
emit
: suspend方法. -
tryEmit
: 非suspend方法.
SharedFLow的buffer是干啥的?
如果subscriber suspend了, sharedflow會suspend這個steam, buffer這個要發(fā)射的元素, 等待subscriber resume.
Because onBufferOverflow is set with BufferOverflow.SUSPEND
, the flow will suspend until it can deliver the event to all subscribers.
total buffer是: replay + extraBufferCapacity
.
在UI里subscribe的時候:
viewLifecycleOwner.lifecycleScope.launchWhenStarted { // 1
sharedViewModel.sharedViewEffects.collect { // 2
when (it) {
// 3
is SharedViewEffects.PriceVariation -> notifyOfPriceVariation(it.variation)
}
}
}
SharedFlow
是用來取代BroadcastChannel
的.
StateFlow
StateFlow是特質版的SharedFlow.
可以這樣創(chuàng)建一個有StateFlow行為的SharedFlow:
val shared = MutableSharedFlow(
replay = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
shared.tryEmit(InitialState()) // emit the initial value
val state = shared.distinctUntilChanged() // get StateFlow-like behavior
如果是StateFlow, 就不要用emit
和tryEmit
了.
應該用:
mutableState.value = newState
注意它的conflated特性, 賦值的時候要用不可變的值.
訂閱的時候也是:
private fun observeViewStateUpdates(adapter: CoinAdapter) {
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
viewModel.viewState.collect { updateUi(it, adapter) }
}
}
StateFlow
可以取代ConflatedBroadcastChannel
.
RxJava的等價替代:
-
PublishSubject
->SharedFlow
. -
BehaviorSubject
->StateFlow
.
一次性事件:
https://proandroiddev.com/android-singleliveevent-redux-with-kotlin-flow-b755c70bb055
這里面的這個工具類還挺好的:
class FlowObserver<T> (
lifecycleOwner: LifecycleOwner,
private val flow: Flow<T>,
private val collector: suspend (T) -> Unit
) {
private var job: Job? = null
init {
lifecycleOwner.lifecycle.addObserver(LifecycleEventObserver {
source: LifecycleOwner, event: Lifecycle.Event ->
when (event) {
Lifecycle.Event.ON_START -> {
job = source.lifecycleScope.launch {
flow.collect { collector(it) }
}
}
Lifecycle.Event.ON_STOP -> {
job?.cancel()
job = null
}
else -> { }
}
})
}
}
inline fun <reified T> Flow<T>.observeOnLifecycle(
lifecycleOwner: LifecycleOwner,
noinline collector: suspend (T) -> Unit
) = FlowObserver(lifecycleOwner, this, collector)
inline fun <reified T> Flow<T>.observeInLifecycle(
lifecycleOwner: LifecycleOwner
) = FlowObserver(lifecycleOwner, this, {})
How To Securely Build and Sign Your Android App With GitHub Actions
在GitHub Action的Workflow上如何配置簽名.
Kotlin flow: Nesting vs Chaining
流的兩種模式:
鏈式:
stream1
.flatMap { stream2 }
.flatMap { stream3 }
.flatMap { stream4 }
.collect()
嵌套式:
stream1
.flatMap {
stream2.flatMap {
stream3.flatMap {
stream4
}
}
}
.collect()
有一些情形適合嵌套:
- 在流之間傳遞數(shù)據(jù). 比如用一個api返回的結果作為參數(shù)請求另一個api, 而且有共同需要的參數(shù).
比如
observeUser()
.flatMap { user ->
api.load(user.id)
.flatMap { data -> api.send(user.id, data) }
}
.collect()
如果不用嵌套式, 那么第二個請求沒法獲取user參數(shù).
- 處理scope lifecycle.
observeUser()
.flatMapLatest { user ->
api.load(user.id)
.flatMapLatest { observeLocation() }
}
.collect()
Compose: UI Screenshot Testing
Compose的UI screenshot testing.
這里有個重要的ScreenshotComparator.kt
Code
- 一個變形動畫的layout: https://github.com/skydoves/transformationlayout
- 一個創(chuàng)建compose app的工具: https://github.com/theapache64/create-compose-app android版本的template: https://github.com/theapache64/compose-android-template
- https://github.com/microsoft/surface-duo-window-manager-samples
- https://github.com/akshay2211/BubbleTabBar
- https://github.com/MackHartley/DashedView